@burdenoff/microfe-assethandler 2026.826.13 → 2026.826.14

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.
@@ -1 +1 @@
1
- {"version":3,"file":"CmdbBrowserPage.js","names":[],"sources":["../../../src/assethandler/pages/CmdbBrowserPage.tsx"],"sourcesContent":["import {\n Network,\n Plus,\n Search,\n Server,\n Database,\n AppWindow,\n HardDrive,\n Cog,\n ChevronDown,\n ChevronRight,\n Pencil,\n X,\n Trash2,\n} from 'lucide-react';\nimport type { ComponentType, ReactNode } from 'react';\nimport { useMemo, useState, useSyncExternalStore } from 'react';\nimport {\n Button,\n GradientText,\n IllustratedEmptyState,\n GlassCard,\n PagePurpose,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n StatTile,\n ChartCard,\n DonutChart,\n BarsChart,\n seriesColor,\n type DonutSlice,\n} from '../components';\nimport { cn } from '../utils/cn';\nimport { notify } from '../utils/toast';\n\nconst CONFIGURATION_ITEMS_QUERY = gql`\n query AssetHandlerConfigurationItems {\n configurationItems {\n id\n ciNumber\n name\n ciType\n status\n environment\n owner\n attributes\n updatedAt\n }\n }\n`;\n\nconst UPSERT_CONFIGURATION_ITEM_MUTATION = gql`\n mutation AssetHandlerUpsertConfigurationItem($input: UpsertConfigurationItemInput!) {\n upsertConfigurationItem(input: $input) {\n id\n ciNumber\n }\n }\n`;\n\ninterface ConfigurationItem {\n id: string;\n ciNumber: string;\n name: string;\n ciType: string;\n status: string;\n environment: string | null;\n owner: string | null;\n attributes: Record<string, unknown> | null;\n updatedAt: string;\n}\n\ninterface UpsertConfigurationItemResult {\n upsertConfigurationItem: { id: string; ciNumber: string };\n}\n\nconst CI_TYPES = ['SERVER', 'SERVICE', 'DATABASE', 'NETWORK', 'APPLICATION', 'STORAGE'] as const;\nconst CI_STATUSES = ['ACTIVE', 'INACTIVE', 'MAINTENANCE', 'RETIRED'] as const;\nconst CI_ENVIRONMENTS = ['PRODUCTION', 'STAGING', 'DEVELOPMENT', 'DR'] as const;\n\nconst TYPE_ICON: Record<string, ComponentType<{ className?: string }>> = {\n SERVER: Server,\n SERVICE: Cog,\n DATABASE: Database,\n NETWORK: Network,\n APPLICATION: AppWindow,\n STORAGE: HardDrive,\n};\n\nconst STATUS_META: Record<string, { label: string; className: string }> = {\n ACTIVE: {\n label: 'Operational',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n },\n MAINTENANCE: {\n label: 'Maintenance',\n className: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n INACTIVE: { label: 'Inactive', className: 'bg-bg-sunken text-text-secondary' },\n RETIRED: { label: 'Retired', className: 'bg-status-error-bg-subtle text-status-error-text' },\n};\n\nfunction StatusPill({ status }: { status: string }) {\n const meta = STATUS_META[status] ?? {\n label: status,\n className: 'bg-bg-sunken text-text-secondary',\n };\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n meta.className\n )}\n >\n {meta.label}\n </span>\n );\n}\n\n/** Track a min-width breakpoint so the CI detail can render as a rail (desktop) or sheet (mobile). */\nfunction useIsLargeScreen(): boolean {\n return useSyncExternalStore(\n (onChange) => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return () => {};\n const mq = window.matchMedia('(min-width: 1024px)');\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n },\n () => (typeof window !== 'undefined' ? window.matchMedia('(min-width: 1024px)').matches : true),\n () => true\n );\n}\n\ntype AttrRow = { key: string; value: string };\n\nfunction toAttrRows(attributes: Record<string, unknown> | null | undefined): AttrRow[] {\n if (!attributes) return [];\n return Object.entries(attributes).map(([key, value]) => ({\n key,\n value: typeof value === 'string' ? value : JSON.stringify(value),\n }));\n}\n\nfunction UpsertConfigurationItemDialog({\n open,\n onOpenChange,\n seed,\n onSaved,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n seed: ConfigurationItem | null;\n onSaved: () => void;\n}) {\n const editing = Boolean(seed);\n const [ciNumber, setCiNumber] = useState('');\n const [name, setName] = useState('');\n const [ciType, setCiType] = useState<string>('SERVER');\n const [status, setStatus] = useState<string>('ACTIVE');\n const [environment, setEnvironment] = useState<string>('PRODUCTION');\n const [owner, setOwner] = useState('');\n const [attrs, setAttrs] = useState<AttrRow[]>([]);\n // Re-seed the form each time the dialog opens against a (possibly new) CI.\n const [seededId, setSeededId] = useState<string | null>(null);\n if (open && seededId !== (seed?.id ?? '__new__')) {\n setSeededId(seed?.id ?? '__new__');\n setCiNumber(seed?.ciNumber ?? '');\n setName(seed?.name ?? '');\n setCiType(seed?.ciType ?? 'SERVER');\n setStatus(seed?.status ?? 'ACTIVE');\n setEnvironment(seed?.environment ?? 'PRODUCTION');\n setOwner(seed?.owner ?? '');\n setAttrs(toAttrRows(seed?.attributes));\n }\n if (!open && seededId !== null) setSeededId(null);\n\n const [upsertItem, { loading }] = useMutation<UpsertConfigurationItemResult>(\n UPSERT_CONFIGURATION_ITEM_MUTATION,\n {\n onCompleted: (data) => {\n notify.success(\n editing ? 'Configuration item updated' : 'Configuration item saved',\n `${data.upsertConfigurationItem.ciNumber} is now in the CMDB.`\n );\n onOpenChange(false);\n onSaved();\n },\n onError: (error) => notify.error('Could not save configuration item', error.message),\n }\n );\n\n const handleSubmit = () => {\n if (!ciNumber.trim() || !name.trim()) {\n notify.warning('Missing details', 'CI number and name are required.');\n return;\n }\n const attributes = attrs.reduce<Record<string, string>>((acc, row) => {\n if (row.key.trim()) acc[row.key.trim()] = row.value;\n return acc;\n }, {});\n void upsertItem({\n variables: {\n input: {\n ...(seed ? { id: seed.id } : {}),\n ciNumber: ciNumber.trim(),\n name: name.trim(),\n ciType,\n status,\n environment,\n owner: owner.trim() || null,\n attributes,\n },\n },\n });\n };\n\n const updateAttr = (index: number, patch: Partial<AttrRow>) =>\n setAttrs((rows) => rows.map((r, i) => (i === index ? { ...r, ...patch } : r)));\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{editing ? 'Edit CI' : 'Add CI'}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a configuration item in the CMDB and map it to your estate.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>CI Number *</Label>\n <Input\n placeholder=\"e.g., CI-1042\"\n value={ciNumber}\n onChange={(e) => setCiNumber(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Name *</Label>\n <Input value={name} onChange={(e) => setName(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Type</Label>\n <Select value={ciType} onValueChange={setCiType}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CI_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {t}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={setStatus}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CI_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Environment</Label>\n <Select value={environment} onValueChange={setEnvironment}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CI_ENVIRONMENTS.map((e) => (\n <SelectItem key={e} value={e}>\n {e}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Owner</Label>\n <Input\n placeholder=\"e.g., Platform team\"\n value={owner}\n onChange={(e) => setOwner(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <Label>Attributes</Label>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setAttrs((rows) => [...rows, { key: '', value: '' }])}\n >\n <Plus className=\"mr-1 h-3.5 w-3.5\" />\n Add\n </Button>\n </div>\n {attrs.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n Add structured key/value attributes (e.g. hostname, region, tier).\n </p>\n ) : (\n <div className=\"space-y-2\">\n {attrs.map((row, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <Input\n placeholder=\"key\"\n value={row.key}\n onChange={(e) => updateAttr(i, { key: e.target.value })}\n className=\"flex-1\"\n />\n <Input\n placeholder=\"value\"\n value={row.value}\n onChange={(e) => updateAttr(i, { value: e.target.value })}\n className=\"flex-1\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setAttrs((rows) => rows.filter((_, idx) => idx !== i))}\n aria-label=\"Remove attribute\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? 'Saving…' : editing ? 'Save changes' : 'Add CI'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nfunction DetailRow({ label, value }: { label: string; value: ReactNode }) {\n return (\n <div className=\"flex items-start justify-between gap-3 py-2\">\n <span className=\"text-sm text-text-secondary\">{label}</span>\n <span className=\"min-w-0 text-right text-sm font-medium text-text-primary\">{value}</span>\n </div>\n );\n}\n\nfunction CiDetail({\n ci,\n onEdit,\n onClose,\n}: {\n ci: ConfigurationItem;\n onEdit: () => void;\n onClose?: () => void;\n}) {\n const attrRows = toAttrRows(ci.attributes);\n const TypeIcon = TYPE_ICON[ci.ciType] ?? Cog;\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <TypeIcon className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-base font-semibold text-text-primary\">{ci.name}</p>\n <p className=\"font-mono text-xs text-text-secondary\">{ci.ciNumber}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n <Button variant=\"outline\" size=\"sm\" onClick={onEdit}>\n <Pencil className=\"mr-1 h-3.5 w-3.5\" />\n Edit\n </Button>\n {onClose ? (\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label=\"Close detail\">\n <X className=\"h-4 w-4\" />\n </Button>\n ) : null}\n </div>\n </div>\n\n <div className=\"divide-y divide-border-seam/60\">\n <DetailRow label=\"Type\" value={ci.ciType} />\n <DetailRow label=\"Environment\" value={ci.environment ?? '—'} />\n <DetailRow label=\"Owner\" value={ci.owner ?? '—'} />\n <DetailRow label=\"Status\" value={<StatusPill status={ci.status} />} />\n <DetailRow\n label=\"Updated\"\n value={new Date(ci.updatedAt).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n />\n </div>\n\n <div>\n <h4 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Attributes\n </h4>\n {attrRows.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No attributes recorded for this CI.</p>\n ) : (\n <div className=\"divide-y divide-border-seam/60 rounded-lg bg-bg-sunken/40\">\n {attrRows.map((row) => (\n <div key={row.key} className=\"flex items-start justify-between gap-3 px-3 py-2\">\n <span className=\"text-sm text-text-secondary\">{row.key}</span>\n <span className=\"min-w-0 break-words text-right text-sm font-medium text-text-primary\">\n {row.value}\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nexport function CmdbBrowserPage() {\n const [dialogOpen, setDialogOpen] = useState(false);\n const [editSeed, setEditSeed] = useState<ConfigurationItem | null>(null);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [mobileDetailOpen, setMobileDetailOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<string>('ALL');\n const [envFilter, setEnvFilter] = useState<string>('ALL');\n const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});\n const isLarge = useIsLargeScreen();\n\n const { data, loading, refetch } = useQuery<{ configurationItems: ConfigurationItem[] }>(\n CONFIGURATION_ITEMS_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n\n const cis = useMemo(() => data?.configurationItems ?? [], [data?.configurationItems]);\n\n const filtered = useMemo(() => {\n const term = search.trim().toLowerCase();\n return cis.filter((ci) => {\n if (statusFilter !== 'ALL' && ci.status !== statusFilter) return false;\n if (envFilter !== 'ALL' && ci.environment !== envFilter) return false;\n if (!term) return true;\n return [ci.ciNumber, ci.name, ci.ciType, ci.owner ?? '']\n .join(' ')\n .toLowerCase()\n .includes(term);\n });\n }, [cis, search, statusFilter, envFilter]);\n\n const grouped = useMemo(() => {\n const map = new Map<string, ConfigurationItem[]>();\n for (const ci of filtered) {\n const key = ci.ciType || 'OTHER';\n const bucket = map.get(key);\n if (bucket) bucket.push(ci);\n else map.set(key, [ci]);\n }\n return [...map.entries()].sort((a, b) => a[0].localeCompare(b[0]));\n }, [filtered]);\n\n const stats = useMemo(() => {\n const total = cis.length;\n const operational = cis.filter((c) => c.status === 'ACTIVE').length;\n const maintenance = cis.filter((c) => c.status === 'MAINTENANCE').length;\n const retired = cis.filter((c) => c.status === 'RETIRED').length;\n return {\n total,\n operationalPct: total > 0 ? Math.round((operational / total) * 100) : 0,\n maintenance,\n retired,\n };\n }, [cis]);\n\n const statusMix = useMemo<DonutSlice[]>(() => {\n const counts = cis.reduce<Record<string, number>>((acc, c) => {\n acc[c.status] = (acc[c.status] ?? 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts).map(([status, value], i) => ({\n name: STATUS_META[status]?.label ?? status,\n value,\n color: seriesColor(i),\n }));\n }, [cis]);\n\n const typeBars = useMemo(() => {\n const counts = cis.reduce<Record<string, number>>((acc, c) => {\n acc[c.ciType] = (acc[c.ciType] ?? 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts)\n .sort((a, b) => b[1] - a[1])\n .map(([type, count]) => ({ name: type, count }));\n }, [cis]);\n\n const selected = useMemo(() => cis.find((c) => c.id === selectedId) ?? null, [cis, selectedId]);\n\n const handleSelect = (ci: ConfigurationItem) => {\n setSelectedId(ci.id);\n if (!isLarge) setMobileDetailOpen(true);\n };\n\n const openCreate = () => {\n setEditSeed(null);\n setDialogOpen(true);\n };\n\n const openEdit = (ci: ConfigurationItem) => {\n setEditSeed(ci);\n setDialogOpen(true);\n setMobileDetailOpen(false);\n };\n\n return (\n <div className=\"space-y-5 p-4 sm:p-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <Network className=\"mt-1 h-6 w-6 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0\">\n <GradientText as=\"h1\" className=\"text-2xl font-bold sm:text-3xl\">\n CMDB — Configuration Items\n </GradientText>\n <p className=\"mt-1 max-w-2xl text-sm text-text-secondary\">\n A living map of every configuration item and its attributes.\n </p>\n </div>\n </div>\n <Button className=\"w-full sm:w-auto\" onClick={openCreate}>\n <Plus className=\"mr-2 h-4 w-4\" />\n Add CI\n </Button>\n </div>\n <PagePurpose>\n Your configuration management database (CMDB): servers, services, network gear and how they\n relate. Browse, search and inspect each configuration item to understand blast radius before\n a change and keep asset relationships accurate.\n </PagePurpose>\n\n <UpsertConfigurationItemDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n seed={editSeed}\n onSaved={() => void refetch()}\n />\n\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile label=\"Total CIs\" value={stats.total} icon={Network} />\n <StatTile\n label=\"Operational\"\n value={stats.operationalPct}\n format={(n) => `${n}%`}\n icon={Server}\n deltaTone=\"positive\"\n />\n <StatTile label=\"In Maintenance\" value={stats.maintenance} icon={Cog} deltaTone=\"neutral\" />\n <StatTile label=\"Retired\" value={stats.retired} icon={HardDrive} deltaTone=\"neutral\" />\n </div>\n\n {cis.length > 0 ? (\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <ChartCard title=\"Status mix\" subtitle=\"Configuration items by status\">\n <DonutChart\n data={statusMix}\n height={220}\n centerLabel=\"CIs\"\n centerValue={stats.total}\n ariaLabel=\"Configuration items by status\"\n />\n </ChartCard>\n <ChartCard title=\"By type\" subtitle=\"Configuration items per type\">\n <BarsChart\n data={typeBars}\n xKey=\"name\"\n series={[{ key: 'count', label: 'Items' }]}\n orientation=\"horizontal\"\n height={220}\n categoryWidth={110}\n ariaLabel=\"Configuration items per type\"\n />\n </ChartCard>\n </div>\n ) : null}\n\n <div className=\"grid gap-4 lg:grid-cols-[1fr_360px]\">\n {/* Navigator */}\n <GlassCard treatment=\"glass\" className=\"border-border-seam\">\n <div className=\"space-y-3 p-4\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n className=\"pl-9\"\n placeholder=\"Search CIs by number, name, type or owner…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n aria-label=\"Search configuration items\"\n />\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger>\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All statuses</SelectItem>\n {CI_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={envFilter} onValueChange={setEnvFilter}>\n <SelectTrigger>\n <SelectValue placeholder=\"Environment\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All environments</SelectItem>\n {CI_ENVIRONMENTS.map((e) => (\n <SelectItem key={e} value={e}>\n {e}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"border-t border-border-seam\">\n {loading && cis.length === 0 ? (\n <div className=\"space-y-2 p-4\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={i}\n className=\"h-9 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : null}\n {!loading && filtered.length === 0 ? (\n <IllustratedEmptyState\n illustration={\n search || statusFilter !== 'ALL' || envFilter !== 'ALL'\n ? 'empty-search'\n : 'empty-data'\n }\n title={\n search || statusFilter !== 'ALL' || envFilter !== 'ALL'\n ? 'No configuration items match'\n : 'No configuration items found'\n }\n description={\n search || statusFilter !== 'ALL' || envFilter !== 'ALL'\n ? 'Try a different search or filter.'\n : 'Add a configuration item to start mapping your estate.'\n }\n />\n ) : null}\n <div className=\"max-h-[540px] overflow-y-auto\">\n {grouped.map(([type, items]) => {\n const TypeIcon = TYPE_ICON[type] ?? Cog;\n const isCollapsed = collapsed[type];\n return (\n <div key={type}>\n <button\n type=\"button\"\n onClick={() => setCollapsed((c) => ({ ...c, [type]: !c[type] }))}\n className=\"flex w-full items-center gap-2 border-b border-border-seam/60 bg-bg-sunken/30 px-4 py-2 text-left text-sm font-semibold text-text-primary\"\n >\n {isCollapsed ? (\n <ChevronRight className=\"h-4 w-4 text-text-secondary\" />\n ) : (\n <ChevronDown className=\"h-4 w-4 text-text-secondary\" />\n )}\n <TypeIcon className=\"h-4 w-4 text-primary\" />\n <span className=\"capitalize\">{type.toLowerCase()}</span>\n <span className=\"ml-auto rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary tabular-nums\">\n {items.length}\n </span>\n </button>\n {!isCollapsed\n ? items.map((ci) => (\n <button\n key={ci.id}\n type=\"button\"\n onClick={() => handleSelect(ci)}\n aria-current={selectedId === ci.id}\n className={cn(\n 'flex w-full items-center gap-3 border-b border-border-seam/40 px-4 py-2.5 pl-10 text-left last:border-b-0',\n selectedId === ci.id\n ? 'bg-action-primary-bg/10'\n : 'hover:bg-bg-sunken/40'\n )}\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {ci.name}\n </p>\n <p className=\"truncate font-mono text-xs text-text-secondary\">\n {ci.ciNumber}\n {ci.environment ? ` · ${ci.environment}` : ''}\n </p>\n </div>\n <StatusPill status={ci.status} />\n </button>\n ))\n : null}\n </div>\n );\n })}\n </div>\n </div>\n </GlassCard>\n\n {/* Detail rail (desktop) */}\n <div className=\"hidden lg:block\">\n <GlassCard treatment=\"glass\" className=\"sticky top-4 border-border-seam\">\n <div className=\"p-4\">\n {selected ? (\n <CiDetail ci={selected} onEdit={() => openEdit(selected)} />\n ) : (\n <div className=\"flex flex-col items-center justify-center gap-2 py-12 text-center\">\n <Network className=\"h-8 w-8 text-text-muted\" />\n <p className=\"text-sm font-medium text-text-primary\">\n Select a configuration item\n </p>\n <p className=\"text-xs text-text-secondary\">\n Choose a CI from the navigator to inspect its attributes.\n </p>\n </div>\n )}\n </div>\n </GlassCard>\n </div>\n </div>\n\n {/* Detail sheet (mobile) */}\n <ResponsiveDialog open={mobileDetailOpen && !isLarge} onOpenChange={setMobileDetailOpen}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Configuration item</ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n {selected ? <CiDetail ci={selected} onEdit={() => openEdit(selected)} /> : null}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CmdbBrowserPage.js","names":[],"sources":["../../../src/assethandler/pages/CmdbBrowserPage.tsx"],"sourcesContent":["import {\n Network,\n Plus,\n Search,\n Server,\n Database,\n AppWindow,\n HardDrive,\n Cog,\n ChevronDown,\n ChevronRight,\n Pencil,\n X,\n Trash2,\n} from 'lucide-react';\nimport type { ComponentType, ReactNode } from 'react';\nimport { useMemo, useState, useSyncExternalStore } from 'react';\nimport {\n Button,\n GradientText,\n IllustratedEmptyState,\n GlassCard,\n PagePurpose,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n StatTile,\n ChartCard,\n DonutChart,\n BarsChart,\n seriesColor,\n type DonutSlice,\n} from '../components';\nimport { cn } from '../utils/cn';\nimport { notify } from '../utils/toast';\n\nconst CONFIGURATION_ITEMS_QUERY = gql`\n query AssetHandlerConfigurationItems {\n configurationItems {\n id\n ciNumber\n name\n ciType\n status\n environment\n owner\n attributes\n updatedAt\n }\n }\n`;\n\nconst UPSERT_CONFIGURATION_ITEM_MUTATION = gql`\n mutation AssetHandlerUpsertConfigurationItem($input: UpsertConfigurationItemInput!) {\n upsertConfigurationItem(input: $input) {\n id\n ciNumber\n }\n }\n`;\n\nconst DELETE_CONFIGURATION_ITEM_MUTATION = gql`\n mutation AssetHandlerDeleteConfigurationItem($id: ID!) {\n deleteConfigurationItem(id: $id)\n }\n`;\n\ninterface ConfigurationItem {\n id: string;\n ciNumber: string;\n name: string;\n ciType: string;\n status: string;\n environment: string | null;\n owner: string | null;\n attributes: Record<string, unknown> | null;\n updatedAt: string;\n}\n\ninterface UpsertConfigurationItemResult {\n upsertConfigurationItem: { id: string; ciNumber: string };\n}\n\nconst CI_TYPES = ['SERVER', 'SERVICE', 'DATABASE', 'NETWORK', 'APPLICATION', 'STORAGE'] as const;\nconst CI_STATUSES = ['ACTIVE', 'INACTIVE', 'MAINTENANCE', 'RETIRED'] as const;\nconst CI_ENVIRONMENTS = ['PRODUCTION', 'STAGING', 'DEVELOPMENT', 'DR'] as const;\n\nconst TYPE_ICON: Record<string, ComponentType<{ className?: string }>> = {\n SERVER: Server,\n SERVICE: Cog,\n DATABASE: Database,\n NETWORK: Network,\n APPLICATION: AppWindow,\n STORAGE: HardDrive,\n};\n\nconst STATUS_META: Record<string, { label: string; className: string }> = {\n ACTIVE: {\n label: 'Operational',\n className: 'bg-status-success-bg-subtle text-status-success-text',\n },\n MAINTENANCE: {\n label: 'Maintenance',\n className: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n INACTIVE: { label: 'Inactive', className: 'bg-bg-sunken text-text-secondary' },\n RETIRED: { label: 'Retired', className: 'bg-status-error-bg-subtle text-status-error-text' },\n};\n\nfunction StatusPill({ status }: { status: string }) {\n const meta = STATUS_META[status] ?? {\n label: status,\n className: 'bg-bg-sunken text-text-secondary',\n };\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n meta.className\n )}\n >\n {meta.label}\n </span>\n );\n}\n\n/** Track a min-width breakpoint so the CI detail can render as a rail (desktop) or sheet (mobile). */\nfunction useIsLargeScreen(): boolean {\n return useSyncExternalStore(\n (onChange) => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return () => {};\n const mq = window.matchMedia('(min-width: 1024px)');\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n },\n () => (typeof window !== 'undefined' ? window.matchMedia('(min-width: 1024px)').matches : true),\n () => true\n );\n}\n\ntype AttrRow = { key: string; value: string };\n\nfunction toAttrRows(attributes: Record<string, unknown> | null | undefined): AttrRow[] {\n if (!attributes) return [];\n return Object.entries(attributes).map(([key, value]) => ({\n key,\n value: typeof value === 'string' ? value : JSON.stringify(value),\n }));\n}\n\nfunction UpsertConfigurationItemDialog({\n open,\n onOpenChange,\n seed,\n onSaved,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n seed: ConfigurationItem | null;\n onSaved: () => void;\n}) {\n const editing = Boolean(seed);\n const [ciNumber, setCiNumber] = useState('');\n const [name, setName] = useState('');\n const [ciType, setCiType] = useState<string>('SERVER');\n const [status, setStatus] = useState<string>('ACTIVE');\n const [environment, setEnvironment] = useState<string>('PRODUCTION');\n const [owner, setOwner] = useState('');\n const [attrs, setAttrs] = useState<AttrRow[]>([]);\n // Re-seed the form each time the dialog opens against a (possibly new) CI.\n const [seededId, setSeededId] = useState<string | null>(null);\n if (open && seededId !== (seed?.id ?? '__new__')) {\n setSeededId(seed?.id ?? '__new__');\n setCiNumber(seed?.ciNumber ?? '');\n setName(seed?.name ?? '');\n setCiType(seed?.ciType ?? 'SERVER');\n setStatus(seed?.status ?? 'ACTIVE');\n setEnvironment(seed?.environment ?? 'PRODUCTION');\n setOwner(seed?.owner ?? '');\n setAttrs(toAttrRows(seed?.attributes));\n }\n if (!open && seededId !== null) setSeededId(null);\n\n const [upsertItem, { loading }] = useMutation<UpsertConfigurationItemResult>(\n UPSERT_CONFIGURATION_ITEM_MUTATION,\n {\n onCompleted: (data) => {\n notify.success(\n editing ? 'Configuration item updated' : 'Configuration item saved',\n `${data.upsertConfigurationItem.ciNumber} is now in the CMDB.`\n );\n onOpenChange(false);\n onSaved();\n },\n onError: (error) => notify.error('Could not save configuration item', error.message),\n }\n );\n\n const handleSubmit = () => {\n if (!ciNumber.trim() || !name.trim()) {\n notify.warning('Missing details', 'CI number and name are required.');\n return;\n }\n const attributes = attrs.reduce<Record<string, string>>((acc, row) => {\n if (row.key.trim()) acc[row.key.trim()] = row.value;\n return acc;\n }, {});\n void upsertItem({\n variables: {\n input: {\n ...(seed ? { id: seed.id } : {}),\n ciNumber: ciNumber.trim(),\n name: name.trim(),\n ciType,\n status,\n environment,\n owner: owner.trim() || null,\n attributes,\n },\n },\n });\n };\n\n const updateAttr = (index: number, patch: Partial<AttrRow>) =>\n setAttrs((rows) => rows.map((r, i) => (i === index ? { ...r, ...patch } : r)));\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{editing ? 'Edit CI' : 'Add CI'}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a configuration item in the CMDB and map it to your estate.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>CI Number *</Label>\n <Input\n placeholder=\"e.g., CI-1042\"\n value={ciNumber}\n onChange={(e) => setCiNumber(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Name *</Label>\n <Input value={name} onChange={(e) => setName(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Type</Label>\n <Select value={ciType} onValueChange={setCiType}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CI_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {t}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={setStatus}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CI_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Environment</Label>\n <Select value={environment} onValueChange={setEnvironment}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CI_ENVIRONMENTS.map((e) => (\n <SelectItem key={e} value={e}>\n {e}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Owner</Label>\n <Input\n placeholder=\"e.g., Platform team\"\n value={owner}\n onChange={(e) => setOwner(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <Label>Attributes</Label>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setAttrs((rows) => [...rows, { key: '', value: '' }])}\n >\n <Plus className=\"mr-1 h-3.5 w-3.5\" />\n Add\n </Button>\n </div>\n {attrs.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n Add structured key/value attributes (e.g. hostname, region, tier).\n </p>\n ) : (\n <div className=\"space-y-2\">\n {attrs.map((row, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <Input\n placeholder=\"key\"\n value={row.key}\n onChange={(e) => updateAttr(i, { key: e.target.value })}\n className=\"flex-1\"\n />\n <Input\n placeholder=\"value\"\n value={row.value}\n onChange={(e) => updateAttr(i, { value: e.target.value })}\n className=\"flex-1\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setAttrs((rows) => rows.filter((_, idx) => idx !== i))}\n aria-label=\"Remove attribute\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? 'Saving…' : editing ? 'Save changes' : 'Add CI'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nfunction DetailRow({ label, value }: { label: string; value: ReactNode }) {\n return (\n <div className=\"flex items-start justify-between gap-3 py-2\">\n <span className=\"text-sm text-text-secondary\">{label}</span>\n <span className=\"min-w-0 text-right text-sm font-medium text-text-primary\">{value}</span>\n </div>\n );\n}\n\nfunction CiDetail({\n ci,\n onEdit,\n onDelete,\n deleting,\n onClose,\n}: {\n ci: ConfigurationItem;\n onEdit: () => void;\n onDelete: () => void;\n deleting: boolean;\n onClose?: () => void;\n}) {\n const attrRows = toAttrRows(ci.attributes);\n const TypeIcon = TYPE_ICON[ci.ciType] ?? Cog;\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <TypeIcon className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-base font-semibold text-text-primary\">{ci.name}</p>\n <p className=\"font-mono text-xs text-text-secondary\">{ci.ciNumber}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n <Button variant=\"outline\" size=\"sm\" onClick={onEdit}>\n <Pencil className=\"mr-1 h-3.5 w-3.5\" />\n Edit\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={onDelete} disabled={deleting}>\n <Trash2 className=\"mr-1 h-3.5 w-3.5\" />\n {deleting ? 'Deleting…' : 'Delete'}\n </Button>\n {onClose ? (\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label=\"Close detail\">\n <X className=\"h-4 w-4\" />\n </Button>\n ) : null}\n </div>\n </div>\n\n <div className=\"divide-y divide-border-seam/60\">\n <DetailRow label=\"Type\" value={ci.ciType} />\n <DetailRow label=\"Environment\" value={ci.environment ?? '—'} />\n <DetailRow label=\"Owner\" value={ci.owner ?? '—'} />\n <DetailRow label=\"Status\" value={<StatusPill status={ci.status} />} />\n <DetailRow\n label=\"Updated\"\n value={new Date(ci.updatedAt).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n />\n </div>\n\n <div>\n <h4 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Attributes\n </h4>\n {attrRows.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No attributes recorded for this CI.</p>\n ) : (\n <div className=\"divide-y divide-border-seam/60 rounded-lg bg-bg-sunken/40\">\n {attrRows.map((row) => (\n <div key={row.key} className=\"flex items-start justify-between gap-3 px-3 py-2\">\n <span className=\"text-sm text-text-secondary\">{row.key}</span>\n <span className=\"min-w-0 break-words text-right text-sm font-medium text-text-primary\">\n {row.value}\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n\nexport function CmdbBrowserPage() {\n const [dialogOpen, setDialogOpen] = useState(false);\n const [editSeed, setEditSeed] = useState<ConfigurationItem | null>(null);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [mobileDetailOpen, setMobileDetailOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<string>('ALL');\n const [envFilter, setEnvFilter] = useState<string>('ALL');\n const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});\n const isLarge = useIsLargeScreen();\n\n const { data, loading, refetch } = useQuery<{ configurationItems: ConfigurationItem[] }>(\n CONFIGURATION_ITEMS_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n const [deleteItem, { loading: deleting }] = useMutation(DELETE_CONFIGURATION_ITEM_MUTATION, {\n onCompleted: () => {\n notify.success('Configuration item deleted');\n setSelectedId(null);\n setMobileDetailOpen(false);\n void refetch();\n },\n onError: (error) => notify.error('Could not delete configuration item', error.message),\n });\n\n const cis = useMemo(() => data?.configurationItems ?? [], [data?.configurationItems]);\n\n const filtered = useMemo(() => {\n const term = search.trim().toLowerCase();\n return cis.filter((ci) => {\n if (statusFilter !== 'ALL' && ci.status !== statusFilter) return false;\n if (envFilter !== 'ALL' && ci.environment !== envFilter) return false;\n if (!term) return true;\n return [ci.ciNumber, ci.name, ci.ciType, ci.owner ?? '']\n .join(' ')\n .toLowerCase()\n .includes(term);\n });\n }, [cis, search, statusFilter, envFilter]);\n\n const grouped = useMemo(() => {\n const map = new Map<string, ConfigurationItem[]>();\n for (const ci of filtered) {\n const key = ci.ciType || 'OTHER';\n const bucket = map.get(key);\n if (bucket) bucket.push(ci);\n else map.set(key, [ci]);\n }\n return [...map.entries()].sort((a, b) => a[0].localeCompare(b[0]));\n }, [filtered]);\n\n const stats = useMemo(() => {\n const total = cis.length;\n const operational = cis.filter((c) => c.status === 'ACTIVE').length;\n const maintenance = cis.filter((c) => c.status === 'MAINTENANCE').length;\n const retired = cis.filter((c) => c.status === 'RETIRED').length;\n return {\n total,\n operationalPct: total > 0 ? Math.round((operational / total) * 100) : 0,\n maintenance,\n retired,\n };\n }, [cis]);\n\n const statusMix = useMemo<DonutSlice[]>(() => {\n const counts = cis.reduce<Record<string, number>>((acc, c) => {\n acc[c.status] = (acc[c.status] ?? 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts).map(([status, value], i) => ({\n name: STATUS_META[status]?.label ?? status,\n value,\n color: seriesColor(i),\n }));\n }, [cis]);\n\n const typeBars = useMemo(() => {\n const counts = cis.reduce<Record<string, number>>((acc, c) => {\n acc[c.ciType] = (acc[c.ciType] ?? 0) + 1;\n return acc;\n }, {});\n return Object.entries(counts)\n .sort((a, b) => b[1] - a[1])\n .map(([type, count]) => ({ name: type, count }));\n }, [cis]);\n\n const selected = useMemo(() => cis.find((c) => c.id === selectedId) ?? null, [cis, selectedId]);\n\n const handleSelect = (ci: ConfigurationItem) => {\n setSelectedId(ci.id);\n if (!isLarge) setMobileDetailOpen(true);\n };\n\n const openCreate = () => {\n setEditSeed(null);\n setDialogOpen(true);\n };\n\n const openEdit = (ci: ConfigurationItem) => {\n setEditSeed(ci);\n setDialogOpen(true);\n setMobileDetailOpen(false);\n };\n\n const handleDelete = (ci: ConfigurationItem) => {\n if (!window.confirm(`Delete ${ci.ciNumber} — ${ci.name}?`)) return;\n void deleteItem({ variables: { id: ci.id } });\n };\n\n return (\n <div className=\"space-y-5 p-4 sm:p-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <Network className=\"mt-1 h-6 w-6 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0\">\n <GradientText as=\"h1\" className=\"text-2xl font-bold sm:text-3xl\">\n CMDB — Configuration Items\n </GradientText>\n <p className=\"mt-1 max-w-2xl text-sm text-text-secondary\">\n A searchable catalog of configuration items and their attributes.\n </p>\n </div>\n </div>\n <Button className=\"w-full sm:w-auto\" onClick={openCreate}>\n <Plus className=\"mr-2 h-4 w-4\" />\n Add CI\n </Button>\n </div>\n <PagePurpose>\n Your configuration-item catalog: record servers, services, network gear, status, ownership,\n environment and structured attributes. Dependency relationships and blast-radius analysis\n are not shipped yet.\n </PagePurpose>\n\n <UpsertConfigurationItemDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n seed={editSeed}\n onSaved={() => void refetch()}\n />\n\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile label=\"Total CIs\" value={stats.total} icon={Network} />\n <StatTile\n label=\"Operational\"\n value={stats.operationalPct}\n format={(n) => `${n}%`}\n icon={Server}\n deltaTone=\"positive\"\n />\n <StatTile label=\"In Maintenance\" value={stats.maintenance} icon={Cog} deltaTone=\"neutral\" />\n <StatTile label=\"Retired\" value={stats.retired} icon={HardDrive} deltaTone=\"neutral\" />\n </div>\n\n {cis.length > 0 ? (\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <ChartCard title=\"Status mix\" subtitle=\"Configuration items by status\">\n <DonutChart\n data={statusMix}\n height={220}\n centerLabel=\"CIs\"\n centerValue={stats.total}\n ariaLabel=\"Configuration items by status\"\n />\n </ChartCard>\n <ChartCard title=\"By type\" subtitle=\"Configuration items per type\">\n <BarsChart\n data={typeBars}\n xKey=\"name\"\n series={[{ key: 'count', label: 'Items' }]}\n orientation=\"horizontal\"\n height={220}\n categoryWidth={110}\n ariaLabel=\"Configuration items per type\"\n />\n </ChartCard>\n </div>\n ) : null}\n\n <div className=\"grid gap-4 lg:grid-cols-[1fr_360px]\">\n {/* Navigator */}\n <GlassCard treatment=\"glass\" className=\"border-border-seam\">\n <div className=\"space-y-3 p-4\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n className=\"pl-9\"\n placeholder=\"Search CIs by number, name, type or owner…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n aria-label=\"Search configuration items\"\n />\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger>\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All statuses</SelectItem>\n {CI_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {s}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={envFilter} onValueChange={setEnvFilter}>\n <SelectTrigger>\n <SelectValue placeholder=\"Environment\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All environments</SelectItem>\n {CI_ENVIRONMENTS.map((e) => (\n <SelectItem key={e} value={e}>\n {e}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"border-t border-border-seam\">\n {loading && cis.length === 0 ? (\n <div className=\"space-y-2 p-4\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={i}\n className=\"h-9 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : null}\n {!loading && filtered.length === 0 ? (\n <IllustratedEmptyState\n illustration={\n search || statusFilter !== 'ALL' || envFilter !== 'ALL'\n ? 'empty-search'\n : 'empty-data'\n }\n title={\n search || statusFilter !== 'ALL' || envFilter !== 'ALL'\n ? 'No configuration items match'\n : 'No configuration items found'\n }\n description={\n search || statusFilter !== 'ALL' || envFilter !== 'ALL'\n ? 'Try a different search or filter.'\n : 'Add a configuration item to start mapping your estate.'\n }\n />\n ) : null}\n <div className=\"max-h-[540px] overflow-y-auto\">\n {grouped.map(([type, items]) => {\n const TypeIcon = TYPE_ICON[type] ?? Cog;\n const isCollapsed = collapsed[type];\n return (\n <div key={type}>\n <button\n type=\"button\"\n onClick={() => setCollapsed((c) => ({ ...c, [type]: !c[type] }))}\n className=\"flex w-full items-center gap-2 border-b border-border-seam/60 bg-bg-sunken/30 px-4 py-2 text-left text-sm font-semibold text-text-primary\"\n >\n {isCollapsed ? (\n <ChevronRight className=\"h-4 w-4 text-text-secondary\" />\n ) : (\n <ChevronDown className=\"h-4 w-4 text-text-secondary\" />\n )}\n <TypeIcon className=\"h-4 w-4 text-primary\" />\n <span className=\"capitalize\">{type.toLowerCase()}</span>\n <span className=\"ml-auto rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary tabular-nums\">\n {items.length}\n </span>\n </button>\n {!isCollapsed\n ? items.map((ci) => (\n <button\n key={ci.id}\n type=\"button\"\n onClick={() => handleSelect(ci)}\n aria-current={selectedId === ci.id}\n className={cn(\n 'flex w-full items-center gap-3 border-b border-border-seam/40 px-4 py-2.5 pl-10 text-left last:border-b-0',\n selectedId === ci.id\n ? 'bg-action-primary-bg/10'\n : 'hover:bg-bg-sunken/40'\n )}\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {ci.name}\n </p>\n <p className=\"truncate font-mono text-xs text-text-secondary\">\n {ci.ciNumber}\n {ci.environment ? ` · ${ci.environment}` : ''}\n </p>\n </div>\n <StatusPill status={ci.status} />\n </button>\n ))\n : null}\n </div>\n );\n })}\n </div>\n </div>\n </GlassCard>\n\n {/* Detail rail (desktop) */}\n <div className=\"hidden lg:block\">\n <GlassCard treatment=\"glass\" className=\"sticky top-4 border-border-seam\">\n <div className=\"p-4\">\n {selected ? (\n <CiDetail\n ci={selected}\n onEdit={() => openEdit(selected)}\n onDelete={() => handleDelete(selected)}\n deleting={deleting}\n />\n ) : (\n <div className=\"flex flex-col items-center justify-center gap-2 py-12 text-center\">\n <Network className=\"h-8 w-8 text-text-muted\" />\n <p className=\"text-sm font-medium text-text-primary\">\n Select a configuration item\n </p>\n <p className=\"text-xs text-text-secondary\">\n Choose a CI from the navigator to inspect its attributes.\n </p>\n </div>\n )}\n </div>\n </GlassCard>\n </div>\n </div>\n\n {/* Detail sheet (mobile) */}\n <ResponsiveDialog open={mobileDetailOpen && !isLarge} onOpenChange={setMobileDetailOpen}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Configuration item</ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n {selected ? (\n <CiDetail\n ci={selected}\n onEdit={() => openEdit(selected)}\n onDelete={() => handleDelete(selected)}\n deleting={deleting}\n />\n ) : null}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SamLicensesPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/SamLicensesPage.tsx"],"names":[],"mappings":"AA0SA,wBAAgB,eAAe,gCAoW9B"}
1
+ {"version":3,"file":"SamLicensesPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/SamLicensesPage.tsx"],"names":[],"mappings":"AAiTA,wBAAgB,eAAe,gCA2X9B"}