chore: move source to repo root and standardize config
Phase 1 — Structural overhaul: - Move all source from headlamp-sealed-secrets/ subdirectory to repo root - Delete 23 AI-generated docs, 8 pre-built tarballs, release snapshots dir - Remove all working-directory refs from CI/release workflows - Update install-plugin.sh and typedoc.json paths Phase 2 — Config standardization: - Create .eslintrc.js and .prettierrc.js (standard Headlamp configs) - Remove inline eslintConfig/prettier from package.json (drop jsx-a11y, prettier extends) - Rewrite tsconfig.json (package name extend, add compilerOptions.types) - Create vitest.config.mts and vitest.setup.ts (standard from polaris) - Replace headlamp-plugin CLI scripts with direct tool invocation - Rewrite .gitignore with standard baseline Phase 3 — MCP & Claude settings: - Create .mcp.json with github/kubernetes/flux/playwright servers - Create .claude/settings.local.json - Remove 7 specialized agents, keep 3 meta-orchestration agents Phase 4 — Documentation: - Rewrite CLAUDE.md (remove subdirectory refs, standard format) - Add ArtifactHub badge, Architecture section, standardized install methods to README.md - Create CONTRIBUTING.md and SECURITY.md - Fix pre-existing test bugs in validators.test.ts (isValidNamespace returns boolean, not ValidationResult; error message string mismatches) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* SealedSecrets List View
|
||||
*
|
||||
* Displays all SealedSecrets in the cluster with filtering and navigation
|
||||
*/
|
||||
|
||||
import { Link } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||
import {
|
||||
SectionBox,
|
||||
SectionFilterHeader,
|
||||
SimpleTable,
|
||||
StatusLabel,
|
||||
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||
import { Box, Button } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { usePermission } from '../hooks/usePermissions';
|
||||
import { SealedSecret } from '../lib/SealedSecretCRD';
|
||||
import { SealedSecretScope } from '../types';
|
||||
import { EncryptDialog } from './EncryptDialog';
|
||||
import { SealedSecretListSkeleton } from './LoadingSkeletons';
|
||||
import { SealedSecretDetail } from './SealedSecretDetail';
|
||||
import { VersionWarning } from './VersionWarning';
|
||||
|
||||
/**
|
||||
* Format scope for display
|
||||
*/
|
||||
function formatScope(scope: SealedSecretScope): string {
|
||||
switch (scope) {
|
||||
case 'strict':
|
||||
return 'Strict';
|
||||
case 'namespace-wide':
|
||||
return 'Namespace-wide';
|
||||
case 'cluster-wide':
|
||||
return 'Cluster-wide';
|
||||
default:
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SealedSecrets list view component
|
||||
*/
|
||||
export function SealedSecretList() {
|
||||
const { namespace, name } = useParams<{ namespace?: string; name?: string }>();
|
||||
const [sealedSecrets, error, loading] = SealedSecret.useList();
|
||||
const [createDialogOpen, setCreateDialogOpen] = React.useState(false);
|
||||
const { allowed: canCreate } = usePermission(undefined, 'canCreate');
|
||||
|
||||
// Memoize callbacks to prevent re-renders
|
||||
const handleOpenDialog = React.useCallback(() => {
|
||||
setCreateDialogOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleCloseDialog = React.useCallback(() => {
|
||||
setCreateDialogOpen(false);
|
||||
}, []);
|
||||
|
||||
// Memoize column definitions (stable reference for table)
|
||||
const columns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
label: 'Name',
|
||||
getter: (ss: SealedSecret) => (
|
||||
<Link
|
||||
routeName="sealedsecret"
|
||||
params={{
|
||||
namespace: ss.metadata.namespace,
|
||||
name: ss.metadata.name,
|
||||
}}
|
||||
>
|
||||
{ss.metadata.name}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: 'Namespace',
|
||||
getter: (ss: SealedSecret) => ss.metadata.namespace,
|
||||
},
|
||||
{
|
||||
label: 'Encrypted Keys',
|
||||
getter: (ss: SealedSecret) => ss.encryptedKeysCount,
|
||||
},
|
||||
{
|
||||
label: 'Scope',
|
||||
getter: (ss: SealedSecret) => formatScope(ss.scope),
|
||||
},
|
||||
{
|
||||
label: 'Sync Status',
|
||||
getter: (ss: SealedSecret) => (
|
||||
<StatusLabel status={ss.isSynced ? 'success' : 'error'}>
|
||||
{ss.isSynced ? 'Synced' : 'Not Synced'}
|
||||
</StatusLabel>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: 'Age',
|
||||
getter: (ss: SealedSecret) => ss.getAge(),
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
// Memoize actions array (stable reference)
|
||||
const actions = React.useMemo(
|
||||
() =>
|
||||
canCreate
|
||||
? [
|
||||
<Button key="create" variant="contained" color="primary" onClick={handleOpenDialog}>
|
||||
Create Sealed Secret
|
||||
</Button>,
|
||||
]
|
||||
: [],
|
||||
[canCreate, handleOpenDialog]
|
||||
);
|
||||
|
||||
// Show loading skeleton while data is being fetched
|
||||
if (loading) {
|
||||
return (
|
||||
<SectionBox title="Sealed Secrets">
|
||||
<SealedSecretListSkeleton />
|
||||
</SectionBox>
|
||||
);
|
||||
}
|
||||
|
||||
// Show error if CRD is not installed
|
||||
if (error) {
|
||||
return (
|
||||
<SectionBox
|
||||
title="Sealed Secrets"
|
||||
>
|
||||
<Box p={2}>
|
||||
<StatusLabel status="error">Error</StatusLabel>
|
||||
<Box mt={2}>
|
||||
{error.message.includes('404') ? (
|
||||
<>
|
||||
<p>
|
||||
Sealed Secrets CRD not found. Please ensure Sealed Secrets is installed on your
|
||||
cluster.
|
||||
</p>
|
||||
<p>
|
||||
Install with: <code>kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml</code>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<p>Failed to load Sealed Secrets: {error.message}</p>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</SectionBox>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SectionBox
|
||||
title="Sealed Secrets"
|
||||
>
|
||||
<VersionWarning autoDetect showDetails={false} />
|
||||
<SectionFilterHeader title="" noNamespaceFilter={false} actions={actions} />
|
||||
<SimpleTable data={sealedSecrets} columns={columns} />
|
||||
</SectionBox>
|
||||
|
||||
<EncryptDialog open={createDialogOpen} onClose={handleCloseDialog} />
|
||||
|
||||
{namespace && name && <SealedSecretDetail />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user