@burdenoff/microfe-cosmicintersection 2026.827.1 → 2026.830.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/cosmicintersection/components/ActionMenu.d.ts +1 -1
  2. package/dist/cosmicintersection/components/ActionMenu.d.ts.map +1 -1
  3. package/dist/cosmicintersection/components/ActionMenu.js +51 -54
  4. package/dist/cosmicintersection/components/ActionMenu.js.map +1 -1
  5. package/dist/cosmicintersection/components/EntityFormShell.d.ts +1 -1
  6. package/dist/cosmicintersection/components/EntityFormShell.d.ts.map +1 -1
  7. package/dist/cosmicintersection/components/EntityFormShell.js +29 -27
  8. package/dist/cosmicintersection/components/EntityFormShell.js.map +1 -1
  9. package/dist/cosmicintersection/components/ErrorState.d.ts +1 -1
  10. package/dist/cosmicintersection/components/ErrorState.d.ts.map +1 -1
  11. package/dist/cosmicintersection/components/ErrorState.js +19 -17
  12. package/dist/cosmicintersection/components/ErrorState.js.map +1 -1
  13. package/dist/cosmicintersection/components/FileUploadField.d.ts.map +1 -1
  14. package/dist/cosmicintersection/components/FileUploadField.js +61 -60
  15. package/dist/cosmicintersection/components/FileUploadField.js.map +1 -1
  16. package/dist/cosmicintersection/components/LoadingState.d.ts +1 -1
  17. package/dist/cosmicintersection/components/LoadingState.d.ts.map +1 -1
  18. package/dist/cosmicintersection/components/LoadingState.js +15 -13
  19. package/dist/cosmicintersection/components/LoadingState.js.map +1 -1
  20. package/dist/cosmicintersection/components/PageLayout.d.ts.map +1 -1
  21. package/dist/cosmicintersection/components/PageLayout.js +28 -27
  22. package/dist/cosmicintersection/components/PageLayout.js.map +1 -1
  23. package/dist/cosmicintersection/components/Pagination.d.ts.map +1 -1
  24. package/dist/cosmicintersection/components/Pagination.js +38 -35
  25. package/dist/cosmicintersection/components/Pagination.js.map +1 -1
  26. package/dist/cosmicintersection/components/StatusBadge.d.ts.map +1 -1
  27. package/dist/cosmicintersection/components/StatusBadge.js +30 -9
  28. package/dist/cosmicintersection/components/StatusBadge.js.map +1 -1
  29. package/dist/cosmicintersection/pages/access/AccessPage.d.ts.map +1 -1
  30. package/dist/cosmicintersection/pages/access/AccessPage.js +147 -146
  31. package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
  32. package/dist/cosmicintersection/pages/analyses/AnalysesPage.d.ts.map +1 -1
  33. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js +260 -259
  34. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
  35. package/dist/cosmicintersection/pages/collections/CollectionsPage.d.ts.map +1 -1
  36. package/dist/cosmicintersection/pages/collections/CollectionsPage.js +63 -62
  37. package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -1
  38. package/dist/cosmicintersection/pages/dashboard/DashboardPage.d.ts.map +1 -1
  39. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js +132 -131
  40. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
  41. package/dist/cosmicintersection/pages/datasets/DatasetsPage.d.ts.map +1 -1
  42. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js +178 -177
  43. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
  44. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.d.ts.map +1 -1
  45. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js +138 -145
  46. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -1
  47. package/dist/cosmicintersection/pages/lineage/LineagePage.d.ts.map +1 -1
  48. package/dist/cosmicintersection/pages/lineage/LineagePage.js +78 -88
  49. package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -1
  50. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.d.ts.map +1 -1
  51. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js +125 -124
  52. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
  53. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.d.ts.map +1 -1
  54. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js +152 -151
  55. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
  56. package/dist/cosmicintersection/pages/releases/ReleasesPage.d.ts.map +1 -1
  57. package/dist/cosmicintersection/pages/releases/ReleasesPage.js +175 -174
  58. package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
  59. package/dist/cosmicintersection/pages/runs/RunsPage.d.ts.map +1 -1
  60. package/dist/cosmicintersection/pages/runs/RunsPage.js +197 -188
  61. package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
  62. package/dist/cosmicintersection/pages/sources/SourcesPage.d.ts.map +1 -1
  63. package/dist/cosmicintersection/pages/sources/SourcesPage.js +65 -64
  64. package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
  65. package/dist/dev/main.d.ts.map +1 -1
  66. package/dist/shared/hooks/useTr.d.ts +12 -0
  67. package/dist/shared/hooks/useTr.d.ts.map +1 -0
  68. package/dist/shared/hooks/useTr.js +16 -0
  69. package/dist/shared/hooks/useTr.js.map +1 -0
  70. package/package.json +1 -1
@@ -1,12 +1,13 @@
1
1
  import { notify as e } from "../lib/toast.js";
2
- import { useId as t, useRef as n, useState as r } from "react";
3
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
4
- import { Download as o, FileUp as s, Loader2 as c, Paperclip as l, X as u } from "lucide-react";
5
- import { Button as d } from "@burdenoff/fe-libs/ui";
6
- import { gql as f } from "@apollo/client";
7
- import { useApolloClient as p, useQuery as m } from "@apollo/client/react";
2
+ import { useTr as t } from "../../shared/hooks/useTr.js";
3
+ import { useId as n, useRef as r, useState as i } from "react";
4
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
5
+ import { Download as s, FileUp as c, Loader2 as l, Paperclip as u, X as d } from "lucide-react";
6
+ import { Button as f } from "@burdenoff/fe-libs/ui";
7
+ import { gql as p } from "@apollo/client";
8
+ import { useApolloClient as m, useQuery as h } from "@apollo/client/react";
8
9
  //#region src/cosmicintersection/components/FileUploadField.tsx
9
- var h = f`
10
+ var g = p`
10
11
  mutation CosInitiateUpload($input: InitiateUploadInput!) {
11
12
  initiateUpload(input: $input) {
12
13
  session {
@@ -15,7 +16,7 @@ var h = f`
15
16
  uploadUrl
16
17
  }
17
18
  }
18
- `, g = f`
19
+ `, _ = p`
19
20
  mutation CosCompleteUpload($input: CompleteUploadInput!) {
20
21
  completeUpload(input: $input) {
21
22
  fileId
@@ -24,123 +25,123 @@ var h = f`
24
25
  }
25
26
  }
26
27
  }
27
- `, _ = f`
28
+ `, v = p`
28
29
  query CosGetFileDownloadUrl($id: ID!, $expiresIn: Int) {
29
30
  getFileDownloadUrl(id: $id, expiresIn: $expiresIn)
30
31
  }
31
- `, v = 3600;
32
- function y(e) {
33
- return m(_, {
32
+ `, y = 3600;
33
+ function b(e) {
34
+ return h(v, {
34
35
  variables: {
35
36
  id: e ?? "",
36
- expiresIn: v
37
+ expiresIn: y
37
38
  },
38
39
  skip: !e,
39
40
  fetchPolicy: "no-cache"
40
41
  });
41
42
  }
42
- function b({ value: f, onChange: m, label: _, hint: v, folderId: b, accept: x, disabled: S = !1, className: C }) {
43
- let w = p(), T = n(null), E = t(), [D, O] = r(!1), [k, A] = r(null), j = y(D ? null : f).data?.getFileDownloadUrl ?? null, M = async (t) => {
43
+ function x({ value: p, onChange: h, label: v, hint: y, folderId: x, accept: S, disabled: C = !1, className: w }) {
44
+ let T = t(), E = m(), D = r(null), O = n(), [k, A] = i(!1), [j, M] = i(null), N = b(k ? null : p).data?.getFileDownloadUrl ?? null, P = async (t) => {
44
45
  let n = t.target.files?.[0];
45
46
  if (t.target.value = "", n) {
46
- O(!0);
47
+ A(!0);
47
48
  try {
48
- let t = n.name.includes(".") ? n.name.substring(n.name.lastIndexOf(".") + 1) : "", r = n.type || "application/octet-stream", i = await w.mutate({
49
- mutation: h,
49
+ let t = n.name.includes(".") ? n.name.substring(n.name.lastIndexOf(".") + 1) : "", r = n.type || "application/octet-stream", i = await E.mutate({
50
+ mutation: g,
50
51
  variables: { input: {
51
52
  fileName: n.name,
52
53
  fileSize: String(n.size),
53
54
  extension: t,
54
55
  mimeType: r,
55
- ...b ? { folderId: b } : {}
56
+ ...x ? { folderId: x } : {}
56
57
  } }
57
58
  }), a = i.data?.initiateUpload?.session, o = i.data?.initiateUpload?.uploadUrl;
58
- if (!a || !o) throw Error(i.error?.message ?? "Could not start the upload.");
59
+ if (!a || !o) throw Error(i.error?.message ?? T("cosmicintersection.common.uploadStartError", "Could not start the upload."));
59
60
  await new Promise((e, t) => {
60
61
  let i = new XMLHttpRequest();
61
62
  i.addEventListener("load", () => {
62
63
  i.status >= 200 && i.status < 300 ? e() : t(/* @__PURE__ */ Error(`Upload failed with status ${i.status}`));
63
64
  }), i.addEventListener("error", () => t(/* @__PURE__ */ Error("Upload network error"))), i.addEventListener("abort", () => t(/* @__PURE__ */ Error("Upload aborted"))), i.open("PUT", o), i.setRequestHeader("Content-Type", r), i.send(n);
64
65
  });
65
- let s = await w.mutate({
66
- mutation: g,
66
+ let s = await E.mutate({
67
+ mutation: _,
67
68
  variables: { input: {
68
69
  sessionId: a.id,
69
- ...b ? { folderId: b } : {}
70
+ ...x ? { folderId: x } : {}
70
71
  } }
71
72
  }), c = s.data?.completeUpload?.fileId ?? s.data?.completeUpload?.file?.id;
72
- if (!c) throw Error(s.error?.message ?? "The file was uploaded but not confirmed.");
73
- A(n.name), await m(c), e.success("File uploaded");
73
+ if (!c) throw Error(s.error?.message ?? T("cosmicintersection.common.uploadConfirmError", "The file was uploaded but not confirmed."));
74
+ M(n.name), await h(c), e.success(T("cosmicintersection.common.uploadSuccess", "File uploaded"));
74
75
  } catch (t) {
75
- e.error("Upload failed", t instanceof Error ? t.message : "Please try again.");
76
+ e.error(T("cosmicintersection.common.uploadFailed", "Upload failed"), t instanceof Error ? t.message : T("cosmicintersection.common.uploadTryAgain", "Please try again."));
76
77
  } finally {
77
- O(!1);
78
+ A(!1);
78
79
  }
79
80
  }
80
- }, N = async () => {
81
- A(null), await m("");
81
+ }, F = async () => {
82
+ M(null), await h("");
82
83
  };
83
- return /* @__PURE__ */ a("div", {
84
- className: C,
85
- children: [/* @__PURE__ */ a("div", {
84
+ return /* @__PURE__ */ o("div", {
85
+ className: w,
86
+ children: [/* @__PURE__ */ o("div", {
86
87
  className: "flex flex-wrap items-center gap-3",
87
- children: [/* @__PURE__ */ i("div", {
88
+ children: [/* @__PURE__ */ a("div", {
88
89
  className: "flex size-12 shrink-0 items-center justify-center rounded-card border border-dashed border-border-default bg-bg-sunken text-text-secondary",
89
- children: /* @__PURE__ */ i(l, {
90
+ children: /* @__PURE__ */ a(u, {
90
91
  className: "size-5",
91
92
  "aria-hidden": "true"
92
93
  })
93
- }), /* @__PURE__ */ a("div", {
94
+ }), /* @__PURE__ */ o("div", {
94
95
  className: "space-y-1",
95
- children: [/* @__PURE__ */ a("div", {
96
+ children: [/* @__PURE__ */ o("div", {
96
97
  className: "flex items-center gap-2",
97
- children: [/* @__PURE__ */ a(d, {
98
+ children: [/* @__PURE__ */ o(f, {
98
99
  type: "button",
99
100
  variant: "secondary",
100
101
  size: "sm",
101
- disabled: S || D,
102
- onClick: () => T.current?.click(),
103
- children: [D ? /* @__PURE__ */ i(c, { className: "size-4 animate-spin" }) : /* @__PURE__ */ i(s, { className: "size-4" }), D ? "Uploading…" : f ? "Replace file" : _]
104
- }), f && !D ? /* @__PURE__ */ a(d, {
102
+ disabled: C || k,
103
+ onClick: () => D.current?.click(),
104
+ children: [k ? /* @__PURE__ */ a(l, { className: "size-4 animate-spin" }) : /* @__PURE__ */ a(c, { className: "size-4" }), k ? T("cosmicintersection.common.uploading", "Uploading…") : p ? T("cosmicintersection.common.replaceFile", "Replace file") : v]
105
+ }), p && !k ? /* @__PURE__ */ o(f, {
105
106
  type: "button",
106
107
  variant: "ghost",
107
108
  size: "sm",
108
- onClick: () => void N(),
109
- disabled: S,
110
- children: [/* @__PURE__ */ i(u, { className: "size-4" }), "Remove"]
109
+ onClick: () => void F(),
110
+ disabled: C,
111
+ children: [/* @__PURE__ */ a(d, { className: "size-4" }), T("cosmicintersection.common.remove", "Remove")]
111
112
  }) : null]
112
- }), f ? /* @__PURE__ */ a("div", {
113
+ }), p ? /* @__PURE__ */ o("div", {
113
114
  className: "flex flex-wrap items-center gap-2 text-xs text-text-secondary",
114
- children: [/* @__PURE__ */ i("span", {
115
+ children: [/* @__PURE__ */ a("span", {
115
116
  className: "max-w-[16rem] truncate",
116
- children: k ?? `File ${f}`
117
- }), j ? /* @__PURE__ */ a("a", {
118
- href: j,
117
+ children: j ?? `File ${p}`
118
+ }), N ? /* @__PURE__ */ o("a", {
119
+ href: N,
119
120
  target: "_blank",
120
121
  rel: "noopener noreferrer",
121
122
  className: "inline-flex items-center gap-1 text-text-primary underline-offset-2 hover:underline",
122
- children: [/* @__PURE__ */ i(o, {
123
+ children: [/* @__PURE__ */ a(s, {
123
124
  className: "size-3.5",
124
125
  "aria-hidden": "true"
125
- }), "Download"]
126
+ }), T("cosmicintersection.common.download", "Download")]
126
127
  }) : null]
127
- }) : v ? /* @__PURE__ */ i("p", {
128
+ }) : y ? /* @__PURE__ */ a("p", {
128
129
  className: "text-xs text-text-secondary",
129
- children: v
130
+ children: y
130
131
  }) : null]
131
132
  })]
132
- }), /* @__PURE__ */ i("input", {
133
- ref: T,
134
- id: E,
133
+ }), /* @__PURE__ */ a("input", {
134
+ ref: D,
135
+ id: O,
135
136
  type: "file",
136
- accept: x,
137
+ accept: S,
137
138
  className: "sr-only",
138
- "aria-label": _,
139
- onChange: (e) => void M(e)
139
+ "aria-label": v,
140
+ onChange: (e) => void P(e)
140
141
  })]
141
142
  });
142
143
  }
143
144
  //#endregion
144
- export { _ as GET_FILE_DOWNLOAD_URL, b as default, y as useFileDownloadUrl };
145
+ export { v as GET_FILE_DOWNLOAD_URL, x as default, b as useFileDownloadUrl };
145
146
 
146
147
  //# sourceMappingURL=FileUploadField.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileUploadField.js","names":[],"sources":["../../../src/cosmicintersection/components/FileUploadField.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useApolloClient, useQuery } from '@apollo/client/react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Download, FileUp, Loader2, Paperclip, X } from 'lucide-react';\nimport { useId, useRef, useState } from 'react';\n\nimport { notify } from '../lib/toast';\n\n/**\n * Generic artifact uploader backed by wspace-files-svc.\n *\n * Dataset/notebook/analysis blobs are NOT stored by this MFE — they live in\n * wspace-files as `File` records, and the Cos entities only hold the returned\n * `fileId` (CosDataset.fileId / CosNotebook.fileId / cosCompleteAnalysis.outputFileId).\n * This control performs the shared 3-step presigned upload (initiateUpload → PUT\n * → completeUpload) inline — the same protocol microfe-files uses — and hands the\n * resulting `fileId` back via `onChange`. It never keeps a local blob store and\n * accepts any file type.\n */\n\n// initiateUpload returns a presigned PUT URL + the upload session id.\nconst INITIATE_UPLOAD = gql`\n mutation CosInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n session {\n id\n }\n uploadUrl\n }\n }\n`;\n\n// completeUpload finalizes the session and returns the committed File id.\nconst COMPLETE_UPLOAD = gql`\n mutation CosCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file {\n id\n }\n }\n }\n`;\n\n// Presigned, time-limited download URL for a stored File — used to render a\n// \"Download\" affordance for an already-attached artifact.\nexport const GET_FILE_DOWNLOAD_URL = gql`\n query CosGetFileDownloadUrl($id: ID!, $expiresIn: Int) {\n getFileDownloadUrl(id: $id, expiresIn: $expiresIn)\n }\n`;\n\n// One hour — artifact links are resolved on demand when a detail page renders.\nconst DOWNLOAD_URL_TTL_SECONDS = 3_600;\n\ninterface InitiateUploadResponse {\n initiateUpload?: { session?: { id: string } | null; uploadUrl?: string | null } | null;\n}\n\ninterface CompleteUploadResponse {\n completeUpload?: { fileId?: string | null; file?: { id: string } | null } | null;\n}\n\ninterface DownloadUrlResponse {\n getFileDownloadUrl?: string | null;\n}\n\n/**\n * Resolve a presigned download URL for an attached File id. Skips when no id is\n * present. Detail pages use this to render a \"Download\" link for a dataset/\n * notebook/analysis artifact.\n */\nexport function useFileDownloadUrl(fileId: string | null | undefined) {\n return useQuery<DownloadUrlResponse, { id: string; expiresIn: number }>(GET_FILE_DOWNLOAD_URL, {\n variables: { id: fileId ?? '', expiresIn: DOWNLOAD_URL_TTL_SECONDS },\n skip: !fileId,\n fetchPolicy: 'no-cache',\n });\n}\n\nexport interface FileUploadFieldProps {\n /** Current attached wspace-files File id, if any. */\n value?: string | null;\n /** Called with the new File id once an upload completes, or '' when cleared. */\n onChange: (fileId: string) => void | Promise<void>;\n label: string;\n hint?: string;\n /** Optional wspace-files folder id; when omitted the file lands in the root. */\n folderId?: string;\n /** `accept` attribute for the file input; defaults to any file type. */\n accept?: string;\n disabled?: boolean;\n className?: string;\n}\n\nexport function FileUploadField({\n value,\n onChange,\n label,\n hint,\n folderId,\n accept,\n disabled = false,\n className,\n}: FileUploadFieldProps) {\n const client = useApolloClient();\n const inputRef = useRef<HTMLInputElement | null>(null);\n const inputId = useId();\n const [uploading, setUploading] = useState(false);\n const [fileName, setFileName] = useState<string | null>(null);\n\n const download = useFileDownloadUrl(uploading ? null : value);\n const downloadUrl = download.data?.getFileDownloadUrl ?? null;\n\n const handleSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the input so selecting the same file again re-triggers change.\n event.target.value = '';\n if (!file) {\n return;\n }\n\n setUploading(true);\n try {\n const extension = file.name.includes('.')\n ? file.name.substring(file.name.lastIndexOf('.') + 1)\n : '';\n const mimeType = file.type || 'application/octet-stream';\n\n const initiate = await client.mutate<InitiateUploadResponse>({\n mutation: INITIATE_UPLOAD,\n variables: {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n extension,\n mimeType,\n ...(folderId ? { folderId } : {}),\n },\n },\n });\n const session = initiate.data?.initiateUpload?.session;\n const uploadUrl = initiate.data?.initiateUpload?.uploadUrl;\n if (!session || !uploadUrl) {\n throw new Error(initiate.error?.message ?? 'Could not start the upload.');\n }\n\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`Upload failed with status ${xhr.status}`));\n }\n });\n xhr.addEventListener('error', () => reject(new Error('Upload network error')));\n xhr.addEventListener('abort', () => reject(new Error('Upload aborted')));\n xhr.open('PUT', uploadUrl);\n xhr.setRequestHeader('Content-Type', mimeType);\n xhr.send(file);\n });\n\n const complete = await client.mutate<CompleteUploadResponse>({\n mutation: COMPLETE_UPLOAD,\n variables: {\n input: {\n sessionId: session.id,\n ...(folderId ? { folderId } : {}),\n },\n },\n });\n const uploadedId =\n complete.data?.completeUpload?.fileId ?? complete.data?.completeUpload?.file?.id;\n if (!uploadedId) {\n throw new Error(complete.error?.message ?? 'The file was uploaded but not confirmed.');\n }\n\n setFileName(file.name);\n await onChange(uploadedId);\n notify.success('File uploaded');\n } catch (error) {\n notify.error('Upload failed', error instanceof Error ? error.message : 'Please try again.');\n } finally {\n setUploading(false);\n }\n };\n\n const clear = async () => {\n setFileName(null);\n await onChange('');\n };\n\n return (\n <div className={className}>\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex size-12 shrink-0 items-center justify-center rounded-card border border-dashed border-border-default bg-bg-sunken text-text-secondary\">\n <Paperclip className=\"size-5\" aria-hidden=\"true\" />\n </div>\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={disabled || uploading}\n onClick={() => inputRef.current?.click()}\n >\n {uploading ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <FileUp className=\"size-4\" />\n )}\n {uploading ? 'Uploading…' : value ? 'Replace file' : label}\n </Button>\n {value && !uploading ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void clear()}\n disabled={disabled}\n >\n <X className=\"size-4\" />\n Remove\n </Button>\n ) : null}\n </div>\n {value ? (\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <span className=\"max-w-[16rem] truncate\">{fileName ?? `File ${value}`}</span>\n {downloadUrl ? (\n <a\n href={downloadUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1 text-text-primary underline-offset-2 hover:underline\"\n >\n <Download className=\"size-3.5\" aria-hidden=\"true\" />\n Download\n </a>\n ) : null}\n </div>\n ) : hint ? (\n <p className=\"text-xs text-text-secondary\">{hint}</p>\n ) : null}\n </div>\n </div>\n <input\n ref={inputRef}\n id={inputId}\n type=\"file\"\n accept={accept}\n className=\"sr-only\"\n aria-label={label}\n onChange={(event) => void handleSelect(event)}\n />\n </div>\n );\n}\n\nexport default FileUploadField;\n"],"mappings":"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"}
1
+ {"version":3,"file":"FileUploadField.js","names":[],"sources":["../../../src/cosmicintersection/components/FileUploadField.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useApolloClient, useQuery } from '@apollo/client/react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Download, FileUp, Loader2, Paperclip, X } from 'lucide-react';\nimport { useId, useRef, useState } from 'react';\n\nimport { notify } from '../lib/toast';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Generic artifact uploader backed by wspace-files-svc.\n *\n * Dataset/notebook/analysis blobs are NOT stored by this MFE — they live in\n * wspace-files as `File` records, and the Cos entities only hold the returned\n * `fileId` (CosDataset.fileId / CosNotebook.fileId / cosCompleteAnalysis.outputFileId).\n * This control performs the shared 3-step presigned upload (initiateUpload → PUT\n * → completeUpload) inline — the same protocol microfe-files uses — and hands the\n * resulting `fileId` back via `onChange`. It never keeps a local blob store and\n * accepts any file type.\n */\n\n// initiateUpload returns a presigned PUT URL + the upload session id.\nconst INITIATE_UPLOAD = gql`\n mutation CosInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n session {\n id\n }\n uploadUrl\n }\n }\n`;\n\n// completeUpload finalizes the session and returns the committed File id.\nconst COMPLETE_UPLOAD = gql`\n mutation CosCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file {\n id\n }\n }\n }\n`;\n\n// Presigned, time-limited download URL for a stored File — used to render a\n// \"Download\" affordance for an already-attached artifact.\nexport const GET_FILE_DOWNLOAD_URL = gql`\n query CosGetFileDownloadUrl($id: ID!, $expiresIn: Int) {\n getFileDownloadUrl(id: $id, expiresIn: $expiresIn)\n }\n`;\n\n// One hour — artifact links are resolved on demand when a detail page renders.\nconst DOWNLOAD_URL_TTL_SECONDS = 3_600;\n\ninterface InitiateUploadResponse {\n initiateUpload?: { session?: { id: string } | null; uploadUrl?: string | null } | null;\n}\n\ninterface CompleteUploadResponse {\n completeUpload?: { fileId?: string | null; file?: { id: string } | null } | null;\n}\n\ninterface DownloadUrlResponse {\n getFileDownloadUrl?: string | null;\n}\n\n/**\n * Resolve a presigned download URL for an attached File id. Skips when no id is\n * present. Detail pages use this to render a \"Download\" link for a dataset/\n * notebook/analysis artifact.\n */\nexport function useFileDownloadUrl(fileId: string | null | undefined) {\n return useQuery<DownloadUrlResponse, { id: string; expiresIn: number }>(GET_FILE_DOWNLOAD_URL, {\n variables: { id: fileId ?? '', expiresIn: DOWNLOAD_URL_TTL_SECONDS },\n skip: !fileId,\n fetchPolicy: 'no-cache',\n });\n}\n\nexport interface FileUploadFieldProps {\n /** Current attached wspace-files File id, if any. */\n value?: string | null;\n /** Called with the new File id once an upload completes, or '' when cleared. */\n onChange: (fileId: string) => void | Promise<void>;\n label: string;\n hint?: string;\n /** Optional wspace-files folder id; when omitted the file lands in the root. */\n folderId?: string;\n /** `accept` attribute for the file input; defaults to any file type. */\n accept?: string;\n disabled?: boolean;\n className?: string;\n}\n\nexport function FileUploadField({\n value,\n onChange,\n label,\n hint,\n folderId,\n accept,\n disabled = false,\n className,\n}: FileUploadFieldProps) {\n const tr = useTr();\n const client = useApolloClient();\n const inputRef = useRef<HTMLInputElement | null>(null);\n const inputId = useId();\n const [uploading, setUploading] = useState(false);\n const [fileName, setFileName] = useState<string | null>(null);\n\n const download = useFileDownloadUrl(uploading ? null : value);\n const downloadUrl = download.data?.getFileDownloadUrl ?? null;\n\n const handleSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the input so selecting the same file again re-triggers change.\n event.target.value = '';\n if (!file) {\n return;\n }\n\n setUploading(true);\n try {\n const extension = file.name.includes('.')\n ? file.name.substring(file.name.lastIndexOf('.') + 1)\n : '';\n const mimeType = file.type || 'application/octet-stream';\n\n const initiate = await client.mutate<InitiateUploadResponse>({\n mutation: INITIATE_UPLOAD,\n variables: {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n extension,\n mimeType,\n ...(folderId ? { folderId } : {}),\n },\n },\n });\n const session = initiate.data?.initiateUpload?.session;\n const uploadUrl = initiate.data?.initiateUpload?.uploadUrl;\n if (!session || !uploadUrl) {\n throw new Error(\n initiate.error?.message ??\n tr('cosmicintersection.common.uploadStartError', 'Could not start the upload.')\n );\n }\n\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`Upload failed with status ${xhr.status}`));\n }\n });\n xhr.addEventListener('error', () => reject(new Error('Upload network error')));\n xhr.addEventListener('abort', () => reject(new Error('Upload aborted')));\n xhr.open('PUT', uploadUrl);\n xhr.setRequestHeader('Content-Type', mimeType);\n xhr.send(file);\n });\n\n const complete = await client.mutate<CompleteUploadResponse>({\n mutation: COMPLETE_UPLOAD,\n variables: {\n input: {\n sessionId: session.id,\n ...(folderId ? { folderId } : {}),\n },\n },\n });\n const uploadedId =\n complete.data?.completeUpload?.fileId ?? complete.data?.completeUpload?.file?.id;\n if (!uploadedId) {\n throw new Error(\n complete.error?.message ??\n tr(\n 'cosmicintersection.common.uploadConfirmError',\n 'The file was uploaded but not confirmed.'\n )\n );\n }\n\n setFileName(file.name);\n await onChange(uploadedId);\n notify.success(tr('cosmicintersection.common.uploadSuccess', 'File uploaded'));\n } catch (error) {\n notify.error(\n tr('cosmicintersection.common.uploadFailed', 'Upload failed'),\n error instanceof Error\n ? error.message\n : tr('cosmicintersection.common.uploadTryAgain', 'Please try again.')\n );\n } finally {\n setUploading(false);\n }\n };\n\n const clear = async () => {\n setFileName(null);\n await onChange('');\n };\n\n return (\n <div className={className}>\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex size-12 shrink-0 items-center justify-center rounded-card border border-dashed border-border-default bg-bg-sunken text-text-secondary\">\n <Paperclip className=\"size-5\" aria-hidden=\"true\" />\n </div>\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={disabled || uploading}\n onClick={() => inputRef.current?.click()}\n >\n {uploading ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <FileUp className=\"size-4\" />\n )}\n {uploading\n ? tr('cosmicintersection.common.uploading', 'Uploading…')\n : value\n ? tr('cosmicintersection.common.replaceFile', 'Replace file')\n : label}\n </Button>\n {value && !uploading ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void clear()}\n disabled={disabled}\n >\n <X className=\"size-4\" />\n {tr('cosmicintersection.common.remove', 'Remove')}\n </Button>\n ) : null}\n </div>\n {value ? (\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <span className=\"max-w-[16rem] truncate\">{fileName ?? `File ${value}`}</span>\n {downloadUrl ? (\n <a\n href={downloadUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1 text-text-primary underline-offset-2 hover:underline\"\n >\n <Download className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('cosmicintersection.common.download', 'Download')}\n </a>\n ) : null}\n </div>\n ) : hint ? (\n <p className=\"text-xs text-text-secondary\">{hint}</p>\n ) : null}\n </div>\n </div>\n <input\n ref={inputRef}\n id={inputId}\n type=\"file\"\n accept={accept}\n className=\"sr-only\"\n aria-label={label}\n onChange={(event) => void handleSelect(event)}\n />\n </div>\n );\n}\n\nexport default FileUploadField;\n"],"mappings":"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"}
@@ -10,6 +10,6 @@ export interface LoadingStateProps {
10
10
  * Consistent loading affordance for every CosmicIntersection query surface.
11
11
  * Token-styled only — no raw palette colours.
12
12
  */
13
- export declare function LoadingState({ variant, rows, label, className, }: LoadingStateProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function LoadingState({ variant, rows, label: labelProp, className, }: LoadingStateProps): import("react/jsx-runtime").JSX.Element;
14
14
  export default LoadingState;
15
15
  //# sourceMappingURL=LoadingState.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoadingState.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/LoadingState.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC7B,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAmB,EACnB,IAAQ,EACR,KAAkB,EAClB,SAAS,GACV,EAAE,iBAAiB,2CAqBnB;AAED,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"LoadingState.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/LoadingState.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC7B,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAmB,EACnB,IAAQ,EACR,KAAK,EAAE,SAAS,EAChB,SAAS,GACV,EAAE,iBAAiB,2CAuBnB;AAED,eAAe,YAAY,CAAC"}
@@ -1,23 +1,25 @@
1
- import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
- import { Skeleton as n, Spinner as r } from "@burdenoff/fe-libs/ui";
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ import { Skeleton as r, Spinner as i } from "@burdenoff/fe-libs/ui";
3
4
  //#region src/cosmicintersection/components/LoadingState.tsx
4
- function i({ variant: i = "spinner", rows: a = 5, label: o = "Loading…", className: s }) {
5
- return i === "rows" ? /* @__PURE__ */ e("div", {
6
- className: `space-y-3 ${s ?? ""}`,
5
+ function a({ variant: a = "spinner", rows: o = 5, label: s, className: c }) {
6
+ let l = e(), u = s ?? l("cosmicintersection.common.loading", "Loading…");
7
+ return a === "rows" ? /* @__PURE__ */ t("div", {
8
+ className: `space-y-3 ${c ?? ""}`,
7
9
  role: "status",
8
- "aria-label": o,
9
- children: Array.from({ length: a }).map((t, r) => /* @__PURE__ */ e(n, { className: "h-14 w-full rounded-card" }, r))
10
- }) : /* @__PURE__ */ t("div", {
11
- className: `flex min-h-[240px] flex-col items-center justify-center gap-3 ${s ?? ""}`,
10
+ "aria-label": u,
11
+ children: Array.from({ length: o }).map((e, n) => /* @__PURE__ */ t(r, { className: "h-14 w-full rounded-card" }, n))
12
+ }) : /* @__PURE__ */ n("div", {
13
+ className: `flex min-h-[240px] flex-col items-center justify-center gap-3 ${c ?? ""}`,
12
14
  role: "status",
13
- "aria-label": o,
14
- children: [/* @__PURE__ */ e(r, { className: "size-6 text-text-secondary" }), /* @__PURE__ */ e("p", {
15
+ "aria-label": u,
16
+ children: [/* @__PURE__ */ t(i, { className: "size-6 text-text-secondary" }), /* @__PURE__ */ t("p", {
15
17
  className: "text-sm text-text-secondary",
16
- children: o
18
+ children: u
17
19
  })]
18
20
  });
19
21
  }
20
22
  //#endregion
21
- export { i as default };
23
+ export { a as default };
22
24
 
23
25
  //# sourceMappingURL=LoadingState.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoadingState.js","names":[],"sources":["../../../src/cosmicintersection/components/LoadingState.tsx"],"sourcesContent":["import { Skeleton, Spinner } from '@burdenoff/fe-libs/ui';\n\nexport interface LoadingStateProps {\n /** `spinner` centres a spinner; `rows` renders shimmering list placeholders. */\n variant?: 'spinner' | 'rows';\n /** Number of placeholder rows for the `rows` variant. */\n rows?: number;\n label?: string;\n className?: string;\n}\n\n/**\n * Consistent loading affordance for every CosmicIntersection query surface.\n * Token-styled only — no raw palette colours.\n */\nexport function LoadingState({\n variant = 'spinner',\n rows = 5,\n label = 'Loading…',\n className,\n}: LoadingStateProps) {\n if (variant === 'rows') {\n return (\n <div className={`space-y-3 ${className ?? ''}`} role=\"status\" aria-label={label}>\n {Array.from({ length: rows }).map((_, index) => (\n <Skeleton key={index} className=\"h-14 w-full rounded-card\" />\n ))}\n </div>\n );\n }\n\n return (\n <div\n className={`flex min-h-[240px] flex-col items-center justify-center gap-3 ${className ?? ''}`}\n role=\"status\"\n aria-label={label}\n >\n <Spinner className=\"size-6 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">{label}</p>\n </div>\n );\n}\n\nexport default LoadingState;\n"],"mappings":";;;AAeA,SAAgB,EAAa,EAC3B,aAAU,WACV,UAAO,GACP,WAAQ,YACR,gBACoB;CAWpB,OAVI,MAAY,SAEZ,kBAAC,OAAD;EAAK,WAAW,aAAa,KAAa;EAAM,MAAK;EAAS,cAAY;YACvE,MAAM,KAAK,EAAE,QAAQ,EAAK,CAAC,EAAE,KAAK,GAAG,MACpC,kBAAC,GAAD,EAAsB,WAAU,2BAA4B,GAA7C,CAA6C,CAC7D;CACE,CAAA,IAKP,kBAAC,OAAD;EACE,WAAW,iEAAiE,KAAa;EACzF,MAAK;EACL,cAAY;YAHd,CAKE,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,GACjD,kBAAC,KAAD;GAAG,WAAU;aAA+B;EAAS,CAAA,CAClD;;AAET"}
1
+ {"version":3,"file":"LoadingState.js","names":[],"sources":["../../../src/cosmicintersection/components/LoadingState.tsx"],"sourcesContent":["import { Skeleton, Spinner } from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface LoadingStateProps {\n /** `spinner` centres a spinner; `rows` renders shimmering list placeholders. */\n variant?: 'spinner' | 'rows';\n /** Number of placeholder rows for the `rows` variant. */\n rows?: number;\n label?: string;\n className?: string;\n}\n\n/**\n * Consistent loading affordance for every CosmicIntersection query surface.\n * Token-styled only — no raw palette colours.\n */\nexport function LoadingState({\n variant = 'spinner',\n rows = 5,\n label: labelProp,\n className,\n}: LoadingStateProps) {\n const tr = useTr();\n const label = labelProp ?? tr('cosmicintersection.common.loading', 'Loading…');\n if (variant === 'rows') {\n return (\n <div className={`space-y-3 ${className ?? ''}`} role=\"status\" aria-label={label}>\n {Array.from({ length: rows }).map((_, index) => (\n <Skeleton key={index} className=\"h-14 w-full rounded-card\" />\n ))}\n </div>\n );\n }\n\n return (\n <div\n className={`flex min-h-[240px] flex-col items-center justify-center gap-3 ${className ?? ''}`}\n role=\"status\"\n aria-label={label}\n >\n <Spinner className=\"size-6 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">{label}</p>\n </div>\n );\n}\n\nexport default LoadingState;\n"],"mappings":";;;;AAgBA,SAAgB,EAAa,EAC3B,aAAU,WACV,UAAO,GACP,OAAO,GACP,gBACoB;CACpB,IAAM,IAAK,EAAM,GACX,IAAQ,KAAa,EAAG,qCAAqC,UAAU;CAW7E,OAVI,MAAY,SAEZ,kBAAC,OAAD;EAAK,WAAW,aAAa,KAAa;EAAM,MAAK;EAAS,cAAY;YACvE,MAAM,KAAK,EAAE,QAAQ,EAAK,CAAC,EAAE,KAAK,GAAG,MACpC,kBAAC,GAAD,EAAsB,WAAU,2BAA4B,GAA7C,CAA6C,CAC7D;CACE,CAAA,IAKP,kBAAC,OAAD;EACE,WAAW,iEAAiE,KAAa;EACzF,MAAK;EACL,cAAY;YAHd,CAKE,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,GACjD,kBAAC,KAAD;GAAG,WAAU;aAA+B;EAAS,CAAA,CAClD;;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"PageLayout.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/PageLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAkC9F;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"PageLayout.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/PageLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAmC9F;AAED,eAAe,UAAU,CAAC"}
@@ -1,46 +1,47 @@
1
1
  import { useCosmicIntersection as e } from "../context/CosmicIntersectionContext.js";
2
- import { cn as t } from "../utils/cn.js";
3
- import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
- import { ChevronLeft as i } from "lucide-react";
2
+ import { useTr as t } from "../../shared/hooks/useTr.js";
3
+ import { cn as n } from "../utils/cn.js";
4
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
+ import { ChevronLeft as a } from "lucide-react";
5
6
  //#region src/cosmicintersection/components/PageLayout.tsx
6
- function a({ title: a, description: o, actions: s, children: c, backPath: l }) {
7
- let { navigate: u } = e();
8
- return /* @__PURE__ */ r("div", {
9
- className: t("flex h-full flex-col bg-bg-surface"),
10
- children: [/* @__PURE__ */ r("div", {
11
- className: t("flex items-start justify-between gap-4 border-b border-border-seam bg-bg-surface px-6 py-5"),
12
- children: [/* @__PURE__ */ r("div", {
7
+ function o({ title: o, description: s, actions: c, children: l, backPath: u }) {
8
+ let { navigate: d } = e(), f = t();
9
+ return /* @__PURE__ */ i("div", {
10
+ className: n("flex h-full flex-col bg-bg-surface"),
11
+ children: [/* @__PURE__ */ i("div", {
12
+ className: n("flex items-start justify-between gap-4 border-b border-border-seam bg-bg-surface px-6 py-5"),
13
+ children: [/* @__PURE__ */ i("div", {
13
14
  className: "flex min-w-0 flex-col gap-1",
14
15
  children: [
15
- l ? /* @__PURE__ */ r("button", {
16
+ u ? /* @__PURE__ */ i("button", {
16
17
  type: "button",
17
- onClick: () => u(l),
18
- className: t("flex w-fit items-center gap-1 rounded-button text-sm text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus"),
19
- children: [/* @__PURE__ */ n(i, {
18
+ onClick: () => d(u),
19
+ className: n("flex w-fit items-center gap-1 rounded-button text-sm text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus"),
20
+ children: [/* @__PURE__ */ r(a, {
20
21
  className: "size-4",
21
22
  "aria-hidden": "true"
22
- }), "Back"]
23
+ }), f("cosmicintersection.common.back", "Back")]
23
24
  }) : null,
24
- /* @__PURE__ */ n("h1", {
25
- className: t("truncate text-2xl font-semibold text-text-primary"),
26
- children: a
27
- }),
28
- o ? /* @__PURE__ */ n("p", {
29
- className: t("mt-0.5 text-sm text-text-secondary"),
25
+ /* @__PURE__ */ r("h1", {
26
+ className: n("truncate text-2xl font-semibold text-text-primary"),
30
27
  children: o
28
+ }),
29
+ s ? /* @__PURE__ */ r("p", {
30
+ className: n("mt-0.5 text-sm text-text-secondary"),
31
+ children: s
31
32
  }) : null
32
33
  ]
33
- }), s ? /* @__PURE__ */ n("div", {
34
+ }), c ? /* @__PURE__ */ r("div", {
34
35
  className: "flex shrink-0 items-center gap-2",
35
- children: s
36
+ children: c
36
37
  }) : null]
37
- }), /* @__PURE__ */ n("div", {
38
- className: t("flex-1 overflow-auto p-6"),
39
- children: c
38
+ }), /* @__PURE__ */ r("div", {
39
+ className: n("flex-1 overflow-auto p-6"),
40
+ children: l
40
41
  })]
41
42
  });
42
43
  }
43
44
  //#endregion
44
- export { a as default };
45
+ export { o as default };
45
46
 
46
47
  //# sourceMappingURL=PageLayout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/cosmicintersection/components/PageLayout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { ChevronLeft } from 'lucide-react';\nimport { useCosmicIntersection } from '../context/CosmicIntersectionContext';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n title: ReactNode;\n description?: ReactNode;\n actions?: ReactNode;\n children: ReactNode;\n /** basePath-relative path for the header back button (e.g. \"datasets\"). */\n backPath?: string;\n}\n\n/**\n * Standard page scaffold: a sticky-feeling header (optional back button, title,\n * description, right-aligned actions) over a scrollable content region. Token\n * styling only.\n */\nexport function PageLayout({ title, description, actions, children, backPath }: PageLayoutProps) {\n const { navigate } = useCosmicIntersection();\n\n return (\n <div className={cn('flex h-full flex-col bg-bg-surface')}>\n <div\n className={cn(\n 'flex items-start justify-between gap-4 border-b border-border-seam bg-bg-surface px-6 py-5'\n )}\n >\n <div className=\"flex min-w-0 flex-col gap-1\">\n {backPath ? (\n <button\n type=\"button\"\n onClick={() => navigate(backPath)}\n className={cn(\n 'flex w-fit items-center gap-1 rounded-button text-sm text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus'\n )}\n >\n <ChevronLeft className=\"size-4\" aria-hidden=\"true\" />\n Back\n </button>\n ) : null}\n <h1 className={cn('truncate text-2xl font-semibold text-text-primary')}>{title}</h1>\n {description ? (\n <p className={cn('mt-0.5 text-sm text-text-secondary')}>{description}</p>\n ) : null}\n </div>\n {actions ? <div className=\"flex shrink-0 items-center gap-2\">{actions}</div> : null}\n </div>\n\n <div className={cn('flex-1 overflow-auto p-6')}>{children}</div>\n </div>\n );\n}\n\nexport default PageLayout;\n"],"mappings":";;;;;AAmBA,SAAgB,EAAW,EAAE,UAAO,gBAAa,YAAS,aAAU,eAA6B;CAC/F,IAAM,EAAE,gBAAa,EAAsB;CAE3C,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,oCAAoC;YAAvD,CACE,kBAAC,OAAD;GACE,WAAW,EACT,4FACF;aAHF,CAKE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,IACC,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAS,CAAQ;MAChC,WAAW,EACT,oMACF;gBALF,CAOE,kBAAC,GAAD;OAAa,WAAU;OAAS,eAAY;MAAQ,CAAA,GAAC,MAE/C;UACN;KACJ,kBAAC,MAAD;MAAI,WAAW,EAAG,mDAAmD;gBAAI;KAAU,CAAA;KAClF,IACC,kBAAC,KAAD;MAAG,WAAW,EAAG,oCAAoC;gBAAI;KAAe,CAAA,IACtE;IACD;OACJ,IAAU,kBAAC,OAAD;IAAK,WAAU;cAAoC;GAAa,CAAA,IAAI,IAC5E;MAEL,kBAAC,OAAD;GAAK,WAAW,EAAG,0BAA0B;GAAI;EAAc,CAAA,CAC5D;;AAET"}
1
+ {"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/cosmicintersection/components/PageLayout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { ChevronLeft } from 'lucide-react';\nimport { useCosmicIntersection } from '../context/CosmicIntersectionContext';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n title: ReactNode;\n description?: ReactNode;\n actions?: ReactNode;\n children: ReactNode;\n /** basePath-relative path for the header back button (e.g. \"datasets\"). */\n backPath?: string;\n}\n\n/**\n * Standard page scaffold: a sticky-feeling header (optional back button, title,\n * description, right-aligned actions) over a scrollable content region. Token\n * styling only.\n */\nexport function PageLayout({ title, description, actions, children, backPath }: PageLayoutProps) {\n const { navigate } = useCosmicIntersection();\n const tr = useTr();\n\n return (\n <div className={cn('flex h-full flex-col bg-bg-surface')}>\n <div\n className={cn(\n 'flex items-start justify-between gap-4 border-b border-border-seam bg-bg-surface px-6 py-5'\n )}\n >\n <div className=\"flex min-w-0 flex-col gap-1\">\n {backPath ? (\n <button\n type=\"button\"\n onClick={() => navigate(backPath)}\n className={cn(\n 'flex w-fit items-center gap-1 rounded-button text-sm text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus'\n )}\n >\n <ChevronLeft className=\"size-4\" aria-hidden=\"true\" />\n {tr('cosmicintersection.common.back', 'Back')}\n </button>\n ) : null}\n <h1 className={cn('truncate text-2xl font-semibold text-text-primary')}>{title}</h1>\n {description ? (\n <p className={cn('mt-0.5 text-sm text-text-secondary')}>{description}</p>\n ) : null}\n </div>\n {actions ? <div className=\"flex shrink-0 items-center gap-2\">{actions}</div> : null}\n </div>\n\n <div className={cn('flex-1 overflow-auto p-6')}>{children}</div>\n </div>\n );\n}\n\nexport default PageLayout;\n"],"mappings":";;;;;;AAoBA,SAAgB,EAAW,EAAE,UAAO,gBAAa,YAAS,aAAU,eAA6B;CAC/F,IAAM,EAAE,gBAAa,EAAsB,GACrC,IAAK,EAAM;CAEjB,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,oCAAoC;YAAvD,CACE,kBAAC,OAAD;GACE,WAAW,EACT,4FACF;aAHF,CAKE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,IACC,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAS,CAAQ;MAChC,WAAW,EACT,oMACF;gBALF,CAOE,kBAAC,GAAD;OAAa,WAAU;OAAS,eAAY;MAAQ,CAAA,GACnD,EAAG,kCAAkC,MAAM,CACtC;UACN;KACJ,kBAAC,MAAD;MAAI,WAAW,EAAG,mDAAmD;gBAAI;KAAU,CAAA;KAClF,IACC,kBAAC,KAAD;MAAG,WAAW,EAAG,oCAAoC;gBAAI;KAAe,CAAA,IACtE;IACD;OACJ,IAAU,kBAAC,OAAD;IAAK,WAAU;cAAoC;GAAa,CAAA,IAAI,IAC5E;MAEL,kBAAC,OAAD;GAAK,WAAW,EAAG,0BAA0B;GAAI;EAAc,CAAA,CAC5D;;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/Pagination.tsx"],"names":[],"mappings":"AAgBA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAsBrF;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAmB,EACnB,OAAe,EACf,SAAS,GACV,EAAE,eAAe,kDA8CjB;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/Pagination.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAsBrF;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAmB,EACnB,OAAe,EACf,SAAS,GACV,EAAE,eAAe,kDAqDjB;AAED,eAAe,UAAU,CAAC"}
@@ -1,12 +1,13 @@
1
- import { useState as e } from "react";
2
- import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
- import { ChevronLeft as r, ChevronRight as i } from "lucide-react";
4
- import { Button as a } from "@burdenoff/fe-libs/ui";
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { useState as t } from "react";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ import { ChevronLeft as i, ChevronRight as a } from "lucide-react";
5
+ import { Button as o } from "@burdenoff/fe-libs/ui";
5
6
  //#region src/cosmicintersection/components/Pagination.tsx
6
- var o = 50;
7
- function s(t = {}) {
8
- let { pageSize: n = o, resetKey: r } = t, [i, a] = e(0), [s, c] = e(r);
9
- return s !== r && (c(r), i !== 0 && a(0)), {
7
+ var s = 50;
8
+ function c(e = {}) {
9
+ let { pageSize: n = s, resetKey: r } = e, [i, a] = t(0), [o, c] = t(r);
10
+ return o !== r && (c(r), i !== 0 && a(0)), {
10
11
  limit: n,
11
12
  offset: i,
12
13
  page: Math.floor(i / Math.max(1, n)) + 1,
@@ -15,51 +16,53 @@ function s(t = {}) {
15
16
  reset: () => a(0)
16
17
  };
17
18
  }
18
- function c({ offset: e, limit: o, loadedCount: s, hasMore: c, onPrev: l, onNext: u, itemLabel: d = "items", loading: f = !1, className: p }) {
19
- if (e === 0 && !c && s === 0) return null;
20
- let m = s === 0 ? 0 : e + 1, h = e + s, g = e > 0, _ = c, v = Math.floor(e / Math.max(1, o)) + 1;
21
- return /* @__PURE__ */ n("div", {
22
- className: `flex flex-wrap items-center justify-between gap-3 pt-4 ${p ?? ""}`,
19
+ function l({ offset: t, limit: s, loadedCount: c, hasMore: l, onPrev: u, onNext: d, itemLabel: f = "items", loading: p = !1, className: m }) {
20
+ let h = e();
21
+ if (t === 0 && !l && c === 0) return null;
22
+ let g = c === 0 ? 0 : t + 1, _ = t + c, v = t > 0, y = l, b = Math.floor(t / Math.max(1, s)) + 1;
23
+ return /* @__PURE__ */ r("div", {
24
+ className: `flex flex-wrap items-center justify-between gap-3 pt-4 ${m ?? ""}`,
23
25
  role: "navigation",
24
26
  "aria-label": "Pagination",
25
27
  children: [/* @__PURE__ */ n("p", {
26
28
  className: "text-xs tabular-nums text-text-secondary",
27
- children: [
28
- "Showing ",
29
- m,
30
- "–",
31
- h,
32
- " ",
33
- d
34
- ]
35
- }), /* @__PURE__ */ n("div", {
29
+ children: h("cosmicintersection.common.showingResults", "Showing {{from}}–{{to}} {{itemLabel}}", {
30
+ from: g,
31
+ to: _,
32
+ itemLabel: f
33
+ })
34
+ }), /* @__PURE__ */ r("div", {
36
35
  className: "flex items-center gap-2",
37
36
  children: [
38
- /* @__PURE__ */ n("span", {
37
+ /* @__PURE__ */ r("span", {
39
38
  className: "text-xs tabular-nums text-text-secondary",
40
- children: ["Page ", v]
39
+ children: [
40
+ h("cosmicintersection.common.page", "Page"),
41
+ " ",
42
+ b
43
+ ]
41
44
  }),
42
- /* @__PURE__ */ n(a, {
45
+ /* @__PURE__ */ r(o, {
43
46
  size: "sm",
44
47
  variant: "outline",
45
- onClick: l,
46
- disabled: !g || f,
47
- "aria-label": "Previous page",
48
- children: [/* @__PURE__ */ t(r, { className: "size-4" }), "Prev"]
48
+ onClick: u,
49
+ disabled: !v || p,
50
+ "aria-label": h("cosmicintersection.common.prev", "Previous page"),
51
+ children: [/* @__PURE__ */ n(i, { className: "size-4" }), h("cosmicintersection.common.prev", "Prev")]
49
52
  }),
50
- /* @__PURE__ */ n(a, {
53
+ /* @__PURE__ */ r(o, {
51
54
  size: "sm",
52
55
  variant: "outline",
53
- onClick: u,
54
- disabled: !_ || f,
55
- "aria-label": "Next page",
56
- children: ["Next", /* @__PURE__ */ t(i, { className: "size-4" })]
56
+ onClick: d,
57
+ disabled: !y || p,
58
+ "aria-label": h("cosmicintersection.common.next", "Next page"),
59
+ children: [h("cosmicintersection.common.next", "Next"), /* @__PURE__ */ n(a, { className: "size-4" })]
57
60
  })
58
61
  ]
59
62
  })]
60
63
  });
61
64
  }
62
65
  //#endregion
63
- export { c as default, s as usePagination };
66
+ export { l as default, c as usePagination };
64
67
 
65
68
  //# sourceMappingURL=Pagination.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/cosmicintersection/components/Pagination.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\n\n/**\n * Offset/limit pagination for the CosmicIntersection list surfaces.\n *\n * The Cos* connection resolvers return `{ items, pageInfo { hasMore, cursor } }`\n * with NO total count, so paging is driven by `offset`/`limit` + the boolean\n * `hasMore` rather than a computable page count. `usePagination` owns the window;\n * the page feeds `limit`/`offset` into `PaginationInput`, then renders\n * `<Pagination>` with `hasMore` so Next gates correctly.\n */\n\nconst DEFAULT_PAGE_SIZE = 50;\n\nexport interface UsePaginationOptions {\n pageSize?: number;\n /** When this changes (a filter/search term), the window resets to page 1. */\n resetKey?: unknown;\n}\n\nexport interface UsePaginationResult {\n limit: number;\n offset: number;\n /** 1-based page index derived from offset/limit. */\n page: number;\n nextPage: () => void;\n prevPage: () => void;\n reset: () => void;\n}\n\nexport function usePagination(options: UsePaginationOptions = {}): UsePaginationResult {\n const { pageSize = DEFAULT_PAGE_SIZE, resetKey } = options;\n const [offset, setOffset] = useState(0);\n\n // Reset to the first page whenever resetKey changes (React's documented\n // derive-state-during-render pattern — no extra effect/render pass).\n const [prevResetKey, setPrevResetKey] = useState(resetKey);\n if (prevResetKey !== resetKey) {\n setPrevResetKey(resetKey);\n if (offset !== 0) {\n setOffset(0);\n }\n }\n\n return {\n limit: pageSize,\n offset,\n page: Math.floor(offset / Math.max(1, pageSize)) + 1,\n nextPage: () => setOffset((current) => current + pageSize),\n prevPage: () => setOffset((current) => Math.max(0, current - pageSize)),\n reset: () => setOffset(0),\n };\n}\n\nexport interface PaginationProps {\n offset: number;\n limit: number;\n /** Number of rows returned for the current page (`items.length`). */\n loadedCount: number;\n /** Whether the backend reports more rows after this page (`pageInfo.hasMore`). */\n hasMore: boolean;\n onPrev: () => void;\n onNext: () => void;\n /** Plural noun for the range label, e.g. \"datasets\". Defaults to \"items\". */\n itemLabel?: string;\n loading?: boolean;\n className?: string;\n}\n\nexport function Pagination({\n offset,\n limit,\n loadedCount,\n hasMore,\n onPrev,\n onNext,\n itemLabel = 'items',\n loading = false,\n className,\n}: PaginationProps) {\n // Nothing to page through: first page and no more results.\n if (offset === 0 && !hasMore && loadedCount === 0) {\n return null;\n }\n\n const rangeStart = loadedCount === 0 ? 0 : offset + 1;\n const rangeEnd = offset + loadedCount;\n const canPrev = offset > 0;\n const canNext = hasMore;\n const page = Math.floor(offset / Math.max(1, limit)) + 1;\n\n return (\n <div\n className={`flex flex-wrap items-center justify-between gap-3 pt-4 ${className ?? ''}`}\n role=\"navigation\"\n aria-label=\"Pagination\"\n >\n <p className=\"text-xs tabular-nums text-text-secondary\">\n Showing {rangeStart}–{rangeEnd} {itemLabel}\n </p>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs tabular-nums text-text-secondary\">Page {page}</span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={onPrev}\n disabled={!canPrev || loading}\n aria-label=\"Previous page\"\n >\n <ChevronLeft className=\"size-4\" />\n Prev\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={onNext}\n disabled={!canNext || loading}\n aria-label=\"Next page\"\n >\n Next\n <ChevronRight className=\"size-4\" />\n </Button>\n </div>\n </div>\n );\n}\n\nexport default Pagination;\n"],"mappings":";;;;;AAcA,IAAM,IAAoB;AAkB1B,SAAgB,EAAc,IAAgC,CAAC,GAAwB;CACrF,IAAM,EAAE,cAAW,GAAmB,gBAAa,GAC7C,CAAC,GAAQ,KAAa,EAAS,CAAC,GAIhC,CAAC,GAAc,KAAmB,EAAS,CAAQ;CAQzD,OAPI,MAAiB,MACnB,EAAgB,CAAQ,GACpB,MAAW,KACb,EAAU,CAAC,IAIR;EACL,OAAO;EACP;EACA,MAAM,KAAK,MAAM,IAAS,KAAK,IAAI,GAAG,CAAQ,CAAC,IAAI;EACnD,gBAAgB,GAAW,MAAY,IAAU,CAAQ;EACzD,gBAAgB,GAAW,MAAY,KAAK,IAAI,GAAG,IAAU,CAAQ,CAAC;EACtE,aAAa,EAAU,CAAC;CAC1B;AACF;AAiBA,SAAgB,EAAW,EACzB,WACA,UACA,gBACA,YACA,WACA,WACA,eAAY,SACZ,aAAU,IACV,gBACkB;CAElB,IAAI,MAAW,KAAK,CAAC,KAAW,MAAgB,GAC9C,OAAO;CAGT,IAAM,IAAa,MAAgB,IAAI,IAAI,IAAS,GAC9C,IAAW,IAAS,GACpB,IAAU,IAAS,GACnB,IAAU,GACV,IAAO,KAAK,MAAM,IAAS,KAAK,IAAI,GAAG,CAAK,CAAC,IAAI;CAEvD,OACE,kBAAC,OAAD;EACE,WAAW,0DAA0D,KAAa;EAClF,MAAK;EACL,cAAW;YAHb,CAKE,kBAAC,KAAD;GAAG,WAAU;aAAb;IAAwD;IAC7C;IAAW;IAAE;IAAS;IAAE;GAChC;MACH,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAA2D,SAAM,CAAW;;IAC5E,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,SAAS;KACT,UAAU,CAAC,KAAW;KACtB,cAAW;eALb,CAOE,kBAAC,GAAD,EAAa,WAAU,SAAU,CAAA,GAAC,MAE5B;;IACR,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,SAAS;KACT,UAAU,CAAC,KAAW;KACtB,cAAW;eALb,CAMC,QAEC,kBAAC,GAAD,EAAc,WAAU,SAAU,CAAA,CAC5B;;GACL;IACF;;AAET"}
1
+ {"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/cosmicintersection/components/Pagination.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Offset/limit pagination for the CosmicIntersection list surfaces.\n *\n * The Cos* connection resolvers return `{ items, pageInfo { hasMore, cursor } }`\n * with NO total count, so paging is driven by `offset`/`limit` + the boolean\n * `hasMore` rather than a computable page count. `usePagination` owns the window;\n * the page feeds `limit`/`offset` into `PaginationInput`, then renders\n * `<Pagination>` with `hasMore` so Next gates correctly.\n */\n\nconst DEFAULT_PAGE_SIZE = 50;\n\nexport interface UsePaginationOptions {\n pageSize?: number;\n /** When this changes (a filter/search term), the window resets to page 1. */\n resetKey?: unknown;\n}\n\nexport interface UsePaginationResult {\n limit: number;\n offset: number;\n /** 1-based page index derived from offset/limit. */\n page: number;\n nextPage: () => void;\n prevPage: () => void;\n reset: () => void;\n}\n\nexport function usePagination(options: UsePaginationOptions = {}): UsePaginationResult {\n const { pageSize = DEFAULT_PAGE_SIZE, resetKey } = options;\n const [offset, setOffset] = useState(0);\n\n // Reset to the first page whenever resetKey changes (React's documented\n // derive-state-during-render pattern — no extra effect/render pass).\n const [prevResetKey, setPrevResetKey] = useState(resetKey);\n if (prevResetKey !== resetKey) {\n setPrevResetKey(resetKey);\n if (offset !== 0) {\n setOffset(0);\n }\n }\n\n return {\n limit: pageSize,\n offset,\n page: Math.floor(offset / Math.max(1, pageSize)) + 1,\n nextPage: () => setOffset((current) => current + pageSize),\n prevPage: () => setOffset((current) => Math.max(0, current - pageSize)),\n reset: () => setOffset(0),\n };\n}\n\nexport interface PaginationProps {\n offset: number;\n limit: number;\n /** Number of rows returned for the current page (`items.length`). */\n loadedCount: number;\n /** Whether the backend reports more rows after this page (`pageInfo.hasMore`). */\n hasMore: boolean;\n onPrev: () => void;\n onNext: () => void;\n /** Plural noun for the range label, e.g. \"datasets\". Defaults to \"items\". */\n itemLabel?: string;\n loading?: boolean;\n className?: string;\n}\n\nexport function Pagination({\n offset,\n limit,\n loadedCount,\n hasMore,\n onPrev,\n onNext,\n itemLabel = 'items',\n loading = false,\n className,\n}: PaginationProps) {\n const tr = useTr();\n // Nothing to page through: first page and no more results.\n if (offset === 0 && !hasMore && loadedCount === 0) {\n return null;\n }\n\n const rangeStart = loadedCount === 0 ? 0 : offset + 1;\n const rangeEnd = offset + loadedCount;\n const canPrev = offset > 0;\n const canNext = hasMore;\n const page = Math.floor(offset / Math.max(1, limit)) + 1;\n\n return (\n <div\n className={`flex flex-wrap items-center justify-between gap-3 pt-4 ${className ?? ''}`}\n role=\"navigation\"\n aria-label=\"Pagination\"\n >\n <p className=\"text-xs tabular-nums text-text-secondary\">\n {tr('cosmicintersection.common.showingResults', 'Showing {{from}}–{{to}} {{itemLabel}}', {\n from: rangeStart,\n to: rangeEnd,\n itemLabel,\n })}\n </p>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs tabular-nums text-text-secondary\">\n {tr('cosmicintersection.common.page', 'Page')} {page}\n </span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={onPrev}\n disabled={!canPrev || loading}\n aria-label={tr('cosmicintersection.common.prev', 'Previous page')}\n >\n <ChevronLeft className=\"size-4\" />\n {tr('cosmicintersection.common.prev', 'Prev')}\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={onNext}\n disabled={!canNext || loading}\n aria-label={tr('cosmicintersection.common.next', 'Next page')}\n >\n {tr('cosmicintersection.common.next', 'Next')}\n <ChevronRight className=\"size-4\" />\n </Button>\n </div>\n </div>\n );\n}\n\nexport default Pagination;\n"],"mappings":";;;;;;AAeA,IAAM,IAAoB;AAkB1B,SAAgB,EAAc,IAAgC,CAAC,GAAwB;CACrF,IAAM,EAAE,cAAW,GAAmB,gBAAa,GAC7C,CAAC,GAAQ,KAAa,EAAS,CAAC,GAIhC,CAAC,GAAc,KAAmB,EAAS,CAAQ;CAQzD,OAPI,MAAiB,MACnB,EAAgB,CAAQ,GACpB,MAAW,KACb,EAAU,CAAC,IAIR;EACL,OAAO;EACP;EACA,MAAM,KAAK,MAAM,IAAS,KAAK,IAAI,GAAG,CAAQ,CAAC,IAAI;EACnD,gBAAgB,GAAW,MAAY,IAAU,CAAQ;EACzD,gBAAgB,GAAW,MAAY,KAAK,IAAI,GAAG,IAAU,CAAQ,CAAC;EACtE,aAAa,EAAU,CAAC;CAC1B;AACF;AAiBA,SAAgB,EAAW,EACzB,WACA,UACA,gBACA,YACA,WACA,WACA,eAAY,SACZ,aAAU,IACV,gBACkB;CAClB,IAAM,IAAK,EAAM;CAEjB,IAAI,MAAW,KAAK,CAAC,KAAW,MAAgB,GAC9C,OAAO;CAGT,IAAM,IAAa,MAAgB,IAAI,IAAI,IAAS,GAC9C,IAAW,IAAS,GACpB,IAAU,IAAS,GACnB,IAAU,GACV,IAAO,KAAK,MAAM,IAAS,KAAK,IAAI,GAAG,CAAK,CAAC,IAAI;CAEvD,OACE,kBAAC,OAAD;EACE,WAAW,0DAA0D,KAAa;EAClF,MAAK;EACL,cAAW;YAHb,CAKE,kBAAC,KAAD;GAAG,WAAU;aACV,EAAG,4CAA4C,yCAAyC;IACvF,MAAM;IACN,IAAI;IACJ;GACF,CAAC;EACA,CAAA,GACH,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,EAAG,kCAAkC,MAAM;MAAE;MAAE;KAC5C;;IACN,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,SAAS;KACT,UAAU,CAAC,KAAW;KACtB,cAAY,EAAG,kCAAkC,eAAe;eALlE,CAOE,kBAAC,GAAD,EAAa,WAAU,SAAU,CAAA,GAChC,EAAG,kCAAkC,MAAM,CACtC;;IACR,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,SAAS;KACT,UAAU,CAAC,KAAW;KACtB,cAAY,EAAG,kCAAkC,WAAW;eAL9D,CAOG,EAAG,kCAAkC,MAAM,GAC5C,kBAAC,GAAD,EAAc,WAAU,SAAU,CAAA,CAC5B;;GACL;IACF;;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/StatusBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAiD9E,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,gBAAgB,2CAS/E;AAED,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/StatusBadge.tsx"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAiD9E,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD;AAuBD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,gBAAgB,2CAc/E;AAED,eAAe,WAAW,CAAC"}