@burdenoff/microfe-adaptercloud 2026.801.1 → 2026.802.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adaptercloud/components/viz/StackedBar.js +35 -1
- package/dist/adaptercloud/components/viz/StackedBar.js.map +1 -0
- package/dist/adaptercloud/pages/OverviewPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/OverviewPage.js +178 -163
- package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +136 -139
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/costs/CostsPage.js +9 -9
- package/dist/adaptercloud/pages/policies/PolicyFormPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/policies/PolicyFormPage.js +34 -31
- package/dist/adaptercloud/pages/policies/PolicyFormPage.js.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +149 -128
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/topology/TopologyPage.js +4 -4
- package/dist/adaptercloud/pages/topology/TopologyPage.js.map +1 -1
- package/dist/adaptercloud/types/index.d.ts.map +1 -1
- package/dist/types/assistant.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConnectionDetailPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/connections/ConnectionDetailPage.tsx"],"sourcesContent":["/**\n * Connection Detail Page\n * @module adaptercloud/pages/connections\n *\n * One connection (`adaConnection`) — its adapter, health, config, and the\n * resources it has discovered. Supports the validated health transition\n * (`adaSetConnectionStatus`), disable (`adaDisableConnection`), and starting a\n * sync run (`adaStartSyncRun`) against it.\n */\n\nimport { useCallback, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Ban, Pencil, Play, RefreshCw, Server } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataState } from '../../components/DataState';\nimport { ConfirmDialog } from '../../components/ConfirmDialog';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport {\n useConnection,\n useDisableConnection,\n useSetConnectionStatus,\n useStartSyncRun,\n} from '../../hooks/useAdapterCloudApi';\nimport {\n authMethodLabels,\n connectionStatusLabels,\n resourceStateLabels,\n syncKindLabels,\n} from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\nimport { formatMoney } from '../../utils/domain';\nimport type { AdaConnectionStatus, AdaSyncKind } from '../../types';\n\nconst TRANSITIONABLE: AdaConnectionStatus[] = ['CONNECTED', 'DEGRADED', 'ERROR', 'PENDING'];\nconst SYNC_KINDS: AdaSyncKind[] = ['DISCOVERY', 'RECONCILE', 'COST_INGEST', 'FULL'];\n\nexport function ConnectionDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const query = useConnection(id);\n const setStatus = useSetConnectionStatus();\n const disable = useDisableConnection();\n const startRun = useStartSyncRun();\n const conn = query.data;\n\n const [syncKind, setSyncKind] = useState<AdaSyncKind>('DISCOVERY');\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmDisableOpen, setConfirmDisableOpen] = useState(false);\n\n const onError = useCallback(\n (err: unknown) =>\n setActionError(err instanceof Error ? err.message : 'Action failed. Please try again.'),\n []\n );\n\n const handleSetStatus = useCallback(\n (newStatus: AdaConnectionStatus) => {\n if (!id) return;\n setActionError(null);\n setStatus.mutate({ id, status: newStatus }, { onError });\n },\n [id, setStatus, onError]\n );\n\n const handleDisable = useCallback(() => {\n if (!id) return;\n setActionError(null);\n disable.mutate(id, {\n onSuccess: () => setConfirmDisableOpen(false),\n onError,\n });\n }, [id, disable, onError]);\n\n const handleStartRun = useCallback(() => {\n if (!id) return;\n setActionError(null);\n startRun.mutate(\n { connectionId: id, kind: syncKind },\n { onSuccess: () => navigateTo('/sync-runs'), onError }\n );\n }, [id, syncKind, startRun, navigateTo, onError]);\n\n return (\n <PageLayout\n title={conn?.name ?? t('adaptercloud.connections.detailTitle', 'Connection')}\n description={conn?.adapter?.name}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.back', 'Back')}\n </button>\n {conn && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/connections/${conn.id}/edit`)}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <Pencil className=\"h-4 w-4\" />\n {t('adaptercloud.common.edit', 'Edit')}\n </button>\n )}\n </div>\n }\n >\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={!query.isLoading && !conn}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.connections.notFound', 'Connection not found')}\n >\n {conn && (\n <div className=\"space-y-6\">\n {actionError && (\n <p\n role=\"alert\"\n aria-live=\"polite\"\n className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {actionError}\n </p>\n )}\n\n {/* Health + actions */}\n <GlassCard className=\"p-6\">\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <StatusBadge status={conn.status} />\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {authMethodLabels[conn.authMethod]}\n </span>\n </div>\n\n <dl className=\"grid grid-cols-2 gap-4 text-sm sm:grid-cols-4\">\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.connections.region', 'Region')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">{conn.region ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.connections.environment', 'Environment')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {conn.environment ?? '—'}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.connections.resourceCount', 'Resources')}\n </dt>\n <dd className=\"mt-0.5 font-medium tabular-nums text-text-primary\">\n {conn.resourceCount}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.connections.lastSynced', 'Last synced')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {conn.lastSyncedAt ? formatDate(conn.lastSyncedAt) : '—'}\n </dd>\n </div>\n </dl>\n\n {conn.lastError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {conn.lastError}\n </p>\n )}\n\n {/* Health transitions */}\n <div className=\"mt-5 border-t border-border-subtle pt-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.connections.setHealth', 'Set health')}\n </h3>\n <div className=\"flex flex-wrap gap-2\">\n {TRANSITIONABLE.map((s) => (\n <button\n key={s}\n type=\"button\"\n disabled={setStatus.isPending || conn.status === s}\n onClick={() => handleSetStatus(s)}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {connectionStatusLabels[s]}\n </button>\n ))}\n {conn.status !== 'DISABLED' && (\n <button\n type=\"button\"\n disabled={disable.isPending}\n onClick={() => setConfirmDisableOpen(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-40\"\n >\n <Ban className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.connections.disable', 'Disable')}\n </button>\n )}\n </div>\n </div>\n\n {/* Start sync run */}\n <div className=\"mt-5 border-t border-border-subtle pt-5\">\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <RefreshCw className=\"h-4 w-4\" />\n {t('adaptercloud.connections.startSync', 'Start a sync run')}\n </h3>\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n value={syncKind}\n onChange={(e) => setSyncKind(e.target.value as AdaSyncKind)}\n aria-label={t('adaptercloud.connections.syncKind', 'Sync run kind')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {SYNC_KINDS.map((k) => (\n <option key={k} value={k}>\n {syncKindLabels[k]}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n disabled={startRun.isPending}\n onClick={handleStartRun}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n <Play className=\"h-4 w-4\" />\n {t('adaptercloud.connections.run', 'Run')}\n </button>\n </div>\n </div>\n </GlassCard>\n\n {/* Config */}\n {conn.config != null && (\n <GlassCard className=\"p-6\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.connections.config', 'Configuration')}\n </h3>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary\">\n {JSON.stringify(conn.config, null, 2)}\n </pre>\n </GlassCard>\n )}\n\n {/* Resources */}\n <section>\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {t('adaptercloud.connections.discoveredResources', 'Discovered Resources')}\n </h3>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/resources?connectionId=${conn.id}`)}\n className=\"text-sm font-medium text-accent-blue hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}\n </button>\n </div>\n {conn.resources && conn.resources.length > 0 ? (\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {conn.resources.slice(0, 10).map((r) => (\n <button\n key={r.id}\n type=\"button\"\n onClick={() => navigateTo(`/resources/${r.id}`)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <Server className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{r.name}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {r.resourceType}\n {r.region ? ` · ${r.region}` : ''}\n </p>\n </div>\n <span className=\"hidden text-xs text-text-secondary sm:inline\">\n {formatMoney(r.monthlyCost)}\n </span>\n <StatusBadge status={r.state} label={resourceStateLabels[r.state]} />\n </button>\n ))}\n </div>\n ) : (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary\">\n {t(\n 'adaptercloud.connections.noResources',\n 'No resources discovered yet. Start a discovery sync run.'\n )}\n </p>\n )}\n </section>\n </div>\n )}\n </DataState>\n\n <ConfirmDialog\n open={confirmDisableOpen}\n onOpenChange={setConfirmDisableOpen}\n title={t('adaptercloud.connections.disableTitle', 'Disable this connection?')}\n description={t(\n 'adaptercloud.connections.disablePrompt',\n 'Disabling stops discovery and sync runs against this target. You can re-enable it later by setting its health.'\n )}\n confirmLabel={t('adaptercloud.connections.disable', 'Disable')}\n cancelLabel={t('adaptercloud.common.cancel', 'Cancel')}\n onConfirm={handleDisable}\n isPending={disable.isPending}\n tone=\"danger\"\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ConnectionDetailPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/connections/ConnectionDetailPage.tsx"],"sourcesContent":["/**\n * Connection Detail Page\n * @module adaptercloud/pages/connections\n *\n * One connection (`adaConnection`) — its adapter, health, config, and the\n * resources it has discovered. Supports the validated health transition\n * (`adaSetConnectionStatus`), disable (`adaDisableConnection`), and starting a\n * sync run (`adaStartSyncRun`) against it.\n */\n\nimport { useCallback, useState } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Ban, Pencil, Play, RefreshCw, Server } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataState } from '../../components/DataState';\nimport { ConfirmDialog } from '../../components/ConfirmDialog';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport {\n useConnection,\n useDisableConnection,\n useSetConnectionStatus,\n useStartSyncRun,\n} from '../../hooks/useAdapterCloudApi';\nimport {\n authMethodLabels,\n connectionStatusLabels,\n resourceStateLabels,\n syncKindLabels,\n} from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\nimport { formatMoney } from '../../utils/domain';\nimport type { AdaConnectionStatus, AdaSyncKind } from '../../types';\n\nconst TRANSITIONABLE: AdaConnectionStatus[] = ['CONNECTED', 'DEGRADED', 'ERROR', 'PENDING'];\nconst SYNC_KINDS: AdaSyncKind[] = ['DISCOVERY', 'RECONCILE', 'COST_INGEST', 'FULL'];\n\nexport function ConnectionDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const query = useConnection(id);\n const setStatus = useSetConnectionStatus();\n const disable = useDisableConnection();\n const startRun = useStartSyncRun();\n const conn = query.data;\n\n const [runningKind, setRunningKind] = useState<AdaSyncKind | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmDisableOpen, setConfirmDisableOpen] = useState(false);\n\n const onError = useCallback(\n (err: unknown) =>\n setActionError(err instanceof Error ? err.message : 'Action failed. Please try again.'),\n []\n );\n\n const handleSetStatus = useCallback(\n (newStatus: AdaConnectionStatus) => {\n if (!id) return;\n setActionError(null);\n setStatus.mutate({ id, status: newStatus }, { onError });\n },\n [id, setStatus, onError]\n );\n\n const handleDisable = useCallback(() => {\n if (!id) return;\n setActionError(null);\n disable.mutate(id, {\n onSuccess: () => setConfirmDisableOpen(false),\n onError,\n });\n }, [id, disable, onError]);\n\n const handleStartKind = useCallback(\n (kind: AdaSyncKind) => {\n if (!id) return;\n setActionError(null);\n setRunningKind(kind);\n startRun.mutate(\n { connectionId: id, kind },\n {\n onSuccess: () => {\n setRunningKind(null);\n navigateTo('/sync-runs');\n },\n onError: (err) => {\n setRunningKind(null);\n onError(err);\n },\n }\n );\n },\n [id, startRun, navigateTo, onError]\n );\n\n return (\n <PageLayout\n title={conn?.name ?? t('adaptercloud.connections.detailTitle', 'Connection')}\n description={conn?.adapter?.name}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.back', 'Back')}\n </button>\n {conn && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/connections/${conn.id}/edit`)}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <Pencil className=\"h-4 w-4\" />\n {t('adaptercloud.common.edit', 'Edit')}\n </button>\n )}\n </div>\n }\n >\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={!query.isLoading && !conn}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.connections.notFound', 'Connection not found')}\n >\n {conn && (\n <div className=\"space-y-6\">\n {actionError && (\n <p\n role=\"alert\"\n aria-live=\"polite\"\n className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {actionError}\n </p>\n )}\n\n {/* Health + actions */}\n <GlassCard className=\"p-6\">\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <StatusBadge status={conn.status} />\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {authMethodLabels[conn.authMethod]}\n </span>\n </div>\n\n <dl className=\"grid grid-cols-2 gap-4 text-sm sm:grid-cols-4\">\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.connections.region', 'Region')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">{conn.region ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.connections.environment', 'Environment')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {conn.environment ?? '—'}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.connections.resourceCount', 'Resources')}\n </dt>\n <dd className=\"mt-0.5 font-medium tabular-nums text-text-primary\">\n {conn.resourceCount}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.connections.lastSynced', 'Last synced')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {conn.lastSyncedAt ? formatDate(conn.lastSyncedAt) : '—'}\n </dd>\n </div>\n </dl>\n\n {conn.lastError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {conn.lastError}\n </p>\n )}\n\n {/* Health transitions */}\n <div className=\"mt-5 border-t border-border-subtle pt-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.connections.setHealth', 'Set health')}\n </h3>\n <div className=\"flex flex-wrap gap-2\">\n {TRANSITIONABLE.map((s) => (\n <button\n key={s}\n type=\"button\"\n disabled={setStatus.isPending || conn.status === s}\n onClick={() => handleSetStatus(s)}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {connectionStatusLabels[s]}\n </button>\n ))}\n {conn.status !== 'DISABLED' && (\n <button\n type=\"button\"\n disabled={disable.isPending}\n onClick={() => setConfirmDisableOpen(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-40\"\n >\n <Ban className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.connections.disable', 'Disable')}\n </button>\n )}\n </div>\n </div>\n\n {/* Start sync run */}\n <div className=\"mt-5 border-t border-border-subtle pt-5\">\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <RefreshCw className=\"h-4 w-4\" />\n {t('adaptercloud.connections.startSync', 'Start a sync run')}\n </h3>\n <div className=\"flex flex-wrap gap-2\">\n {SYNC_KINDS.map((k) => {\n const pending = startRun.isPending && runningKind === k;\n return (\n <button\n key={k}\n type=\"button\"\n disabled={startRun.isPending}\n onClick={() => handleStartKind(k)}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:opacity-50\"\n >\n {pending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Play className=\"h-4 w-4\" />\n )}\n {syncKindLabels[k]}\n </button>\n );\n })}\n </div>\n </div>\n </GlassCard>\n\n {/* Config */}\n {conn.config != null && (\n <GlassCard className=\"p-6\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.connections.config', 'Configuration')}\n </h3>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary\">\n {JSON.stringify(conn.config, null, 2)}\n </pre>\n </GlassCard>\n )}\n\n {/* Resources */}\n <section>\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {t('adaptercloud.connections.discoveredResources', 'Discovered Resources')}\n </h3>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/resources?connectionId=${conn.id}`)}\n className=\"text-sm font-medium text-accent-blue hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}\n </button>\n </div>\n {conn.resources && conn.resources.length > 0 ? (\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {conn.resources.slice(0, 10).map((r) => (\n <button\n key={r.id}\n type=\"button\"\n onClick={() => navigateTo(`/resources/${r.id}`)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <Server className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{r.name}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {r.resourceType}\n {r.region ? ` · ${r.region}` : ''}\n </p>\n </div>\n <span className=\"hidden text-xs text-text-secondary sm:inline\">\n {formatMoney(r.monthlyCost)}\n </span>\n <StatusBadge status={r.state} label={resourceStateLabels[r.state]} />\n </button>\n ))}\n </div>\n ) : (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary\">\n {t(\n 'adaptercloud.connections.noResources',\n 'No resources discovered yet. Start a discovery sync run.'\n )}\n </p>\n )}\n </section>\n </div>\n )}\n </DataState>\n\n <ConfirmDialog\n open={confirmDisableOpen}\n onOpenChange={setConfirmDisableOpen}\n title={t('adaptercloud.connections.disableTitle', 'Disable this connection?')}\n description={t(\n 'adaptercloud.connections.disablePrompt',\n 'Disabling stops discovery and sync runs against this target. You can re-enable it later by setting its health.'\n )}\n confirmLabel={t('adaptercloud.connections.disable', 'Disable')}\n cancelLabel={t('adaptercloud.common.cancel', 'Cancel')}\n onConfirm={handleDisable}\n isPending={disable.isPending}\n tone=\"danger\"\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -3,11 +3,11 @@ import { PageLayout as t } from "../../components/PageLayout.js";
|
|
|
3
3
|
import { MetricsCard as n } from "../../components/MetricsCard.js";
|
|
4
4
|
import { formatMoney as r } from "../../utils/domain.js";
|
|
5
5
|
import { DataState as i } from "../../components/DataState.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { BarChart as c } from "../../components/viz/BarChart.js";
|
|
9
|
-
import { Sparkline as l } from "../../components/viz/Sparkline.js";
|
|
6
|
+
import { BarChart as a } from "../../components/viz/BarChart.js";
|
|
7
|
+
import { Sparkline as o } from "../../components/viz/Sparkline.js";
|
|
10
8
|
import "../../components/viz/index.js";
|
|
9
|
+
import { useCostRecords as s, useCostSummary as c } from "../../hooks/useAdapterCloudApi.js";
|
|
10
|
+
import { formatDate as l } from "../../utils/formatters.js";
|
|
11
11
|
import { useCallback as u, useMemo as d, useState as f } from "react";
|
|
12
12
|
import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
13
13
|
import { DollarSign as m, Receipt as h, Search as g, X as _ } from "lucide-react";
|
|
@@ -18,7 +18,7 @@ function w() {
|
|
|
18
18
|
let { navigateTo: w } = e(), { t: T } = p(), E = u((e, t) => {
|
|
19
19
|
let n = T(e);
|
|
20
20
|
return n === e ? t : n;
|
|
21
|
-
}, [T]), [D, O] = f(""), [k, A] = f(""), j =
|
|
21
|
+
}, [T]), [D, O] = f(""), [k, A] = f(""), j = c(), M = s({ service: k || void 0 }), N = j.data?.currency ?? "USD", P = d(() => [...j.data?.lines ?? []].sort((e, t) => t.total - e.total), [j.data]), F = d(() => P.map((e) => ({
|
|
22
22
|
key: e.service,
|
|
23
23
|
label: e.service,
|
|
24
24
|
value: e.total,
|
|
@@ -66,13 +66,13 @@ function w() {
|
|
|
66
66
|
cell: (e) => /* @__PURE__ */ b("span", {
|
|
67
67
|
className: "text-sm text-text-secondary",
|
|
68
68
|
children: [
|
|
69
|
-
|
|
69
|
+
l(e.periodStart, {
|
|
70
70
|
hour: void 0,
|
|
71
71
|
minute: void 0
|
|
72
72
|
}),
|
|
73
73
|
" –",
|
|
74
74
|
" ",
|
|
75
|
-
|
|
75
|
+
l(e.periodEnd, {
|
|
76
76
|
hour: void 0,
|
|
77
77
|
minute: void 0
|
|
78
78
|
})
|
|
@@ -147,7 +147,7 @@ function w() {
|
|
|
147
147
|
children: E("adaptercloud.costs.byService", "Spend by Service")
|
|
148
148
|
}), /* @__PURE__ */ y(S, {
|
|
149
149
|
className: "p-5",
|
|
150
|
-
children: /* @__PURE__ */ y(
|
|
150
|
+
children: /* @__PURE__ */ y(a, {
|
|
151
151
|
data: F,
|
|
152
152
|
labelWidthClassName: "w-24 sm:w-32"
|
|
153
153
|
})
|
|
@@ -173,7 +173,7 @@ function w() {
|
|
|
173
173
|
E("adaptercloud.costs.vsPrev", "vs prev")
|
|
174
174
|
]
|
|
175
175
|
})]
|
|
176
|
-
}), /* @__PURE__ */ y(
|
|
176
|
+
}), /* @__PURE__ */ y(o, {
|
|
177
177
|
points: I.points,
|
|
178
178
|
className: "mt-4 h-16 w-full text-accent-blue"
|
|
179
179
|
})] }) : /* @__PURE__ */ y("p", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PolicyFormPage.d.ts","sourceRoot":"","sources":["../../../../src/adaptercloud/pages/policies/PolicyFormPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"PolicyFormPage.d.ts","sourceRoot":"","sources":["../../../../src/adaptercloud/pages/policies/PolicyFormPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0BH,wBAAgB,cAAc,gCAwU7B"}
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { useAdapterCloud as e } from "../../context/AdapterCloudContext.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
2
|
+
import { cn as t } from "../../utils/cn.js";
|
|
3
|
+
import { PageLayout as n } from "../../components/PageLayout.js";
|
|
4
|
+
import { policyEnforcementLabels as r, policyKindLabels as i, severityLabels as a } from "../../utils/domain.js";
|
|
5
|
+
import { DataState as o } from "../../components/DataState.js";
|
|
6
|
+
import { useCreatePolicy as s, usePolicy as c, useUpdatePolicy as l } from "../../hooks/useAdapterCloudApi.js";
|
|
7
|
+
import { useCallback as u, useEffect as d, useState as f } from "react";
|
|
8
|
+
import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
9
|
+
import { ArrowLeft as m, Loader2 as h, Save as g } from "lucide-react";
|
|
10
|
+
import { useParams as ee } from "react-router";
|
|
10
11
|
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
11
12
|
import { GlassCard as y } from "@burdenoff/fe-libs/ui";
|
|
12
13
|
//#region src/adaptercloud/pages/policies/PolicyFormPage.tsx
|
|
13
14
|
var b = (e) => e.split(",").map((e) => e.trim()).filter(Boolean), x = "{\n \"match\": { \"resourceType\": \"*\" },\n \"require\": { \"tags\": [\"owner\", \"env\"] }\n}";
|
|
14
15
|
function S() {
|
|
15
|
-
let { id: S } =
|
|
16
|
+
let { id: S } = ee(), C = !!S, { navigateTo: w } = e(), { t: T } = p(), E = u((e, t) => {
|
|
16
17
|
let n = T(e);
|
|
17
18
|
return n === e ? t : n;
|
|
18
|
-
}, [T]), D =
|
|
19
|
-
|
|
19
|
+
}, [T]), D = c(C ? S : void 0), O = s(), k = l(), [A, j] = f(""), [M, N] = f(""), [P, F] = f("GUARDRAIL"), [I, L] = f("WARN"), [R, z] = f("MEDIUM"), [B, V] = f(""), [H, U] = f(""), [W, G] = f(!0), [K, q] = f(x), [J, Y] = f(null);
|
|
20
|
+
d(() => {
|
|
20
21
|
let e = D.data;
|
|
21
22
|
C && e && (j(e.name), N(e.description ?? ""), F(e.kind), L(e.enforcement), z(e.severity), V(e.appliesTo.join(", ")), U(e.tags.join(", ")), G(e.isActive), q(JSON.stringify(e.expression, null, 2)));
|
|
22
23
|
}, [C, D.data]);
|
|
23
|
-
let X = O.isPending || k.isPending, Z =
|
|
24
|
+
let X = O.isPending || k.isPending, Z = u((e) => {
|
|
24
25
|
if (e.preventDefault(), Y(null), !A.trim()) {
|
|
25
26
|
Y(E("adaptercloud.policies.form.nameRequired", "Name is required."));
|
|
26
27
|
return;
|
|
@@ -70,16 +71,16 @@ function S() {
|
|
|
70
71
|
w,
|
|
71
72
|
E
|
|
72
73
|
]), Q = "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]", $ = "mb-1.5 block text-sm font-medium text-text-primary";
|
|
73
|
-
return /* @__PURE__ */ _(
|
|
74
|
+
return /* @__PURE__ */ _(n, {
|
|
74
75
|
title: C ? E("adaptercloud.policies.editTitle", "Edit Policy") : E("adaptercloud.policies.newTitle", "New Policy"),
|
|
75
76
|
description: E("adaptercloud.policies.formSubtitle", "Put a governance rule into the system instead of a wiki."),
|
|
76
77
|
actions: /* @__PURE__ */ v("button", {
|
|
77
78
|
type: "button",
|
|
78
79
|
onClick: () => w(C && S ? `/policies/${S}` : "/policies"),
|
|
79
80
|
className: "inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent",
|
|
80
|
-
children: [/* @__PURE__ */ _(
|
|
81
|
+
children: [/* @__PURE__ */ _(m, { className: "h-4 w-4" }), E("adaptercloud.common.cancel", "Cancel")]
|
|
81
82
|
}),
|
|
82
|
-
children: /* @__PURE__ */ _(
|
|
83
|
+
children: /* @__PURE__ */ _(o, {
|
|
83
84
|
isLoading: C && D.isLoading,
|
|
84
85
|
isError: C && D.isError,
|
|
85
86
|
isEmpty: C && !D.isLoading && !D.data,
|
|
@@ -128,24 +129,26 @@ function S() {
|
|
|
128
129
|
value: P,
|
|
129
130
|
onChange: (e) => F(e.target.value),
|
|
130
131
|
className: Q,
|
|
131
|
-
children: Object.keys(
|
|
132
|
+
children: Object.keys(i).map((e) => /* @__PURE__ */ _("option", {
|
|
132
133
|
value: e,
|
|
133
|
-
children:
|
|
134
|
+
children: i[e]
|
|
134
135
|
}, e))
|
|
135
136
|
})] }),
|
|
136
|
-
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("
|
|
137
|
-
htmlFor: "ada-pol-enf",
|
|
137
|
+
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("span", {
|
|
138
138
|
className: $,
|
|
139
139
|
children: E("adaptercloud.policies.form.enforcement", "Enforcement")
|
|
140
|
-
}), /* @__PURE__ */ _("
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
140
|
+
}), /* @__PURE__ */ _("div", {
|
|
141
|
+
className: "flex rounded-lg border border-border-subtle p-1",
|
|
142
|
+
children: Object.keys(r).map((e) => {
|
|
143
|
+
let n = I === e;
|
|
144
|
+
return /* @__PURE__ */ _("button", {
|
|
145
|
+
type: "button",
|
|
146
|
+
onClick: () => L(e),
|
|
147
|
+
className: t("flex-1 rounded-md px-3 py-1.5 text-sm font-medium transition-colors", n ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:bg-accent"),
|
|
148
|
+
"aria-pressed": n,
|
|
149
|
+
children: r[e]
|
|
150
|
+
}, e);
|
|
151
|
+
})
|
|
149
152
|
})] }),
|
|
150
153
|
/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
|
|
151
154
|
htmlFor: "ada-pol-sev",
|
|
@@ -156,9 +159,9 @@ function S() {
|
|
|
156
159
|
value: R,
|
|
157
160
|
onChange: (e) => z(e.target.value),
|
|
158
161
|
className: Q,
|
|
159
|
-
children: Object.keys(
|
|
162
|
+
children: Object.keys(a).map((e) => /* @__PURE__ */ _("option", {
|
|
160
163
|
value: e,
|
|
161
|
-
children:
|
|
164
|
+
children: a[e]
|
|
162
165
|
}, e))
|
|
163
166
|
})] })
|
|
164
167
|
]
|
|
@@ -227,7 +230,7 @@ function S() {
|
|
|
227
230
|
type: "submit",
|
|
228
231
|
disabled: X,
|
|
229
232
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50",
|
|
230
|
-
children: [X ? /* @__PURE__ */ _(
|
|
233
|
+
children: [X ? /* @__PURE__ */ _(h, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ _(g, { className: "h-4 w-4" }), C ? E("adaptercloud.common.saveChanges", "Save changes") : E("adaptercloud.policies.create", "Create policy")]
|
|
231
234
|
})
|
|
232
235
|
})
|
|
233
236
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PolicyFormPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/policies/PolicyFormPage.tsx"],"sourcesContent":["/**\n * Policy Create / Edit Form Page\n * @module adaptercloud/pages/policies\n *\n * Authors a new governance policy (`adaCreatePolicy`) or edits an existing one\n * (`adaUpdatePolicy`). The presence of a route `:id` switches to edit mode and\n * pre-fills from `adaPolicy` (including the JSON expression). The expression is a\n * JSON document validated client-side before submit.\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Loader2, Save } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { usePolicy, useCreatePolicy, useUpdatePolicy } from '../../hooks/useAdapterCloudApi';\nimport { policyKindLabels, policyEnforcementLabels, severityLabels } from '../../utils/domain';\nimport type { AdaPolicyEnforcement, AdaPolicyKind, AdaSeverity } from '../../types';\n\nconst parseList = (value: string): string[] =>\n value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\nconst DEFAULT_EXPRESSION = `{\n \"match\": { \"resourceType\": \"*\" },\n \"require\": { \"tags\": [\"owner\", \"env\"] }\n}`;\n\nexport function PolicyFormPage() {\n const { id } = useParams<{ id: string }>();\n const isEdit = Boolean(id);\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const existing = usePolicy(isEdit ? id : undefined);\n const create = useCreatePolicy();\n const update = useUpdatePolicy();\n\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [kind, setKind] = useState<AdaPolicyKind>('GUARDRAIL');\n const [enforcement, setEnforcement] = useState<AdaPolicyEnforcement>('WARN');\n const [severity, setSeverity] = useState<AdaSeverity>('MEDIUM');\n const [appliesTo, setAppliesTo] = useState('');\n const [tags, setTags] = useState('');\n const [isActive, setIsActive] = useState(true);\n const [expression, setExpression] = useState(DEFAULT_EXPRESSION);\n const [error, setError] = useState<string | null>(null);\n\n // Pre-fill in edit mode from the loaded policy.\n useEffect(() => {\n const p = existing.data;\n if (isEdit && p) {\n setName(p.name);\n setDescription(p.description ?? '');\n setKind(p.kind);\n setEnforcement(p.enforcement);\n setSeverity(p.severity);\n setAppliesTo(p.appliesTo.join(', '));\n setTags(p.tags.join(', '));\n setIsActive(p.isActive);\n setExpression(JSON.stringify(p.expression, null, 2));\n }\n }, [isEdit, existing.data]);\n\n const isPending = create.isPending || update.isPending;\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!name.trim()) {\n setError(t('adaptercloud.policies.form.nameRequired', 'Name is required.'));\n return;\n }\n\n let parsedExpression: unknown;\n try {\n parsedExpression = JSON.parse(expression);\n } catch {\n setError(\n t('adaptercloud.policies.form.invalidExpression', 'Expression must be valid JSON.')\n );\n return;\n }\n\n const common = {\n name: name.trim(),\n description: description.trim() || undefined,\n kind,\n enforcement,\n severity,\n isActive,\n appliesTo: parseList(appliesTo),\n tags: parseList(tags),\n expression: parsedExpression,\n };\n\n const onError = (err: unknown) =>\n setError(err instanceof Error ? err.message : 'Failed to save policy.');\n\n if (isEdit && id) {\n update.mutate(\n { id, input: common },\n { onSuccess: (p) => navigateTo(`/policies/${p.id}`), onError }\n );\n } else {\n create.mutate(common, {\n onSuccess: (p) => navigateTo(`/policies/${p.id}`),\n onError,\n });\n }\n },\n [\n create,\n update,\n id,\n isEdit,\n name,\n description,\n kind,\n enforcement,\n severity,\n isActive,\n appliesTo,\n tags,\n expression,\n navigateTo,\n t,\n ]\n );\n\n const fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n const labelClass = 'mb-1.5 block text-sm font-medium text-text-primary';\n\n return (\n <PageLayout\n title={\n isEdit\n ? t('adaptercloud.policies.editTitle', 'Edit Policy')\n : t('adaptercloud.policies.newTitle', 'New Policy')\n }\n description={t(\n 'adaptercloud.policies.formSubtitle',\n 'Put a governance rule into the system instead of a wiki.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo(isEdit && id ? `/policies/${id}` : '/policies')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.cancel', 'Cancel')}\n </button>\n }\n >\n <DataState\n isLoading={isEdit && existing.isLoading}\n isError={isEdit && existing.isError}\n isEmpty={isEdit && !existing.isLoading && !existing.data}\n error={existing.error}\n onRetry={() => void existing.refetch()}\n emptyTitle={t('adaptercloud.policies.notFound', 'Policy not found')}\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl\">\n <GlassCard className=\"space-y-5 p-6\">\n <div>\n <label htmlFor=\"ada-pol-name\" className={labelClass}>\n {t('adaptercloud.policies.form.name', 'Name')} *\n </label>\n <input\n id=\"ada-pol-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className={fieldClass}\n placeholder=\"Require owner + env tags\"\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-desc\" className={labelClass}>\n {t('adaptercloud.policies.form.description', 'Description')}\n </label>\n <textarea\n id=\"ada-pol-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={fieldClass}\n placeholder={t(\n 'adaptercloud.policies.form.descriptionPlaceholder',\n 'What this policy enforces and why.'\n )}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"ada-pol-kind\" className={labelClass}>\n {t('adaptercloud.policies.form.kind', 'Kind')}\n </label>\n <select\n id=\"ada-pol-kind\"\n value={kind}\n onChange={(e) => setKind(e.target.value as AdaPolicyKind)}\n className={fieldClass}\n >\n {(Object.keys(policyKindLabels) as AdaPolicyKind[]).map((k) => (\n <option key={k} value={k}>\n {policyKindLabels[k]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ada-pol-enf\" className={labelClass}>\n {t('adaptercloud.policies.form.enforcement', 'Enforcement')}\n </label>\n <select\n id=\"ada-pol-enf\"\n value={enforcement}\n onChange={(e) => setEnforcement(e.target.value as AdaPolicyEnforcement)}\n className={fieldClass}\n >\n {(Object.keys(policyEnforcementLabels) as AdaPolicyEnforcement[]).map((en) => (\n <option key={en} value={en}>\n {policyEnforcementLabels[en]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ada-pol-sev\" className={labelClass}>\n {t('adaptercloud.policies.form.severity', 'Severity')}\n </label>\n <select\n id=\"ada-pol-sev\"\n value={severity}\n onChange={(e) => setSeverity(e.target.value as AdaSeverity)}\n className={fieldClass}\n >\n {(Object.keys(severityLabels) as AdaSeverity[]).map((s) => (\n <option key={s} value={s}>\n {severityLabels[s]}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-applies\" className={labelClass}>\n {t('adaptercloud.policies.form.appliesTo', 'Applies To')}\n </label>\n <input\n id=\"ada-pol-applies\"\n type=\"text\"\n value={appliesTo}\n onChange={(e) => setAppliesTo(e.target.value)}\n className={fieldClass}\n placeholder=\"aws:ec2:instance, aws:s3:bucket\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.common.commaSeparated', 'Comma-separated.')}\n </p>\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-expr\" className={labelClass}>\n {t('adaptercloud.policies.form.expression', 'Expression (JSON)')} *\n </label>\n <textarea\n id=\"ada-pol-expr\"\n value={expression}\n onChange={(e) => setExpression(e.target.value)}\n rows={8}\n spellCheck={false}\n className={`${fieldClass} font-mono`}\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-tags\" className={labelClass}>\n {t('adaptercloud.common.tags', 'Tags')}\n </label>\n <input\n id=\"ada-pol-tags\"\n type=\"text\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n className={fieldClass}\n placeholder=\"tagging, governance\"\n />\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isActive}\n onChange={(e) => setIsActive(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n {t('adaptercloud.policies.form.activeImmediately', 'Active immediately')}\n </label>\n\n {error && (\n <p\n role=\"alert\"\n aria-live=\"polite\"\n className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {error}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n {isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {isEdit\n ? t('adaptercloud.common.saveChanges', 'Save changes')\n : t('adaptercloud.policies.create', 'Create policy')}\n </button>\n </div>\n </GlassCard>\n </form>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PolicyFormPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/policies/PolicyFormPage.tsx"],"sourcesContent":["/**\n * Policy Create / Edit Form Page\n * @module adaptercloud/pages/policies\n *\n * Authors a new governance policy (`adaCreatePolicy`) or edits an existing one\n * (`adaUpdatePolicy`). The presence of a route `:id` switches to edit mode and\n * pre-fills from `adaPolicy` (including the JSON expression). The expression is a\n * JSON document validated client-side before submit.\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Loader2, Save } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { usePolicy, useCreatePolicy, useUpdatePolicy } from '../../hooks/useAdapterCloudApi';\nimport { policyKindLabels, policyEnforcementLabels, severityLabels } from '../../utils/domain';\nimport { cn } from '../../utils/cn';\nimport type { AdaPolicyEnforcement, AdaPolicyKind, AdaSeverity } from '../../types';\n\nconst parseList = (value: string): string[] =>\n value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\nconst DEFAULT_EXPRESSION = `{\n \"match\": { \"resourceType\": \"*\" },\n \"require\": { \"tags\": [\"owner\", \"env\"] }\n}`;\n\nexport function PolicyFormPage() {\n const { id } = useParams<{ id: string }>();\n const isEdit = Boolean(id);\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const existing = usePolicy(isEdit ? id : undefined);\n const create = useCreatePolicy();\n const update = useUpdatePolicy();\n\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [kind, setKind] = useState<AdaPolicyKind>('GUARDRAIL');\n const [enforcement, setEnforcement] = useState<AdaPolicyEnforcement>('WARN');\n const [severity, setSeverity] = useState<AdaSeverity>('MEDIUM');\n const [appliesTo, setAppliesTo] = useState('');\n const [tags, setTags] = useState('');\n const [isActive, setIsActive] = useState(true);\n const [expression, setExpression] = useState(DEFAULT_EXPRESSION);\n const [error, setError] = useState<string | null>(null);\n\n // Pre-fill in edit mode from the loaded policy.\n useEffect(() => {\n const p = existing.data;\n if (isEdit && p) {\n setName(p.name);\n setDescription(p.description ?? '');\n setKind(p.kind);\n setEnforcement(p.enforcement);\n setSeverity(p.severity);\n setAppliesTo(p.appliesTo.join(', '));\n setTags(p.tags.join(', '));\n setIsActive(p.isActive);\n setExpression(JSON.stringify(p.expression, null, 2));\n }\n }, [isEdit, existing.data]);\n\n const isPending = create.isPending || update.isPending;\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!name.trim()) {\n setError(t('adaptercloud.policies.form.nameRequired', 'Name is required.'));\n return;\n }\n\n let parsedExpression: unknown;\n try {\n parsedExpression = JSON.parse(expression);\n } catch {\n setError(\n t('adaptercloud.policies.form.invalidExpression', 'Expression must be valid JSON.')\n );\n return;\n }\n\n const common = {\n name: name.trim(),\n description: description.trim() || undefined,\n kind,\n enforcement,\n severity,\n isActive,\n appliesTo: parseList(appliesTo),\n tags: parseList(tags),\n expression: parsedExpression,\n };\n\n const onError = (err: unknown) =>\n setError(err instanceof Error ? err.message : 'Failed to save policy.');\n\n if (isEdit && id) {\n update.mutate(\n { id, input: common },\n { onSuccess: (p) => navigateTo(`/policies/${p.id}`), onError }\n );\n } else {\n create.mutate(common, {\n onSuccess: (p) => navigateTo(`/policies/${p.id}`),\n onError,\n });\n }\n },\n [\n create,\n update,\n id,\n isEdit,\n name,\n description,\n kind,\n enforcement,\n severity,\n isActive,\n appliesTo,\n tags,\n expression,\n navigateTo,\n t,\n ]\n );\n\n const fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n const labelClass = 'mb-1.5 block text-sm font-medium text-text-primary';\n\n return (\n <PageLayout\n title={\n isEdit\n ? t('adaptercloud.policies.editTitle', 'Edit Policy')\n : t('adaptercloud.policies.newTitle', 'New Policy')\n }\n description={t(\n 'adaptercloud.policies.formSubtitle',\n 'Put a governance rule into the system instead of a wiki.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo(isEdit && id ? `/policies/${id}` : '/policies')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.cancel', 'Cancel')}\n </button>\n }\n >\n <DataState\n isLoading={isEdit && existing.isLoading}\n isError={isEdit && existing.isError}\n isEmpty={isEdit && !existing.isLoading && !existing.data}\n error={existing.error}\n onRetry={() => void existing.refetch()}\n emptyTitle={t('adaptercloud.policies.notFound', 'Policy not found')}\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl\">\n <GlassCard className=\"space-y-5 p-6\">\n <div>\n <label htmlFor=\"ada-pol-name\" className={labelClass}>\n {t('adaptercloud.policies.form.name', 'Name')} *\n </label>\n <input\n id=\"ada-pol-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className={fieldClass}\n placeholder=\"Require owner + env tags\"\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-desc\" className={labelClass}>\n {t('adaptercloud.policies.form.description', 'Description')}\n </label>\n <textarea\n id=\"ada-pol-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={fieldClass}\n placeholder={t(\n 'adaptercloud.policies.form.descriptionPlaceholder',\n 'What this policy enforces and why.'\n )}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"ada-pol-kind\" className={labelClass}>\n {t('adaptercloud.policies.form.kind', 'Kind')}\n </label>\n <select\n id=\"ada-pol-kind\"\n value={kind}\n onChange={(e) => setKind(e.target.value as AdaPolicyKind)}\n className={fieldClass}\n >\n {(Object.keys(policyKindLabels) as AdaPolicyKind[]).map((k) => (\n <option key={k} value={k}>\n {policyKindLabels[k]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <span className={labelClass}>\n {t('adaptercloud.policies.form.enforcement', 'Enforcement')}\n </span>\n <div className=\"flex rounded-lg border border-border-subtle p-1\">\n {(Object.keys(policyEnforcementLabels) as AdaPolicyEnforcement[]).map((en) => {\n const active = enforcement === en;\n return (\n <button\n key={en}\n type=\"button\"\n onClick={() => setEnforcement(en)}\n className={cn(\n 'flex-1 rounded-md px-3 py-1.5 text-sm font-medium transition-colors',\n active\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-accent'\n )}\n aria-pressed={active}\n >\n {policyEnforcementLabels[en]}\n </button>\n );\n })}\n </div>\n </div>\n <div>\n <label htmlFor=\"ada-pol-sev\" className={labelClass}>\n {t('adaptercloud.policies.form.severity', 'Severity')}\n </label>\n <select\n id=\"ada-pol-sev\"\n value={severity}\n onChange={(e) => setSeverity(e.target.value as AdaSeverity)}\n className={fieldClass}\n >\n {(Object.keys(severityLabels) as AdaSeverity[]).map((s) => (\n <option key={s} value={s}>\n {severityLabels[s]}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-applies\" className={labelClass}>\n {t('adaptercloud.policies.form.appliesTo', 'Applies To')}\n </label>\n <input\n id=\"ada-pol-applies\"\n type=\"text\"\n value={appliesTo}\n onChange={(e) => setAppliesTo(e.target.value)}\n className={fieldClass}\n placeholder=\"aws:ec2:instance, aws:s3:bucket\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.common.commaSeparated', 'Comma-separated.')}\n </p>\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-expr\" className={labelClass}>\n {t('adaptercloud.policies.form.expression', 'Expression (JSON)')} *\n </label>\n <textarea\n id=\"ada-pol-expr\"\n value={expression}\n onChange={(e) => setExpression(e.target.value)}\n rows={8}\n spellCheck={false}\n className={`${fieldClass} font-mono`}\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-tags\" className={labelClass}>\n {t('adaptercloud.common.tags', 'Tags')}\n </label>\n <input\n id=\"ada-pol-tags\"\n type=\"text\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n className={fieldClass}\n placeholder=\"tagging, governance\"\n />\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isActive}\n onChange={(e) => setIsActive(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n {t('adaptercloud.policies.form.activeImmediately', 'Active immediately')}\n </label>\n\n {error && (\n <p\n role=\"alert\"\n aria-live=\"polite\"\n className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {error}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n {isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {isEdit\n ? t('adaptercloud.common.saveChanges', 'Save changes')\n : t('adaptercloud.policies.create', 'Create policy')}\n </button>\n </div>\n </GlassCard>\n </form>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/adaptercloud/pages/resources/ResourceDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"ResourceDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/adaptercloud/pages/resources/ResourceDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyBH,wBAAgB,kBAAkB,gCAgVjC"}
|