@burdenoff/microfe-cosmicintersection 2026.713.1 → 2026.720.1
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/cosmicintersection/CosmicIntersectionRoot.js.map +1 -1
- package/dist/cosmicintersection/CosmicIntersectionRoutes.js.map +1 -1
- package/dist/cosmicintersection/api/cosmicApi.d.ts.map +1 -1
- package/dist/cosmicintersection/api/cosmicApi.js.map +1 -1
- package/dist/cosmicintersection/components/ActionMenu.js.map +1 -1
- package/dist/cosmicintersection/components/ActivityFeedPanel.js.map +1 -1
- package/dist/cosmicintersection/components/DiscussionPanel.js.map +1 -1
- package/dist/cosmicintersection/components/EmptyState.js.map +1 -1
- package/dist/cosmicintersection/components/EntityFormShell.js.map +1 -1
- package/dist/cosmicintersection/components/EntityTagsField.js.map +1 -1
- package/dist/cosmicintersection/components/ErrorState.js.map +1 -1
- package/dist/cosmicintersection/components/FacetPanel.js.map +1 -1
- package/dist/cosmicintersection/components/FileUploadField.js.map +1 -1
- package/dist/cosmicintersection/components/LineageGraph.d.ts.map +1 -1
- package/dist/cosmicintersection/components/LineageGraph.js.map +1 -1
- package/dist/cosmicintersection/components/LoadingState.js.map +1 -1
- package/dist/cosmicintersection/components/PageLayout.js.map +1 -1
- package/dist/cosmicintersection/components/Pagination.js.map +1 -1
- package/dist/cosmicintersection/components/ResultsChart.js.map +1 -1
- package/dist/cosmicintersection/components/SafeLink.js.map +1 -1
- package/dist/cosmicintersection/components/StatusBadge.js.map +1 -1
- package/dist/cosmicintersection/components/StepDAG.js.map +1 -1
- package/dist/cosmicintersection/context/CosmicIntersectionContext.js.map +1 -1
- package/dist/cosmicintersection/lib/mutationResult.js.map +1 -1
- package/dist/cosmicintersection/lib/toast.js.map +1 -1
- package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionFormPage.js.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetFormPage.js.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -1
- package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebookFormPage.js.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/releases/ReleaseFormPage.js.map +1 -1
- package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourceDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourceFormPage.js.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
- package/dist/cosmicintersection/utils/cn.js.map +1 -1
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
- package/dist/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/stories/EmptyState.stories.d.ts +9 -0
- package/dist/stories/EmptyState.stories.d.ts.map +1 -0
- package/dist/stories/LoadingState.stories.d.ts +9 -0
- package/dist/stories/LoadingState.stories.d.ts.map +1 -0
- package/dist/stories/StatusBadge.stories.d.ts +12 -0
- package/dist/stories/StatusBadge.stories.d.ts.map +1 -0
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +14 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SourceFormPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/sources/SourceFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosCreateSourceMutation,\n useCosSourceQuery,\n useCosUpdateSourceMutation,\n type CosSourceStatus,\n type CosSourceType,\n} from '../../api/cosmicApi';\nimport { EntityFormShell, EntityTagsField, formatEnumLabel } from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nconst SOURCE_TYPES: CosSourceType[] = [\n 'TELESCOPE',\n 'SATELLITE',\n 'GROUND_SENSOR',\n 'ARCHIVE',\n 'API',\n 'STREAM',\n 'FILE_DROP',\n 'SIMULATION',\n];\n\nconst SOURCE_STATUSES: CosSourceStatus[] = [\n 'CONFIGURING',\n 'CONNECTED',\n 'SYNCING',\n 'DEGRADED',\n 'ERROR',\n 'DISABLED',\n];\n\ninterface SourceFormState {\n name: string;\n description: string;\n type: CosSourceType;\n status: CosSourceStatus;\n endpoint: string;\n credentialRef: string;\n config: string;\n tags: string[];\n}\n\nconst INITIAL_STATE: SourceFormState = {\n name: '',\n description: '',\n type: 'TELESCOPE',\n status: 'CONFIGURING',\n endpoint: '',\n credentialRef: '',\n config: '',\n tags: [],\n};\n\nconst fieldLabelClass = 'block space-y-1.5 text-sm';\nconst fieldSpanClass = 'font-medium text-text-primary';\n\nexport default function SourceFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useCosmicIntersection();\n\n const { data, loading: loadingSource } = useCosSourceQuery(id);\n const [createSource, { loading: creating }] = useCosCreateSourceMutation();\n const [updateSource, { loading: updating }] = useCosUpdateSourceMutation();\n\n const [form, setForm] = useState<SourceFormState>(INITIAL_STATE);\n const [hydrated, setHydrated] = useState(false);\n\n useEffect(() => {\n if (!isEdit || hydrated || !data?.cosSource) {\n return;\n }\n const source = data.cosSource;\n setForm({\n name: source.name ?? '',\n description: source.description ?? '',\n type: source.type,\n status: source.status,\n endpoint: source.endpoint ?? '',\n credentialRef: '',\n config: source.config ? JSON.stringify(source.config, null, 2) : '',\n tags: source.tags ?? [],\n });\n setHydrated(true);\n }, [isEdit, hydrated, data]);\n\n const onChange = <K extends keyof SourceFormState>(field: K, value: SourceFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const saving = creating || updating;\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.name.trim()) {\n notify.error('Name is required', 'Give this source a recognizable name.');\n return;\n }\n\n let config: Record<string, unknown> | undefined;\n if (form.config.trim()) {\n try {\n const parsed = JSON.parse(form.config) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error('Config must be a JSON object.');\n }\n config = parsed as Record<string, unknown>;\n } catch (parseError) {\n notify.error(\n 'Invalid config JSON',\n parseError instanceof Error ? parseError.message : 'Please provide valid JSON.'\n );\n return;\n }\n }\n\n const base = {\n name: form.name.trim(),\n type: form.type,\n status: form.status,\n description: form.description.trim() || undefined,\n endpoint: form.endpoint.trim() || undefined,\n credentialRef: form.credentialRef.trim() || undefined,\n config,\n tags: form.tags,\n };\n\n if (isEdit) {\n const response = await updateSource({ variables: { id, input: base } });\n if (response.data?.cosUpdateSource?.id) {\n notify.success('Source updated');\n navigate('sources');\n }\n return;\n }\n\n const response = await createSource({ variables: { input: base } });\n if (response.data?.cosCreateSource?.id) {\n notify.success('Source created');\n navigate('sources');\n }\n };\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit source' : 'New data source'}\n description=\"Instruments, archives and feeds that supply raw data into your federation.\"\n purpose=\"A source describes where data comes from — a telescope, satellite feed, archive, API or file drop. Configure it once, then attach datasets that ingest from it and mark it synced as data arrives.\"\n backLabel=\"Back to sources\"\n onBack={() => navigate('sources')}\n onSubmit={(event) => void onSubmit(event)}\n submitLabel={isEdit ? 'Save source' : 'Create source'}\n saving={saving || (isEdit && loadingSource && !hydrated)}\n >\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Name</span>\n <Input\n value={form.name}\n onChange={(event) => onChange('name', event.target.value)}\n placeholder=\"e.g. VLA Radio Array\"\n required\n />\n </label>\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Endpoint</span>\n <Input\n value={form.endpoint}\n onChange={(event) => onChange('endpoint', event.target.value)}\n placeholder=\"https://archive.example.org/api\"\n />\n </label>\n </div>\n\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Description</span>\n <Textarea\n value={form.description}\n onChange={(event) => onChange('description', event.target.value)}\n rows={3}\n placeholder=\"What this source provides and how it connects.\"\n />\n </label>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Type</span>\n <Select\n value={form.type}\n onValueChange={(value) => onChange('type', value as CosSourceType)}\n >\n <SelectTrigger aria-label=\"Source type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SOURCE_TYPES.map((type) => (\n <SelectItem key={type} value={type}>\n {formatEnumLabel(type)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Status</span>\n <Select\n value={form.status}\n onValueChange={(value) => onChange('status', value as CosSourceStatus)}\n >\n <SelectTrigger aria-label=\"Source status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SOURCE_STATUSES.map((status) => (\n <SelectItem key={status} value={status}>\n {formatEnumLabel(status)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Credential reference</span>\n <Input\n value={form.credentialRef}\n onChange={(event) => onChange('credentialRef', event.target.value)}\n placeholder=\"Secret key or vault reference (write-only)\"\n />\n <span className=\"text-xs text-text-secondary\">\n A pointer to a stored credential — never paste raw secrets here.\n </span>\n </label>\n\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Config (JSON)</span>\n <Textarea\n value={form.config}\n onChange={(event) => onChange('config', event.target.value)}\n rows={5}\n className=\"font-mono text-xs\"\n placeholder='{ \"pollIntervalMinutes\": 30 }'\n />\n <span className=\"text-xs text-text-secondary\">\n Optional connection parameters as a JSON object.\n </span>\n </label>\n\n <EntityTagsField\n value={form.tags}\n onChange={(tags) => onChange('tags', tags)}\n disabled={saving}\n />\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SourceFormPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/sources/SourceFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosCreateSourceMutation,\n useCosSourceQuery,\n useCosUpdateSourceMutation,\n type CosSourceStatus,\n type CosSourceType,\n} from '../../api/cosmicApi';\nimport { EntityFormShell, EntityTagsField, formatEnumLabel } from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nconst SOURCE_TYPES: CosSourceType[] = [\n 'TELESCOPE',\n 'SATELLITE',\n 'GROUND_SENSOR',\n 'ARCHIVE',\n 'API',\n 'STREAM',\n 'FILE_DROP',\n 'SIMULATION',\n];\n\nconst SOURCE_STATUSES: CosSourceStatus[] = [\n 'CONFIGURING',\n 'CONNECTED',\n 'SYNCING',\n 'DEGRADED',\n 'ERROR',\n 'DISABLED',\n];\n\ninterface SourceFormState {\n name: string;\n description: string;\n type: CosSourceType;\n status: CosSourceStatus;\n endpoint: string;\n credentialRef: string;\n config: string;\n tags: string[];\n}\n\nconst INITIAL_STATE: SourceFormState = {\n name: '',\n description: '',\n type: 'TELESCOPE',\n status: 'CONFIGURING',\n endpoint: '',\n credentialRef: '',\n config: '',\n tags: [],\n};\n\nconst fieldLabelClass = 'block space-y-1.5 text-sm';\nconst fieldSpanClass = 'font-medium text-text-primary';\n\nexport default function SourceFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useCosmicIntersection();\n\n const { data, loading: loadingSource } = useCosSourceQuery(id);\n const [createSource, { loading: creating }] = useCosCreateSourceMutation();\n const [updateSource, { loading: updating }] = useCosUpdateSourceMutation();\n\n const [form, setForm] = useState<SourceFormState>(INITIAL_STATE);\n const [hydrated, setHydrated] = useState(false);\n\n useEffect(() => {\n if (!isEdit || hydrated || !data?.cosSource) {\n return;\n }\n const source = data.cosSource;\n setForm({\n name: source.name ?? '',\n description: source.description ?? '',\n type: source.type,\n status: source.status,\n endpoint: source.endpoint ?? '',\n credentialRef: '',\n config: source.config ? JSON.stringify(source.config, null, 2) : '',\n tags: source.tags ?? [],\n });\n setHydrated(true);\n }, [isEdit, hydrated, data]);\n\n const onChange = <K extends keyof SourceFormState>(field: K, value: SourceFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const saving = creating || updating;\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.name.trim()) {\n notify.error('Name is required', 'Give this source a recognizable name.');\n return;\n }\n\n let config: Record<string, unknown> | undefined;\n if (form.config.trim()) {\n try {\n const parsed = JSON.parse(form.config) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error('Config must be a JSON object.');\n }\n config = parsed as Record<string, unknown>;\n } catch (parseError) {\n notify.error(\n 'Invalid config JSON',\n parseError instanceof Error ? parseError.message : 'Please provide valid JSON.'\n );\n return;\n }\n }\n\n const base = {\n name: form.name.trim(),\n type: form.type,\n status: form.status,\n description: form.description.trim() || undefined,\n endpoint: form.endpoint.trim() || undefined,\n credentialRef: form.credentialRef.trim() || undefined,\n config,\n tags: form.tags,\n };\n\n if (isEdit) {\n const response = await updateSource({ variables: { id, input: base } });\n if (response.data?.cosUpdateSource?.id) {\n notify.success('Source updated');\n navigate('sources');\n }\n return;\n }\n\n const response = await createSource({ variables: { input: base } });\n if (response.data?.cosCreateSource?.id) {\n notify.success('Source created');\n navigate('sources');\n }\n };\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit source' : 'New data source'}\n description=\"Instruments, archives and feeds that supply raw data into your federation.\"\n purpose=\"A source describes where data comes from — a telescope, satellite feed, archive, API or file drop. Configure it once, then attach datasets that ingest from it and mark it synced as data arrives.\"\n backLabel=\"Back to sources\"\n onBack={() => navigate('sources')}\n onSubmit={(event) => void onSubmit(event)}\n submitLabel={isEdit ? 'Save source' : 'Create source'}\n saving={saving || (isEdit && loadingSource && !hydrated)}\n >\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Name</span>\n <Input\n value={form.name}\n onChange={(event) => onChange('name', event.target.value)}\n placeholder=\"e.g. VLA Radio Array\"\n required\n />\n </label>\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Endpoint</span>\n <Input\n value={form.endpoint}\n onChange={(event) => onChange('endpoint', event.target.value)}\n placeholder=\"https://archive.example.org/api\"\n />\n </label>\n </div>\n\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Description</span>\n <Textarea\n value={form.description}\n onChange={(event) => onChange('description', event.target.value)}\n rows={3}\n placeholder=\"What this source provides and how it connects.\"\n />\n </label>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Type</span>\n <Select\n value={form.type}\n onValueChange={(value) => onChange('type', value as CosSourceType)}\n >\n <SelectTrigger aria-label=\"Source type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SOURCE_TYPES.map((type) => (\n <SelectItem key={type} value={type}>\n {formatEnumLabel(type)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Status</span>\n <Select\n value={form.status}\n onValueChange={(value) => onChange('status', value as CosSourceStatus)}\n >\n <SelectTrigger aria-label=\"Source status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SOURCE_STATUSES.map((status) => (\n <SelectItem key={status} value={status}>\n {formatEnumLabel(status)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Credential reference</span>\n <Input\n value={form.credentialRef}\n onChange={(event) => onChange('credentialRef', event.target.value)}\n placeholder=\"Secret key or vault reference (write-only)\"\n />\n <span className=\"text-xs text-text-secondary\">\n A pointer to a stored credential — never paste raw secrets here.\n </span>\n </label>\n\n <label className={fieldLabelClass}>\n <span className={fieldSpanClass}>Config (JSON)</span>\n <Textarea\n value={form.config}\n onChange={(event) => onChange('config', event.target.value)}\n rows={5}\n className=\"font-mono text-xs\"\n placeholder='{ \"pollIntervalMinutes\": 30 }'\n />\n <span className=\"text-xs text-text-secondary\">\n Optional connection parameters as a JSON object.\n </span>\n </label>\n\n <EntityTagsField\n value={form.tags}\n onChange={(tags) => onChange('tags', tags)}\n disabled={saving}\n />\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SourcesPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/sources/SourcesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Archive,\n FileInput,\n FlaskConical,\n Plus,\n Radio,\n RadioTower,\n RefreshCw,\n Satellite,\n Telescope,\n Webhook,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosArchiveSourceMutation,\n useCosMarkSourceSyncedMutation,\n useCosSourcesQuery,\n type CosSource,\n type CosSourceStatus,\n type CosSourceType,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n Pagination,\n SafeLink,\n StatusBadge,\n formatEnumLabel,\n usePagination,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nconst ALL = 'ALL';\n\nconst SOURCE_TYPES: CosSourceType[] = [\n 'TELESCOPE',\n 'SATELLITE',\n 'GROUND_SENSOR',\n 'ARCHIVE',\n 'API',\n 'STREAM',\n 'FILE_DROP',\n 'SIMULATION',\n];\n\nconst SOURCE_STATUSES: CosSourceStatus[] = [\n 'CONFIGURING',\n 'CONNECTED',\n 'SYNCING',\n 'DEGRADED',\n 'ERROR',\n 'DISABLED',\n];\n\n/** Per-type glyph to aid scannability (mockup 06). */\nconst SOURCE_TYPE_ICON: Record<CosSourceType, LucideIcon> = {\n TELESCOPE: Telescope,\n SATELLITE: Satellite,\n GROUND_SENSOR: RadioTower,\n ARCHIVE: Archive,\n API: Webhook,\n STREAM: Radio,\n FILE_DROP: FileInput,\n SIMULATION: FlaskConical,\n};\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return '—';\n }\n return parsed.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\nexport default function SourcesPage() {\n const { navigate } = useCosmicIntersection();\n const [typeFilter, setTypeFilter] = useState<string>(ALL);\n const [statusFilter, setStatusFilter] = useState<string>(ALL);\n const pagination = usePagination({ pageSize: 20, resetKey: `${typeFilter}|${statusFilter}` });\n\n const { data, loading, error, refetch } = useCosSourcesQuery({\n type: typeFilter === ALL ? undefined : (typeFilter as CosSourceType),\n status: statusFilter === ALL ? undefined : (statusFilter as CosSourceStatus),\n pagination: { limit: pagination.limit, offset: pagination.offset },\n });\n\n const [markSynced] = useCosMarkSourceSyncedMutation();\n const [archiveSource] = useCosArchiveSourceMutation();\n\n const sources = useMemo(() => data?.cosListSources.items ?? [], [data]);\n const pageInfo = data?.cosListSources.pageInfo;\n\n const handleMarkSynced = async (source: CosSource) => {\n const response = await markSynced({ variables: { id: source.id } });\n if (response.data?.cosMarkSourceSynced) {\n notify.success('Source marked synced', `“${source.name}” is now up to date.`);\n await refetch();\n }\n };\n\n const handleArchive = async (source: CosSource) => {\n const response = await archiveSource({ variables: { id: source.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveSource) {\n notify.error('Could not archive source', 'The server did not confirm the change.');\n throw new Error('Archive not confirmed');\n }\n notify.success('Source archived');\n await refetch();\n };\n\n const newButton = (\n <Button type=\"button\" onClick={() => navigate('sources/create')}>\n <Plus className=\"size-4\" />\n New source\n </Button>\n );\n\n const renderBody = () => {\n if (loading) {\n return <LoadingState variant=\"rows\" rows={6} label=\"Loading sources…\" />;\n }\n if (error) {\n return <ErrorState message={error.message} onRetry={() => void refetch()} />;\n }\n if (sources.length === 0) {\n const filtered = typeFilter !== ALL || statusFilter !== ALL;\n return (\n <EmptyState\n icon={Radio}\n title={filtered ? 'No sources match these filters' : 'No data sources yet'}\n description={\n filtered\n ? 'Try clearing the type or status filter to see more sources.'\n : 'Connect a telescope, satellite feed, archive or API to start ingesting scientific data.'\n }\n action={filtered ? undefined : newButton}\n />\n );\n }\n\n return (\n <>\n {/* Desktop / tablet: table */}\n <div className=\"hidden overflow-hidden rounded-card border border-border-default bg-bg-elevated md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Type</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Endpoint</TableHead>\n <TableHead>Last synced</TableHead>\n <TableHead>Tags</TableHead>\n <TableHead className=\"text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {sources.map((source) => {\n const TypeIcon = SOURCE_TYPE_ICON[source.type] ?? Radio;\n return (\n <TableRow\n key={source.id}\n className=\"cursor-pointer\"\n onClick={() => navigate(`sources/${source.id}`)}\n >\n <TableCell>\n <div className=\"flex items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-button bg-bg-sunken text-text-secondary\">\n <TypeIcon className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <div className=\"min-w-0\">\n <div className=\"font-medium text-text-primary\">{source.name}</div>\n {source.description ? (\n <div className=\"mt-0.5 line-clamp-1 max-w-xs text-xs text-text-secondary\">\n {source.description}\n </div>\n ) : null}\n </div>\n </div>\n </TableCell>\n <TableCell>\n <Badge variant=\"secondary\">{formatEnumLabel(source.type)}</Badge>\n </TableCell>\n <TableCell>\n <StatusBadge status={source.status} />\n </TableCell>\n <TableCell\n className=\"max-w-[16rem]\"\n onClick={(event) => event.stopPropagation()}\n >\n {source.endpoint ? (\n <SafeLink\n href={source.endpoint}\n className=\"block truncate text-sm text-text-primary underline-offset-2 hover:underline\"\n fallbackClassName=\"block truncate text-sm text-text-secondary\"\n >\n {source.endpoint}\n </SafeLink>\n ) : (\n <span className=\"text-sm text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(source.lastSyncedAt)}\n </TableCell>\n <TableCell>\n <div className=\"flex max-w-[12rem] flex-wrap gap-1\">\n {source.tags.length === 0 ? (\n <span className=\"text-xs text-text-secondary\">—</span>\n ) : (\n source.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))\n )}\n {source.tags.length > 3 ? (\n <span className=\"text-xs text-text-secondary\">\n +{source.tags.length - 3}\n </span>\n ) : null}\n </div>\n </TableCell>\n <TableCell className=\"text-right\">\n <ActionMenu\n ariaLabel={`Actions for ${source.name}`}\n entityName={source.name}\n onView={() => navigate(`sources/${source.id}`)}\n onEdit={() => navigate(`sources/${source.id}/edit`)}\n onDelete={() => handleArchive(source)}\n deleteLabel=\"Archive source\"\n extraItems={[\n {\n key: 'mark-synced',\n label: 'Mark synced',\n icon: RefreshCw,\n onSelect: () => void handleMarkSynced(source),\n },\n ]}\n />\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n {/* Mobile: stacked cards */}\n <ul className=\"space-y-3 md:hidden\">\n {sources.map((source) => {\n const TypeIcon = SOURCE_TYPE_ICON[source.type] ?? Radio;\n return (\n <li key={source.id}>\n <div\n role=\"button\"\n tabIndex={0}\n aria-label={`Open ${source.name}`}\n onClick={() => navigate(`sources/${source.id}`)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n navigate(`sources/${source.id}`);\n }\n }}\n className=\"flex flex-col gap-3 rounded-card border border-border-default bg-bg-elevated p-4 outline-none transition-colors hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-button bg-bg-sunken text-text-secondary\">\n <TypeIcon className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary\">{source.name}</p>\n {source.description ? (\n <p className=\"mt-0.5 line-clamp-2 text-xs text-text-secondary\">\n {source.description}\n </p>\n ) : null}\n </div>\n </div>\n <div onClick={(event) => event.stopPropagation()}>\n <ActionMenu\n ariaLabel={`Actions for ${source.name}`}\n entityName={source.name}\n onView={() => navigate(`sources/${source.id}`)}\n onEdit={() => navigate(`sources/${source.id}/edit`)}\n onDelete={() => handleArchive(source)}\n deleteLabel=\"Archive source\"\n extraItems={[\n {\n key: 'mark-synced',\n label: 'Mark synced',\n icon: RefreshCw,\n onSelect: () => void handleMarkSynced(source),\n },\n ]}\n />\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Badge variant=\"secondary\">{formatEnumLabel(source.type)}</Badge>\n <StatusBadge status={source.status} />\n </div>\n\n {source.endpoint ? (\n <div onClick={(event) => event.stopPropagation()}>\n <SafeLink\n href={source.endpoint}\n className=\"block truncate text-sm text-text-primary underline-offset-2 hover:underline\"\n fallbackClassName=\"block truncate text-sm text-text-secondary\"\n >\n {source.endpoint}\n </SafeLink>\n </div>\n ) : null}\n\n <div className=\"flex items-center justify-between gap-2 text-xs text-text-secondary\">\n <span>Last synced</span>\n <span>{formatDate(source.lastSyncedAt)}</span>\n </div>\n\n {source.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {source.tags.slice(0, 4).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n {source.tags.length > 4 ? (\n <span className=\"text-xs text-text-secondary\">\n +{source.tags.length - 4}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n </li>\n );\n })}\n </ul>\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={sources.length}\n hasMore={pageInfo?.hasMore ?? false}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"sources\"\n loading={loading}\n />\n </>\n );\n };\n\n return (\n <PageLayout\n title=\"Data sources\"\n description=\"Instruments, archives and feeds that supply raw data into your federation.\"\n actions={newButton}\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"w-full max-w-[14rem]\">\n <Select value={typeFilter} onValueChange={setTypeFilter}>\n <SelectTrigger aria-label=\"Filter by type\">\n <SelectValue placeholder=\"All types\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All types</SelectItem>\n {SOURCE_TYPES.map((type) => (\n <SelectItem key={type} value={type}>\n {formatEnumLabel(type)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"w-full max-w-[14rem]\">\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger aria-label=\"Filter by status\">\n <SelectValue placeholder=\"All statuses\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All statuses</SelectItem>\n {SOURCE_STATUSES.map((status) => (\n <SelectItem key={status} value={status}>\n {formatEnumLabel(status)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {renderBody()}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SourcesPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/sources/SourcesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Archive,\n FileInput,\n FlaskConical,\n Plus,\n Radio,\n RadioTower,\n RefreshCw,\n Satellite,\n Telescope,\n Webhook,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosArchiveSourceMutation,\n useCosMarkSourceSyncedMutation,\n useCosSourcesQuery,\n type CosSource,\n type CosSourceStatus,\n type CosSourceType,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n Pagination,\n SafeLink,\n StatusBadge,\n formatEnumLabel,\n usePagination,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nconst ALL = 'ALL';\n\nconst SOURCE_TYPES: CosSourceType[] = [\n 'TELESCOPE',\n 'SATELLITE',\n 'GROUND_SENSOR',\n 'ARCHIVE',\n 'API',\n 'STREAM',\n 'FILE_DROP',\n 'SIMULATION',\n];\n\nconst SOURCE_STATUSES: CosSourceStatus[] = [\n 'CONFIGURING',\n 'CONNECTED',\n 'SYNCING',\n 'DEGRADED',\n 'ERROR',\n 'DISABLED',\n];\n\n/** Per-type glyph to aid scannability (mockup 06). */\nconst SOURCE_TYPE_ICON: Record<CosSourceType, LucideIcon> = {\n TELESCOPE: Telescope,\n SATELLITE: Satellite,\n GROUND_SENSOR: RadioTower,\n ARCHIVE: Archive,\n API: Webhook,\n STREAM: Radio,\n FILE_DROP: FileInput,\n SIMULATION: FlaskConical,\n};\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return '—';\n }\n return parsed.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\nexport default function SourcesPage() {\n const { navigate } = useCosmicIntersection();\n const [typeFilter, setTypeFilter] = useState<string>(ALL);\n const [statusFilter, setStatusFilter] = useState<string>(ALL);\n const pagination = usePagination({ pageSize: 20, resetKey: `${typeFilter}|${statusFilter}` });\n\n const { data, loading, error, refetch } = useCosSourcesQuery({\n type: typeFilter === ALL ? undefined : (typeFilter as CosSourceType),\n status: statusFilter === ALL ? undefined : (statusFilter as CosSourceStatus),\n pagination: { limit: pagination.limit, offset: pagination.offset },\n });\n\n const [markSynced] = useCosMarkSourceSyncedMutation();\n const [archiveSource] = useCosArchiveSourceMutation();\n\n const sources = useMemo(() => data?.cosListSources.items ?? [], [data]);\n const pageInfo = data?.cosListSources.pageInfo;\n\n const handleMarkSynced = async (source: CosSource) => {\n const response = await markSynced({ variables: { id: source.id } });\n if (response.data?.cosMarkSourceSynced) {\n notify.success('Source marked synced', `“${source.name}” is now up to date.`);\n await refetch();\n }\n };\n\n const handleArchive = async (source: CosSource) => {\n const response = await archiveSource({ variables: { id: source.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveSource) {\n notify.error('Could not archive source', 'The server did not confirm the change.');\n throw new Error('Archive not confirmed');\n }\n notify.success('Source archived');\n await refetch();\n };\n\n const newButton = (\n <Button type=\"button\" onClick={() => navigate('sources/create')}>\n <Plus className=\"size-4\" />\n New source\n </Button>\n );\n\n const renderBody = () => {\n if (loading) {\n return <LoadingState variant=\"rows\" rows={6} label=\"Loading sources…\" />;\n }\n if (error) {\n return <ErrorState message={error.message} onRetry={() => void refetch()} />;\n }\n if (sources.length === 0) {\n const filtered = typeFilter !== ALL || statusFilter !== ALL;\n return (\n <EmptyState\n icon={Radio}\n title={filtered ? 'No sources match these filters' : 'No data sources yet'}\n description={\n filtered\n ? 'Try clearing the type or status filter to see more sources.'\n : 'Connect a telescope, satellite feed, archive or API to start ingesting scientific data.'\n }\n action={filtered ? undefined : newButton}\n />\n );\n }\n\n return (\n <>\n {/* Desktop / tablet: table */}\n <div className=\"hidden overflow-hidden rounded-card border border-border-default bg-bg-elevated md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Type</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Endpoint</TableHead>\n <TableHead>Last synced</TableHead>\n <TableHead>Tags</TableHead>\n <TableHead className=\"text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {sources.map((source) => {\n const TypeIcon = SOURCE_TYPE_ICON[source.type] ?? Radio;\n return (\n <TableRow\n key={source.id}\n className=\"cursor-pointer\"\n onClick={() => navigate(`sources/${source.id}`)}\n >\n <TableCell>\n <div className=\"flex items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-button bg-bg-sunken text-text-secondary\">\n <TypeIcon className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <div className=\"min-w-0\">\n <div className=\"font-medium text-text-primary\">{source.name}</div>\n {source.description ? (\n <div className=\"mt-0.5 line-clamp-1 max-w-xs text-xs text-text-secondary\">\n {source.description}\n </div>\n ) : null}\n </div>\n </div>\n </TableCell>\n <TableCell>\n <Badge variant=\"secondary\">{formatEnumLabel(source.type)}</Badge>\n </TableCell>\n <TableCell>\n <StatusBadge status={source.status} />\n </TableCell>\n <TableCell\n className=\"max-w-[16rem]\"\n onClick={(event) => event.stopPropagation()}\n >\n {source.endpoint ? (\n <SafeLink\n href={source.endpoint}\n className=\"block truncate text-sm text-text-primary underline-offset-2 hover:underline\"\n fallbackClassName=\"block truncate text-sm text-text-secondary\"\n >\n {source.endpoint}\n </SafeLink>\n ) : (\n <span className=\"text-sm text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(source.lastSyncedAt)}\n </TableCell>\n <TableCell>\n <div className=\"flex max-w-[12rem] flex-wrap gap-1\">\n {source.tags.length === 0 ? (\n <span className=\"text-xs text-text-secondary\">—</span>\n ) : (\n source.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))\n )}\n {source.tags.length > 3 ? (\n <span className=\"text-xs text-text-secondary\">\n +{source.tags.length - 3}\n </span>\n ) : null}\n </div>\n </TableCell>\n <TableCell className=\"text-right\">\n <ActionMenu\n ariaLabel={`Actions for ${source.name}`}\n entityName={source.name}\n onView={() => navigate(`sources/${source.id}`)}\n onEdit={() => navigate(`sources/${source.id}/edit`)}\n onDelete={() => handleArchive(source)}\n deleteLabel=\"Archive source\"\n extraItems={[\n {\n key: 'mark-synced',\n label: 'Mark synced',\n icon: RefreshCw,\n onSelect: () => void handleMarkSynced(source),\n },\n ]}\n />\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n {/* Mobile: stacked cards */}\n <ul className=\"space-y-3 md:hidden\">\n {sources.map((source) => {\n const TypeIcon = SOURCE_TYPE_ICON[source.type] ?? Radio;\n return (\n <li key={source.id}>\n <div\n role=\"button\"\n tabIndex={0}\n aria-label={`Open ${source.name}`}\n onClick={() => navigate(`sources/${source.id}`)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n navigate(`sources/${source.id}`);\n }\n }}\n className=\"flex flex-col gap-3 rounded-card border border-border-default bg-bg-elevated p-4 outline-none transition-colors hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n <span className=\"mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-button bg-bg-sunken text-text-secondary\">\n <TypeIcon className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary\">{source.name}</p>\n {source.description ? (\n <p className=\"mt-0.5 line-clamp-2 text-xs text-text-secondary\">\n {source.description}\n </p>\n ) : null}\n </div>\n </div>\n <div onClick={(event) => event.stopPropagation()}>\n <ActionMenu\n ariaLabel={`Actions for ${source.name}`}\n entityName={source.name}\n onView={() => navigate(`sources/${source.id}`)}\n onEdit={() => navigate(`sources/${source.id}/edit`)}\n onDelete={() => handleArchive(source)}\n deleteLabel=\"Archive source\"\n extraItems={[\n {\n key: 'mark-synced',\n label: 'Mark synced',\n icon: RefreshCw,\n onSelect: () => void handleMarkSynced(source),\n },\n ]}\n />\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Badge variant=\"secondary\">{formatEnumLabel(source.type)}</Badge>\n <StatusBadge status={source.status} />\n </div>\n\n {source.endpoint ? (\n <div onClick={(event) => event.stopPropagation()}>\n <SafeLink\n href={source.endpoint}\n className=\"block truncate text-sm text-text-primary underline-offset-2 hover:underline\"\n fallbackClassName=\"block truncate text-sm text-text-secondary\"\n >\n {source.endpoint}\n </SafeLink>\n </div>\n ) : null}\n\n <div className=\"flex items-center justify-between gap-2 text-xs text-text-secondary\">\n <span>Last synced</span>\n <span>{formatDate(source.lastSyncedAt)}</span>\n </div>\n\n {source.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {source.tags.slice(0, 4).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n {source.tags.length > 4 ? (\n <span className=\"text-xs text-text-secondary\">\n +{source.tags.length - 4}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n </li>\n );\n })}\n </ul>\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={sources.length}\n hasMore={pageInfo?.hasMore ?? false}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"sources\"\n loading={loading}\n />\n </>\n );\n };\n\n return (\n <PageLayout\n title=\"Data sources\"\n description=\"Instruments, archives and feeds that supply raw data into your federation.\"\n actions={newButton}\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"w-full max-w-[14rem]\">\n <Select value={typeFilter} onValueChange={setTypeFilter}>\n <SelectTrigger aria-label=\"Filter by type\">\n <SelectValue placeholder=\"All types\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All types</SelectItem>\n {SOURCE_TYPES.map((type) => (\n <SelectItem key={type} value={type}>\n {formatEnumLabel(type)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"w-full max-w-[14rem]\">\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger aria-label=\"Filter by status\">\n <SelectValue placeholder=\"All statuses\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All statuses</SelectItem>\n {SOURCE_STATUSES.map((status) => (\n <SelectItem key={status} value={status}>\n {formatEnumLabel(status)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {renderBody()}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cn.js","names":[],"sources":["../../../src/cosmicintersection/utils/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;AAGA,SAAgB,EAAG,GAAG,GAAsB;
|
|
1
|
+
{"version":3,"file":"cn.js","names":[],"sources":["../../../src/cosmicintersection/utils/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;AAGA,SAAgB,EAAG,GAAG,GAAsB;CAC1C,OAAO,EAAQ,EAAK,CAAM,CAAC;AAC7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clsx.js","names":[],"sources":["../../../../node_modules/clsx/dist/clsx.mjs"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;"],"x_google_ignoreList":[0],"mappings":";AAAA,SAAS,EAAE,GAAE;CAAC,IAAI,GAAE,GAAE,IAAE;
|
|
1
|
+
{"version":3,"file":"clsx.js","names":[],"sources":["../../../../node_modules/clsx/dist/clsx.mjs"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;"],"x_google_ignoreList":[0],"mappings":";AAAA,SAAS,EAAE,GAAE;CAAC,IAAI,GAAE,GAAE,IAAE;CAAG,IAAa,OAAO,KAAjB,YAA8B,OAAO,KAAjB,UAAmB,KAAG;MAAO,IAAa,OAAO,KAAjB,UAAmB,IAAG,MAAM,QAAQ,CAAC,GAAE;EAAC,IAAI,IAAE,EAAE;EAAO,KAAI,IAAE,GAAE,IAAE,GAAE,KAAI,EAAE,OAAK,IAAE,EAAE,EAAE,EAAE,OAAK,MAAI,KAAG,MAAK,KAAG;CAAE,OAAM,KAAI,KAAK,GAAE,EAAE,OAAK,MAAI,KAAG,MAAK,KAAG;CAAG,OAAO;AAAC;AAAC,SAAgB,IAAM;CAAC,KAAI,IAAI,GAAE,GAAE,IAAE,GAAE,IAAE,IAAG,IAAE,UAAU,QAAO,IAAE,GAAE,KAAI,CAAC,IAAE,UAAU,QAAM,IAAE,EAAE,CAAC,OAAK,MAAI,KAAG,MAAK,KAAG;CAAG,OAAO;AAAC"}
|
|
@@ -234,7 +234,7 @@ var e = (e, t) => {
|
|
|
234
234
|
}, Y = (e, t, n = !1) => {
|
|
235
235
|
let r = j.exec(e);
|
|
236
236
|
return r ? r[1] ? t(r[1]) : n : !1;
|
|
237
|
-
}, X = (e) => e === "position" || e === "percentage", Z = (e) => e === "image" || e === "url", Q = (e) => e === "length" || e === "size" || e === "bg-size", $ = (e) => e === "length", Ce = (e) => e === "number", we = (e) => e === "family-name", Te = (e) => e === "number" || e === "weight", Ee = (e) => e === "shadow", De =
|
|
237
|
+
}, X = (e) => e === "position" || e === "percentage", Z = (e) => e === "image" || e === "url", Q = (e) => e === "length" || e === "size" || e === "bg-size", $ = (e) => e === "length", Ce = (e) => e === "number", we = (e) => e === "family-name", Te = (e) => e === "number" || e === "weight", Ee = (e) => e === "shadow", De = /*#__PURE__*/ D(() => {
|
|
238
238
|
let e = k("color"), t = k("font"), n = k("text"), r = k("font-weight"), i = k("tracking"), a = k("leading"), o = k("breakpoint"), s = k("container"), c = k("spacing"), l = k("radius"), u = k("shadow"), d = k("inset-shadow"), ee = k("text-shadow"), f = k("drop-shadow"), p = k("blur"), m = k("perspective"), h = k("aspect"), g = k("ease"), _ = k("animate"), v = () => [
|
|
239
239
|
"auto",
|
|
240
240
|
"avoid",
|