mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-19 07:54:10 +00:00
Canonicalize platform drawer table shells
This commit is contained in:
parent
d147154cfd
commit
f6bf4779be
10 changed files with 314 additions and 300 deletions
|
|
@ -187,7 +187,10 @@ work extends shared components instead of creating new local variants.
|
|||
settings source/configured-node tables, must use `Table` directly rather than
|
||||
nesting another card or scroll wrapper. If a framed table needs bounded
|
||||
vertical height, that constraint belongs on `Table.wrapperClass` so the
|
||||
shared table shell still owns overflow behavior.
|
||||
shared table shell still owns overflow behavior. Resource-detail drawer
|
||||
tables that consume `Table`, including Docker Swarm services and Kubernetes
|
||||
namespaces/deployments, inherit the same scroll-shell owner instead of
|
||||
carrying drawer-local `overflow-x-auto` wrappers.
|
||||
Product-table subgroup/header rows must likewise consume the shared
|
||||
`frontend-modern/src/components/shared/groupedTableRowPresentation.ts`
|
||||
helper and `.grouped-table-row` CSS token contract instead of local
|
||||
|
|
|
|||
|
|
@ -4859,6 +4859,7 @@
|
|||
"owned_files": [
|
||||
"frontend-modern/src/components/Discovery/DiscoveryTab.tsx",
|
||||
"frontend-modern/src/components/Discovery/useDiscoveryTabState.ts",
|
||||
"frontend-modern/src/components/Docker/SwarmServicesDrawer.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts",
|
||||
"frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts",
|
||||
|
|
@ -4891,6 +4892,8 @@
|
|||
"frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts",
|
||||
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts",
|
||||
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts",
|
||||
"frontend-modern/src/components/Kubernetes/K8sDeploymentsDrawer.tsx",
|
||||
"frontend-modern/src/components/Kubernetes/K8sNamespacesDrawer.tsx",
|
||||
"frontend-modern/src/components/PMG/ServiceHealthBadge.tsx",
|
||||
"frontend-modern/src/features/infrastructure/infrastructurePageModel.ts",
|
||||
"frontend-modern/src/features/infrastructure/InfrastructurePageSurface.tsx",
|
||||
|
|
@ -4979,6 +4982,7 @@
|
|||
"match_files": [
|
||||
"frontend-modern/src/components/Discovery/DiscoveryTab.tsx",
|
||||
"frontend-modern/src/components/Discovery/useDiscoveryTabState.ts",
|
||||
"frontend-modern/src/components/Docker/SwarmServicesDrawer.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts",
|
||||
"frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts",
|
||||
|
|
@ -5010,6 +5014,8 @@
|
|||
"frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts",
|
||||
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts",
|
||||
"frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts",
|
||||
"frontend-modern/src/components/Kubernetes/K8sDeploymentsDrawer.tsx",
|
||||
"frontend-modern/src/components/Kubernetes/K8sNamespacesDrawer.tsx",
|
||||
"frontend-modern/src/features/infrastructure/infrastructurePageModel.ts",
|
||||
"frontend-modern/src/features/infrastructure/InfrastructurePageSurface.tsx",
|
||||
"frontend-modern/src/features/infrastructure/useInfrastructurePageRouteState.ts",
|
||||
|
|
@ -5045,6 +5051,7 @@
|
|||
"frontend-modern/src/routing/__tests__/resourceLinks.test.ts",
|
||||
"frontend-modern/src/stores/__tests__/websocket-unified.test.ts",
|
||||
"frontend-modern/src/types/__tests__/resource.test.ts",
|
||||
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
|
||||
"internal/unifiedresources/code_standards_test.go"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -52,60 +52,63 @@ cross-source deduplication.
|
|||
28. `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx`
|
||||
29. `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx`
|
||||
30. `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerSupportDisclosure.tsx`
|
||||
31. `frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx`
|
||||
31. `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`
|
||||
32. `frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx`
|
||||
33. `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx`
|
||||
34. `frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx`
|
||||
35. `frontend-modern/src/components/Infrastructure/resourceBadges.ts`
|
||||
36. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx`
|
||||
37. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx`
|
||||
38. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx`
|
||||
39. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx`
|
||||
40. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts`
|
||||
41. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts`
|
||||
42. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts`
|
||||
43. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerServiceModel.ts`
|
||||
44. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerVmwareModel.ts`
|
||||
45. `frontend-modern/src/components/Infrastructure/resourceDetailDiscoveryModel.ts`
|
||||
46. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerOperationalModel.ts`
|
||||
47. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts`
|
||||
48. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts`
|
||||
49. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`
|
||||
50. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
|
||||
51. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts`
|
||||
52. `frontend-modern/src/components/Discovery/DiscoveryTab.tsx`
|
||||
53. `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts`
|
||||
54. `frontend-modern/src/features/infrastructure/InfrastructurePageSurface.tsx`
|
||||
55. `frontend-modern/src/features/infrastructure/useInfrastructurePageRouteState.ts`
|
||||
56. `frontend-modern/src/features/infrastructure/useInfrastructurePageState.ts`
|
||||
57. `frontend-modern/src/features/infrastructure/infrastructurePageModel.ts`
|
||||
58. `frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx`
|
||||
59. `frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts`
|
||||
60. `frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts`
|
||||
61. `frontend-modern/src/utils/agentResources.ts`
|
||||
62. `frontend-modern/src/utils/canonicalResourceTypes.ts`
|
||||
63. `frontend-modern/src/utils/resourceBadgePresentation.ts`
|
||||
64. `frontend-modern/src/utils/resourceChangePresentation.ts`
|
||||
65. `frontend-modern/src/utils/actionAuditPresentation.ts`
|
||||
65. `frontend-modern/src/utils/resourceCorrelationPresentation.ts`
|
||||
66. `frontend-modern/src/utils/resourcePlatformData.ts`
|
||||
67. `frontend-modern/src/utils/resourcePolicyPresentation.ts`
|
||||
68. `frontend-modern/src/utils/resourceStateAdapters.ts`
|
||||
69. `frontend-modern/src/utils/resourceTypeCompat.ts`
|
||||
70. `frontend-modern/src/utils/resourceTypePresentation.ts`
|
||||
71. `frontend-modern/src/utils/serviceHealthPresentation.ts`
|
||||
72. `frontend-modern/src/utils/sourceTypePresentation.ts`
|
||||
73. `frontend-modern/src/utils/workloadTypePresentation.ts`
|
||||
74. `frontend-modern/src/components/PMG/ServiceHealthBadge.tsx`
|
||||
75. `frontend-modern/src/utils/resourceIdentity.ts`
|
||||
76. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerIdentityModel.ts`
|
||||
77. `frontend-modern/src/hooks/useUnifiedResources.ts`
|
||||
79. `frontend-modern/src/types/resource.ts`
|
||||
80. `frontend-modern/src/utils/sourcePlatforms.ts`
|
||||
81. `frontend-modern/src/utils/platformSupportManifest.generated.ts`
|
||||
82. `internal/unifiedresources/kubernetes_metric_ids.go`
|
||||
83. `internal/unifiedresources/policy_posture.go`
|
||||
31. `frontend-modern/src/components/Docker/SwarmServicesDrawer.tsx`
|
||||
32. `frontend-modern/src/components/Kubernetes/K8sDeploymentsDrawer.tsx`
|
||||
33. `frontend-modern/src/components/Kubernetes/K8sNamespacesDrawer.tsx`
|
||||
34. `frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx`
|
||||
35. `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`
|
||||
36. `frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx`
|
||||
37. `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx`
|
||||
38. `frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx`
|
||||
39. `frontend-modern/src/components/Infrastructure/resourceBadges.ts`
|
||||
40. `frontend-modern/src/components/Infrastructure/UnifiedResourceHostTableCard.tsx`
|
||||
41. `frontend-modern/src/components/Infrastructure/UnifiedResourcePBSTableSection.tsx`
|
||||
42. `frontend-modern/src/components/Infrastructure/UnifiedResourcePMGTableSection.tsx`
|
||||
43. `frontend-modern/src/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx`
|
||||
44. `frontend-modern/src/components/Infrastructure/unifiedResourceTableModel.ts`
|
||||
45. `frontend-modern/src/components/Infrastructure/unifiedResourceTableStateModel.ts`
|
||||
46. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts`
|
||||
47. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerServiceModel.ts`
|
||||
48. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerVmwareModel.ts`
|
||||
49. `frontend-modern/src/components/Infrastructure/resourceDetailDiscoveryModel.ts`
|
||||
50. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerOperationalModel.ts`
|
||||
51. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts`
|
||||
52. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts`
|
||||
53. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`
|
||||
54. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
|
||||
55. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableViewportSync.ts`
|
||||
56. `frontend-modern/src/components/Discovery/DiscoveryTab.tsx`
|
||||
57. `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts`
|
||||
58. `frontend-modern/src/features/infrastructure/InfrastructurePageSurface.tsx`
|
||||
59. `frontend-modern/src/features/infrastructure/useInfrastructurePageRouteState.ts`
|
||||
60. `frontend-modern/src/features/infrastructure/useInfrastructurePageState.ts`
|
||||
61. `frontend-modern/src/features/infrastructure/infrastructurePageModel.ts`
|
||||
62. `frontend-modern/src/components/Infrastructure/InfrastructureSummary.tsx`
|
||||
63. `frontend-modern/src/components/Infrastructure/useInfrastructureSummaryState.ts`
|
||||
64. `frontend-modern/src/components/Infrastructure/infrastructureSummaryModel.ts`
|
||||
65. `frontend-modern/src/utils/agentResources.ts`
|
||||
66. `frontend-modern/src/utils/canonicalResourceTypes.ts`
|
||||
67. `frontend-modern/src/utils/resourceBadgePresentation.ts`
|
||||
68. `frontend-modern/src/utils/resourceChangePresentation.ts`
|
||||
69. `frontend-modern/src/utils/actionAuditPresentation.ts`
|
||||
70. `frontend-modern/src/utils/resourceCorrelationPresentation.ts`
|
||||
71. `frontend-modern/src/utils/resourcePlatformData.ts`
|
||||
72. `frontend-modern/src/utils/resourcePolicyPresentation.ts`
|
||||
73. `frontend-modern/src/utils/resourceStateAdapters.ts`
|
||||
74. `frontend-modern/src/utils/resourceTypeCompat.ts`
|
||||
75. `frontend-modern/src/utils/resourceTypePresentation.ts`
|
||||
76. `frontend-modern/src/utils/serviceHealthPresentation.ts`
|
||||
77. `frontend-modern/src/utils/sourceTypePresentation.ts`
|
||||
78. `frontend-modern/src/utils/workloadTypePresentation.ts`
|
||||
79. `frontend-modern/src/components/PMG/ServiceHealthBadge.tsx`
|
||||
80. `frontend-modern/src/utils/resourceIdentity.ts`
|
||||
81. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerIdentityModel.ts`
|
||||
82. `frontend-modern/src/hooks/useUnifiedResources.ts`
|
||||
83. `frontend-modern/src/types/resource.ts`
|
||||
84. `frontend-modern/src/utils/sourcePlatforms.ts`
|
||||
85. `frontend-modern/src/utils/platformSupportManifest.generated.ts`
|
||||
86. `internal/unifiedresources/kubernetes_metric_ids.go`
|
||||
87. `internal/unifiedresources/policy_posture.go`
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
|
|
@ -162,6 +165,10 @@ cross-source deduplication.
|
|||
infrastructure tables must use the shared `Table` primitive's scroll frame
|
||||
directly instead of reintroducing page-local `overflow-x-auto` wrappers
|
||||
inside the canonical card shell.
|
||||
Docker Swarm service and Kubernetes namespace/deployment resource-drawer
|
||||
tables follow that same shell boundary: their platform-specific rows and
|
||||
actions are unified-resource-owned, while horizontal overflow is owned by the
|
||||
shared `Table` wrapper rather than drawer-local scroll divs.
|
||||
|
||||
Resource detail mappers now reuse the shared
|
||||
`frontend-modern/src/utils/textPresentation.ts` title-case helper for sensor
|
||||
|
|
@ -221,7 +228,7 @@ AI-only summary payloads, or page-local heuristics.
|
|||
generic disk-count aggregate, so resource drawers and incidents do not hide
|
||||
the actual protection boundary behind a broader count phrase.
|
||||
6. Add canonical governed name-resolution or policy-aware resource lookup behavior through `internal/unifiedresources/resolve.go` and `internal/unifiedresources/resolve_context.go`
|
||||
7. Add or change resource drawer timeline/facet/action-history presentation through `frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx`, `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx`, `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx`, `frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx`, `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`, `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`, `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`, and the governed `internal/api/resources.go` facet/timeline plus `internal/api/activity_audit_handlers.go` action-audit contracts together
|
||||
7. Add or change resource drawer timeline/facet/action-history presentation through `frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx`, `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx`, `frontend-modern/src/components/Infrastructure/ResourceDetailDrawerDebugTab.tsx`, `frontend-modern/src/components/Docker/SwarmServicesDrawer.tsx`, `frontend-modern/src/components/Kubernetes/K8sDeploymentsDrawer.tsx`, `frontend-modern/src/components/Kubernetes/K8sNamespacesDrawer.tsx`, `frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx`, `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerState.ts`, `frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx`, `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`, and the governed `internal/api/resources.go` facet/timeline plus `internal/api/activity_audit_handlers.go` action-audit contracts together
|
||||
8. Add or change discovery-support runtime under the resource drawer through `frontend-modern/src/components/Discovery/DiscoveryTab.tsx` for shell/presentation ownership and `frontend-modern/src/components/Discovery/useDiscoveryTabState.ts` for fetch, websocket-progress, and notes-mutation ownership
|
||||
9. Keep dashboard and infrastructure freshness on the canonical unified-resource
|
||||
ownership path. `frontend-modern/src/stores/websocket.ts`,
|
||||
|
|
|
|||
|
|
@ -253,90 +253,82 @@ export const SwarmServicesDrawer: Component<{ cluster: string; swarm?: SwarmInfo
|
|||
}
|
||||
>
|
||||
<Card padding="none" tone="card" class="overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<Table class="w-full min-w-[900px] border-collapse text-xs">
|
||||
<TableHeader class="bg-surface-alt text-muted border-b border-border">
|
||||
<TableRow class="text-left text-[10px] uppercase tracking-wide">
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.serviceColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.stackColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.imageColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.modeColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.desiredColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.runningColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.updateColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.portsColumnLabel}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody class="divide-y divide-border-subtle">
|
||||
<For each={filteredServices()}>
|
||||
{(svc) => {
|
||||
const name = () => asTrimmedString(svc.name) || svc.id;
|
||||
const stack = () => asTrimmedString(svc.docker?.stack) || '—';
|
||||
const image = () => asTrimmedString(svc.docker?.image) || '—';
|
||||
const mode = () => asTrimmedString(svc.docker?.mode) || '—';
|
||||
const desired = () => svc.docker?.desiredTasks ?? 0;
|
||||
const running = () => svc.docker?.runningTasks ?? 0;
|
||||
const update = () => formatUpdate(svc.docker?.serviceUpdate);
|
||||
const ports = () => formatPorts(svc.docker?.endpointPorts);
|
||||
const status = () => getSimpleStatusIndicator(svc.status);
|
||||
<Table class="w-full min-w-[900px] border-collapse text-xs">
|
||||
<TableHeader class="bg-surface-alt text-muted border-b border-border">
|
||||
<TableRow class="text-left text-[10px] uppercase tracking-wide">
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.serviceColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.stackColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.imageColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.modeColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.desiredColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.runningColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.updateColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.portsColumnLabel}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody class="divide-y divide-border-subtle">
|
||||
<For each={filteredServices()}>
|
||||
{(svc) => {
|
||||
const name = () => asTrimmedString(svc.name) || svc.id;
|
||||
const stack = () => asTrimmedString(svc.docker?.stack) || '—';
|
||||
const image = () => asTrimmedString(svc.docker?.image) || '—';
|
||||
const mode = () => asTrimmedString(svc.docker?.mode) || '—';
|
||||
const desired = () => svc.docker?.desiredTasks ?? 0;
|
||||
const running = () => svc.docker?.runningTasks ?? 0;
|
||||
const update = () => formatUpdate(svc.docker?.serviceUpdate);
|
||||
const ports = () => formatPorts(svc.docker?.endpointPorts);
|
||||
const status = () => getSimpleStatusIndicator(svc.status);
|
||||
|
||||
return (
|
||||
<TableRow class="hover:bg-surface-hover">
|
||||
<TableCell class="px-3 py-2">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<StatusDot
|
||||
size="sm"
|
||||
variant={status().variant}
|
||||
title={svc.status || 'unknown'}
|
||||
ariaHidden
|
||||
/>
|
||||
<span
|
||||
class="font-semibold text-base-content truncate"
|
||||
title={name()}
|
||||
>
|
||||
{name()}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{stack()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content truncate" title={image()}>
|
||||
{image()}
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{mode()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{desired()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{running()}</TableCell>
|
||||
<TableCell
|
||||
class="px-3 py-2 text-base-content truncate"
|
||||
title={update()}
|
||||
>
|
||||
{update()}
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content truncate" title={ports()}>
|
||||
{ports()}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
return (
|
||||
<TableRow class="hover:bg-surface-hover">
|
||||
<TableCell class="px-3 py-2">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<StatusDot
|
||||
size="sm"
|
||||
variant={status().variant}
|
||||
title={svc.status || 'unknown'}
|
||||
ariaHidden
|
||||
/>
|
||||
<span class="font-semibold text-base-content truncate" title={name()}>
|
||||
{name()}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{stack()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content truncate" title={image()}>
|
||||
{image()}
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{mode()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{desired()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{running()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content truncate" title={update()}>
|
||||
{update()}
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content truncate" title={ports()}>
|
||||
{ports()}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Show>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,83 +226,78 @@ export const K8sDeploymentsDrawer: Component<{
|
|||
}
|
||||
>
|
||||
<Card padding="none" tone="card" class="overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<Table class="w-full min-w-[760px] border-collapse text-xs">
|
||||
<TableHeader class="bg-surface-alt text-muted border-b border-border">
|
||||
<TableRow class="text-left text-[10px] uppercase tracking-wide">
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.deploymentColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.namespaceColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.desiredColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.updatedColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.readyColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.availableColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.actionsColumnLabel}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody class="divide-y divide-border-subtle">
|
||||
<For each={filteredDeployments()}>
|
||||
{(dep) => {
|
||||
const name = () => asTrimmedString(dep.name) || dep.id;
|
||||
const ns = () => asTrimmedString(dep.kubernetes?.namespace) || '—';
|
||||
const desired = () => dep.kubernetes?.desiredReplicas ?? 0;
|
||||
const updated = () => dep.kubernetes?.updatedReplicas ?? 0;
|
||||
const ready = () => dep.kubernetes?.readyReplicas ?? 0;
|
||||
const available = () => dep.kubernetes?.availableReplicas ?? 0;
|
||||
const status = () => getSimpleStatusIndicator(dep.status);
|
||||
<Table class="w-full min-w-[760px] border-collapse text-xs">
|
||||
<TableHeader class="bg-surface-alt text-muted border-b border-border">
|
||||
<TableRow class="text-left text-[10px] uppercase tracking-wide">
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.deploymentColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.namespaceColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.desiredColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.updatedColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.readyColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.availableColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.actionsColumnLabel}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody class="divide-y divide-border-subtle">
|
||||
<For each={filteredDeployments()}>
|
||||
{(dep) => {
|
||||
const name = () => asTrimmedString(dep.name) || dep.id;
|
||||
const ns = () => asTrimmedString(dep.kubernetes?.namespace) || '—';
|
||||
const desired = () => dep.kubernetes?.desiredReplicas ?? 0;
|
||||
const updated = () => dep.kubernetes?.updatedReplicas ?? 0;
|
||||
const ready = () => dep.kubernetes?.readyReplicas ?? 0;
|
||||
const available = () => dep.kubernetes?.availableReplicas ?? 0;
|
||||
const status = () => getSimpleStatusIndicator(dep.status);
|
||||
|
||||
return (
|
||||
<TableRow class="hover:bg-surface-hover">
|
||||
<TableCell class="px-3 py-2">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<StatusDot
|
||||
size="sm"
|
||||
variant={status().variant}
|
||||
title={dep.status || 'unknown'}
|
||||
ariaHidden
|
||||
/>
|
||||
<span
|
||||
class="font-semibold text-base-content truncate"
|
||||
title={name()}
|
||||
>
|
||||
{name()}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{ns()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{desired()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{updated()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{ready()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{available()}</TableCell>
|
||||
<TableCell class="px-3 py-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openPods(dep.kubernetes?.namespace)}
|
||||
class="rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-semibold text-base-content shadow-sm hover:bg-surface-hover"
|
||||
>
|
||||
{drawerPresentation.viewPodsLabel}
|
||||
</button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
return (
|
||||
<TableRow class="hover:bg-surface-hover">
|
||||
<TableCell class="px-3 py-2">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<StatusDot
|
||||
size="sm"
|
||||
variant={status().variant}
|
||||
title={dep.status || 'unknown'}
|
||||
ariaHidden
|
||||
/>
|
||||
<span class="font-semibold text-base-content truncate" title={name()}>
|
||||
{name()}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{ns()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{desired()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{updated()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{ready()}</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">{available()}</TableCell>
|
||||
<TableCell class="px-3 py-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openPods(dep.kubernetes?.namespace)}
|
||||
class="rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-semibold text-base-content shadow-sm hover:bg-surface-hover"
|
||||
>
|
||||
{drawerPresentation.viewPodsLabel}
|
||||
</button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Show>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,97 +144,93 @@ export const K8sNamespacesDrawer: Component<{
|
|||
}
|
||||
>
|
||||
<Card padding="none" tone="card" class="overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<Table class="w-full min-w-[720px] border-collapse text-xs">
|
||||
<TableHeader class="bg-surface-alt text-muted border-b border-border">
|
||||
<TableRow class="text-left text-[10px] uppercase tracking-wide">
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.namespaceColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.podsColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.deploymentsColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.actionsColumnLabel}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody class="divide-y divide-border-subtle">
|
||||
<For each={filteredRows()}>
|
||||
{(row) => {
|
||||
const podIndicator = () => getNamespaceCountsIndicator(row.pods);
|
||||
return (
|
||||
<TableRow class="hover:bg-surface-hover">
|
||||
<TableCell class="px-3 py-2">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<StatusDot
|
||||
size="sm"
|
||||
variant={podIndicator().variant}
|
||||
title={podIndicator().label}
|
||||
ariaHidden
|
||||
/>
|
||||
<span
|
||||
class="font-semibold text-base-content truncate"
|
||||
title={row.namespace}
|
||||
>
|
||||
{row.namespace}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">
|
||||
<span class="font-semibold">{formatInteger(row.pods.total)}</span>
|
||||
<span class="ml-2 text-[11px] text-muted">
|
||||
{row.pods.offline > 0
|
||||
? `${formatInteger(row.pods.offline)} off`
|
||||
: ''}
|
||||
{row.pods.warning > 0
|
||||
? `${row.pods.offline > 0 ? ' · ' : ''}${formatInteger(row.pods.warning)} warn`
|
||||
: ''}
|
||||
<Table class="w-full min-w-[720px] border-collapse text-xs">
|
||||
<TableHeader class="bg-surface-alt text-muted border-b border-border">
|
||||
<TableRow class="text-left text-[10px] uppercase tracking-wide">
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.namespaceColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.podsColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.deploymentsColumnLabel}
|
||||
</TableHead>
|
||||
<TableHead class="px-3 py-2 font-medium">
|
||||
{drawerPresentation.actionsColumnLabel}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody class="divide-y divide-border-subtle">
|
||||
<For each={filteredRows()}>
|
||||
{(row) => {
|
||||
const podIndicator = () => getNamespaceCountsIndicator(row.pods);
|
||||
return (
|
||||
<TableRow class="hover:bg-surface-hover">
|
||||
<TableCell class="px-3 py-2">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<StatusDot
|
||||
size="sm"
|
||||
variant={podIndicator().variant}
|
||||
title={podIndicator().label}
|
||||
ariaHidden
|
||||
/>
|
||||
<span
|
||||
class="font-semibold text-base-content truncate"
|
||||
title={row.namespace}
|
||||
>
|
||||
{row.namespace}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">
|
||||
<span class="font-semibold">
|
||||
{formatInteger(row.deployments.total)}
|
||||
</span>
|
||||
<span class="ml-2 text-[11px] text-muted">
|
||||
{row.deployments.warning > 0
|
||||
? `${formatInteger(row.deployments.warning)} warn`
|
||||
: ''}
|
||||
{row.deployments.offline > 0
|
||||
? `${row.deployments.warning > 0 ? ' · ' : ''}${formatInteger(row.deployments.offline)} off`
|
||||
: ''}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">
|
||||
<span class="font-semibold">{formatInteger(row.pods.total)}</span>
|
||||
<span class="ml-2 text-[11px] text-muted">
|
||||
{row.pods.offline > 0 ? `${formatInteger(row.pods.offline)} off` : ''}
|
||||
{row.pods.warning > 0
|
||||
? `${row.pods.offline > 0 ? ' · ' : ''}${formatInteger(row.pods.warning)} warn`
|
||||
: ''}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2 text-base-content">
|
||||
<span class="font-semibold">
|
||||
{formatInteger(row.deployments.total)}
|
||||
</span>
|
||||
<span class="ml-2 text-[11px] text-muted">
|
||||
{row.deployments.warning > 0
|
||||
? `${formatInteger(row.deployments.warning)} warn`
|
||||
: ''}
|
||||
{row.deployments.offline > 0
|
||||
? `${row.deployments.warning > 0 ? ' · ' : ''}${formatInteger(row.deployments.offline)} off`
|
||||
: ''}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell class="px-3 py-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openPods(row.namespace)}
|
||||
class="rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-semibold text-base-content shadow-sm hover:bg-surface-hover"
|
||||
>
|
||||
{drawerPresentation.openPodsLabel}
|
||||
</button>
|
||||
<Show when={props.onOpenDeployments}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openPods(row.namespace)}
|
||||
onClick={() => props.onOpenDeployments?.(row.namespace)}
|
||||
class="rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-semibold text-base-content shadow-sm hover:bg-surface-hover"
|
||||
>
|
||||
{drawerPresentation.openPodsLabel}
|
||||
{drawerPresentation.viewDeploymentsLabel}
|
||||
</button>
|
||||
<Show when={props.onOpenDeployments}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => props.onOpenDeployments?.(row.namespace)}
|
||||
class="rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-semibold text-base-content shadow-sm hover:bg-surface-hover"
|
||||
>
|
||||
{drawerPresentation.viewDeploymentsLabel}
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Show>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -131,6 +131,9 @@ import unifiedResourceHostTableCardSource from '@/components/Infrastructure/Unif
|
|||
import unifiedResourceServiceInfrastructureCardSource from '@/components/Infrastructure/UnifiedResourceServiceInfrastructureCard.tsx?raw';
|
||||
import unifiedResourcePBSTableSectionSource from '@/components/Infrastructure/UnifiedResourcePBSTableSection.tsx?raw';
|
||||
import unifiedResourcePMGTableSectionSource from '@/components/Infrastructure/UnifiedResourcePMGTableSection.tsx?raw';
|
||||
import swarmServicesDrawerSource from '@/components/Docker/SwarmServicesDrawer.tsx?raw';
|
||||
import k8sDeploymentsDrawerSource from '@/components/Kubernetes/K8sDeploymentsDrawer.tsx?raw';
|
||||
import k8sNamespacesDrawerSource from '@/components/Kubernetes/K8sNamespacesDrawer.tsx?raw';
|
||||
import nodeGroupHeaderSource from '@/components/shared/NodeGroupHeader.tsx?raw';
|
||||
import storageGroupRowSource from '@/components/Storage/StorageGroupRow.tsx?raw';
|
||||
import storageGroupPresentationSource from '@/features/storageBackups/groupPresentation.ts?raw';
|
||||
|
|
@ -528,6 +531,9 @@ describe('shared primitive guardrails', () => {
|
|||
diskListSource,
|
||||
infrastructureSourceManagerSource,
|
||||
configuredNodeTablesSource,
|
||||
swarmServicesDrawerSource,
|
||||
k8sDeploymentsDrawerSource,
|
||||
k8sNamespacesDrawerSource,
|
||||
]) {
|
||||
expect(source).toContain('<Table');
|
||||
expect(source).not.toContain('<div class="overflow-x-auto">');
|
||||
|
|
|
|||
|
|
@ -2422,6 +2422,8 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(swarmServicesDrawerSource).toContain('getSwarmDrawerPresentation');
|
||||
expect(swarmServicesDrawerSource).toContain('getSwarmServicesEmptyState');
|
||||
expect(swarmServicesDrawerSource).toContain('getSwarmServicesLoadingState');
|
||||
expect(swarmServicesDrawerSource).toMatch(/<Table(?:\s|>)/);
|
||||
expect(swarmServicesDrawerSource).not.toContain('<div class="overflow-x-auto">');
|
||||
expect(swarmServicesDrawerSource).not.toContain('const statusTone =');
|
||||
expect(swarmServicesDrawerSource).not.toContain('No Swarm cluster detected');
|
||||
expect(swarmServicesDrawerSource).not.toContain('Search services...');
|
||||
|
|
@ -2436,6 +2438,8 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(k8sDeploymentsDrawerSource).toContain('getK8sDeploymentsDrawerPresentation');
|
||||
expect(k8sDeploymentsDrawerSource).toContain('getK8sDeploymentsEmptyState');
|
||||
expect(k8sDeploymentsDrawerSource).toContain('getK8sDeploymentsLoadingState');
|
||||
expect(k8sDeploymentsDrawerSource).toMatch(/<Table(?:\s|>)/);
|
||||
expect(k8sDeploymentsDrawerSource).not.toContain('<div class="overflow-x-auto">');
|
||||
expect(k8sDeploymentPresentationSource).toContain('getAllFilterOptionLabel');
|
||||
expect(k8sDeploymentsDrawerSource).not.toContain('const statusTone =');
|
||||
expect(k8sDeploymentsDrawerSource).not.toContain('Desired state controllers (not Pods)');
|
||||
|
|
@ -2463,6 +2467,8 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(k8sNamespacesDrawerSource).toContain('getK8sNamespacesLoadingState');
|
||||
expect(k8sNamespacesDrawerSource).toContain('getK8sNamespacesFailureState');
|
||||
expect(k8sNamespacesDrawerSource).toContain('<StatusDot');
|
||||
expect(k8sNamespacesDrawerSource).toMatch(/<Table(?:\s|>)/);
|
||||
expect(k8sNamespacesDrawerSource).not.toContain('<div class="overflow-x-auto">');
|
||||
expect(k8sNamespacesDrawerSource).not.toContain('const statusTone =');
|
||||
expect(k8sNamespacesDrawerSource).not.toContain('Scope Pods and Deployments by namespace');
|
||||
expect(k8sNamespacesDrawerSource).not.toContain('Search namespaces...');
|
||||
|
|
|
|||
|
|
@ -774,6 +774,7 @@ class CanonicalCompletionGuardTest(unittest.TestCase):
|
|||
"frontend-modern/src/routing/__tests__/resourceLinks.test.ts",
|
||||
"frontend-modern/src/stores/__tests__/websocket-unified.test.ts",
|
||||
"frontend-modern/src/types/__tests__/resource.test.ts",
|
||||
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
|
||||
"internal/unifiedresources/code_standards_test.go",
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4771,6 +4771,7 @@ class SubsystemLookupTest(unittest.TestCase):
|
|||
"frontend-modern/src/routing/__tests__/resourceLinks.test.ts",
|
||||
"frontend-modern/src/stores/__tests__/websocket-unified.test.ts",
|
||||
"frontend-modern/src/types/__tests__/resource.test.ts",
|
||||
"frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts",
|
||||
"internal/unifiedresources/code_standards_test.go",
|
||||
],
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue