@burdenoff/microfe-assethandler 2026.1110.1 → 2026.1111.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.
@@ -5,6 +5,29 @@
5
5
  * `initiateUpload` → presigned PUT → `completeUpload` flow, and mints public
6
6
  * share links via `createFileShareLink`. All entries are live data.
7
7
  *
8
+ * Sharing follows the files service's rules, which depend on visibility:
9
+ *
10
+ * - PRIVATE and TEAM files can only be shared by their uploader or someone
11
+ * holding a SHARE/MANAGE grant or a MANAGER share. For these the panel asks
12
+ * `getUserFilePermissions(fileId).canShare` before minting a link. The check
13
+ * runs lazily (on each desktop click and each time the mobile menu opens), so
14
+ * a long file list never fans out into one permission query per row.
15
+ * - WORKSPACE and PUBLIC files are governed only by the gateway's
16
+ * `@rbac(share, file)` check. `canShare` reflects explicit grants alone for
17
+ * these files, so it is not consulted: the panel calls `createFileShareLink`
18
+ * directly and treats its FORBIDDEN as the denial.
19
+ *
20
+ * The two denials arrive differently and get different copy. The gateway's
21
+ * role check answers HTTP 403 (an Apollo `ServerError`), so the caller is told
22
+ * their workspace role cannot share. The service's per-file rule answers HTTP
23
+ * 200 with a `FORBIDDEN` GraphQL error, so the caller is told who can share
24
+ * the file. A 503 `RBAC_SERVICE_UNAVAILABLE` is an outage, not a denial.
25
+ *
26
+ * `getUserFilePermissions` needs its own `@rbac(read, file-permission)`, which
27
+ * some roles lack. A FORBIDDEN from that query, from either source, means
28
+ * "unknown", never "cannot share": the panel falls through and lets the share
29
+ * mutation decide.
30
+ *
8
31
  * `entityType`/`entityId` scope the panel to the record it is mounted on and
9
32
  * are used for context labelling; uploaded files land in the workspace file
10
33
  * store so they can be shared and reused.
@@ -1,12 +1,13 @@
1
1
  import { notify as e } from "../utils/toast.js";
2
- import { useMemo as t, useRef as n, useState as r } from "react";
3
- import { Download as i, File as a, FileArchive as o, FileAudio as s, FileCode2 as c, FileSpreadsheet as l, FileText as u, FileVideo as d, Image as f, Link2 as p, Loader2 as m, MoreHorizontal as h, Paperclip as g, Upload as _ } from "lucide-react";
4
- import { gql as v } from "@apollo/client";
5
- import { useMutation as y, useQuery as b } from "@apollo/client/react";
6
- import { jsx as x, jsxs as S } from "react/jsx-runtime";
7
- import { Button as C, Card as w, CardContent as T, CardHeader as E, CardTitle as D, DropdownMenu as O, DropdownMenuContent as k, DropdownMenuItem as A, DropdownMenuTrigger as j, IllustratedEmptyState as M } from "@burdenoff/fe-libs/ui";
2
+ import { getForbiddenSource as t, isForbiddenError as n } from "../utils/graphqlErrors.js";
3
+ import { useCallback as r, useMemo as i, useRef as a, useState as o } from "react";
4
+ import { Download as s, File as c, FileArchive as l, FileAudio as u, FileCode2 as d, FileSpreadsheet as f, FileText as p, FileVideo as m, Image as h, Link2 as g, Loader2 as _, Lock as v, MoreHorizontal as y, Paperclip as b, Upload as ee } from "lucide-react";
5
+ import { gql as x } from "@apollo/client";
6
+ import { useApolloClient as te, useMutation as S, useQuery as C } from "@apollo/client/react";
7
+ import { jsx as w, jsxs as T } from "react/jsx-runtime";
8
+ import { Button as ne, Card as E, CardContent as D, CardHeader as O, CardTitle as k, DropdownMenu as A, DropdownMenuContent as j, DropdownMenuItem as M, DropdownMenuTrigger as re, IllustratedEmptyState as ie } from "@burdenoff/fe-libs/ui";
8
9
  // src/assethandler/components/AttachmentsPanel.tsx
9
- var N = v`
10
+ var ae = x`
10
11
  query AssetHandlerGetFiles($pagination: PaginationInput, $sort: FileSortInput) {
11
12
  getFiles(pagination: $pagination, sort: $sort) {
12
13
  total
@@ -19,10 +20,11 @@ var N = v`
19
20
  size
20
21
  downloadUrl
21
22
  createdAt
23
+ visibility
22
24
  }
23
25
  }
24
26
  }
25
- `, P = v`
27
+ `, oe = x`
26
28
  mutation AssetHandlerInitiateUpload($input: InitiateUploadInput!) {
27
29
  initiateUpload(input: $input) {
28
30
  uploadUrl
@@ -31,7 +33,7 @@ var N = v`
31
33
  }
32
34
  }
33
35
  }
34
- `, F = v`
36
+ `, se = x`
35
37
  mutation AssetHandlerCompleteUpload($input: CompleteUploadInput!) {
36
38
  completeUpload(input: $input) {
37
39
  fileId
@@ -41,15 +43,40 @@ var N = v`
41
43
  }
42
44
  }
43
45
  }
44
- `, I = v`
46
+ `, ce = x`
45
47
  mutation AssetHandlerCreateFileShareLink($input: CreateShareGrantLinkInput!) {
46
48
  createFileShareLink(input: $input) {
47
49
  id
48
50
  shareUrl
49
51
  }
50
52
  }
53
+ `, N = x`
54
+ query AssetHandlerUserFilePermissions($fileId: ID!) {
55
+ getUserFilePermissions(fileId: $fileId) {
56
+ fileId
57
+ canShare
58
+ }
59
+ }
51
60
  `;
52
- function L(e) {
61
+ function P(e) {
62
+ return e === "PRIVATE" || e === "TEAM";
63
+ }
64
+ var F = "You can't share this file", I = "Only the uploader, or someone with share or manage access to this file, can create a share link. Ask one of them to share it or to grant you access.", L = "Your workspace role doesn't allow sharing files. Ask a workspace admin for share access.", R = "You don't have permission to share this file. Ask the file's uploader or a workspace admin.";
65
+ function z(e) {
66
+ return e === "denied-role" || e === "denied-restricted" || e === "denied-other";
67
+ }
68
+ function B(e) {
69
+ switch (e) {
70
+ case "denied-role": return L;
71
+ case "denied-restricted": return I;
72
+ case "denied-other": return R;
73
+ }
74
+ }
75
+ function V(e, n) {
76
+ let r = t(e);
77
+ return r === "gateway" ? "denied-role" : r === "service" ? P(n) ? "denied-restricted" : "denied-other" : null;
78
+ }
79
+ function H(e) {
53
80
  if (!e) return "0 B";
54
81
  let t = [
55
82
  "B",
@@ -59,11 +86,11 @@ function L(e) {
59
86
  ], n = Math.min(Math.floor(Math.log(e) / Math.log(1024)), t.length - 1);
60
87
  return `${(e / 1024 ** n).toFixed(n === 0 ? 0 : 1)} ${t[n]}`;
61
88
  }
62
- function R(e) {
89
+ function U(e) {
63
90
  let t = e.lastIndexOf(".");
64
91
  return t >= 0 ? e.slice(t + 1).toLowerCase() : "bin";
65
92
  }
66
- function z(e) {
93
+ function W(e) {
67
94
  let t = new Date(e).getTime();
68
95
  if (Number.isNaN(t)) return "";
69
96
  let n = Date.now() - t, r = Math.round(n / 1e3);
@@ -75,8 +102,8 @@ function z(e) {
75
102
  let o = Math.round(a / 24);
76
103
  return o < 30 ? `${o} day${o === 1 ? "" : "s"} ago` : new Date(e).toLocaleDateString();
77
104
  }
78
- function B(e) {
79
- let t = (e.mimeType || "").toLowerCase(), n = (e.extension || R(e.name)).toLowerCase();
105
+ function G(e) {
106
+ let t = (e.mimeType || "").toLowerCase(), n = (e.extension || U(e.name)).toLowerCase();
80
107
  return t.startsWith("image/") || [
81
108
  "png",
82
109
  "jpg",
@@ -135,68 +162,68 @@ function B(e) {
135
162
  "pages"
136
163
  ].includes(n) ? "doc" : "other";
137
164
  }
138
- var V = {
165
+ var K = {
139
166
  image: {
140
- Icon: f,
167
+ Icon: h,
141
168
  iconClass: "text-status-success-text",
142
169
  tileClass: "bg-status-success-bg-subtle"
143
170
  },
144
171
  pdf: {
145
- Icon: u,
172
+ Icon: p,
146
173
  iconClass: "text-status-error-text",
147
174
  tileClass: "bg-status-error-bg-subtle"
148
175
  },
149
176
  sheet: {
150
- Icon: l,
177
+ Icon: f,
151
178
  iconClass: "text-status-success-text",
152
179
  tileClass: "bg-status-success-bg-subtle"
153
180
  },
154
181
  doc: {
155
- Icon: u,
182
+ Icon: p,
156
183
  iconClass: "text-status-info-text",
157
184
  tileClass: "bg-status-info-bg-subtle"
158
185
  },
159
186
  archive: {
160
- Icon: o,
187
+ Icon: l,
161
188
  iconClass: "text-status-warning-text",
162
189
  tileClass: "bg-status-warning-bg-subtle"
163
190
  },
164
191
  code: {
165
- Icon: c,
192
+ Icon: d,
166
193
  iconClass: "text-status-info-text",
167
194
  tileClass: "bg-status-info-bg-subtle"
168
195
  },
169
196
  video: {
170
- Icon: d,
197
+ Icon: m,
171
198
  iconClass: "text-status-info-text",
172
199
  tileClass: "bg-status-info-bg-subtle"
173
200
  },
174
201
  audio: {
175
- Icon: s,
202
+ Icon: u,
176
203
  iconClass: "text-status-warning-text",
177
204
  tileClass: "bg-status-warning-bg-subtle"
178
205
  },
179
206
  other: {
180
- Icon: a,
207
+ Icon: c,
181
208
  iconClass: "text-text-secondary",
182
209
  tileClass: "bg-bg-sunken"
183
210
  }
184
211
  };
185
- function H({ file: e }) {
186
- let t = B(e), { Icon: n, iconClass: r, tileClass: i } = V[t];
187
- return t === "image" && e.downloadUrl ? /* @__PURE__ */ x("img", {
212
+ function le({ file: e }) {
213
+ let t = G(e), { Icon: n, iconClass: r, tileClass: i } = K[t];
214
+ return t === "image" && e.downloadUrl ? /* @__PURE__ */ w("img", {
188
215
  src: e.downloadUrl,
189
216
  alt: "",
190
217
  loading: "lazy",
191
218
  className: "h-10 w-10 shrink-0 rounded-lg border border-border-subtle object-cover"
192
- }) : /* @__PURE__ */ x("span", {
219
+ }) : /* @__PURE__ */ w("span", {
193
220
  className: `flex h-10 w-10 shrink-0 items-center justify-center rounded-lg ${i}`,
194
221
  "aria-hidden": "true",
195
- children: /* @__PURE__ */ x(n, { className: `h-5 w-5 ${r}` })
222
+ children: /* @__PURE__ */ w(n, { className: `h-5 w-5 ${r}` })
196
223
  });
197
224
  }
198
- function U({ entityType: a, entityId: o }) {
199
- let s = n(null), [c, l] = r(!1), [u, d] = r(null), { data: f, loading: v, refetch: B } = b(N, {
225
+ function q({ entityType: t, entityId: c }) {
226
+ let l = a(null), [u, d] = o(!1), [f, p] = o(null), [m, h] = o({}), x = te(), { data: I, loading: L, refetch: R } = C(ae, {
200
227
  variables: {
201
228
  pagination: {
202
229
  limit: 25,
@@ -209,14 +236,14 @@ function U({ entityType: a, entityId: o }) {
209
236
  },
210
237
  fetchPolicy: "cache-and-network",
211
238
  errorPolicy: "all"
212
- }), [V] = y(P), [U] = y(F), [W] = y(I), G = t(() => f?.getFiles.files ?? [], [f]), K = async (t) => {
239
+ }), [G] = S(oe), [K] = S(se), [q] = S(ce), J = i(() => I?.getFiles.files ?? [], [I]), ue = async (t) => {
213
240
  let n = t.target.files?.[0];
214
241
  if (t.target.value = "", n) {
215
- l(!0);
242
+ d(!0);
216
243
  try {
217
- let t = await V({ variables: { input: {
244
+ let t = await G({ variables: { input: {
218
245
  fileName: n.name,
219
- extension: R(n.name),
246
+ extension: U(n.name),
220
247
  fileSize: n.size,
221
248
  mimeType: n.type || "application/octet-stream",
222
249
  visibility: "WORKSPACE"
@@ -228,150 +255,201 @@ function U({ entityType: a, entityId: o }) {
228
255
  headers: { "Content-Type": n.type || "application/octet-stream" }
229
256
  });
230
257
  if (!a.ok) throw Error(`Storage rejected the upload (HTTP ${a.status}).`);
231
- await U({ variables: { input: {
258
+ await K({ variables: { input: {
232
259
  sessionId: i,
233
260
  fileSize: n.size
234
- } } }), e.success("File uploaded", `${n.name} is now available on this record.`), await B();
261
+ } } }), e.success("File uploaded", `${n.name} is now available on this record.`), await R();
235
262
  } catch (t) {
236
263
  e.error("Upload failed", t instanceof Error ? t.message : "The file could not be uploaded.");
237
264
  } finally {
238
- l(!1);
265
+ d(!1);
239
266
  }
240
267
  }
241
- }, q = async (t) => {
242
- d(t);
268
+ }, Y = r((e, t) => {
269
+ h((n) => {
270
+ let r = { ...n };
271
+ return t ? r[e] = t : delete r[e], r;
272
+ });
273
+ }, []), X = r(async (e) => {
274
+ Y(e, "checking");
275
+ try {
276
+ let t = (await x.query({
277
+ query: N,
278
+ variables: { fileId: e },
279
+ fetchPolicy: "network-only"
280
+ })).data?.getUserFilePermissions.canShare === !0 ? "allowed" : "denied-restricted";
281
+ return Y(e, t), t;
282
+ } catch (t) {
283
+ return n(t) ? (Y(e, "unverified"), "unverified") : (Y(e, null), "failed");
284
+ }
285
+ }, [x, Y]), de = (e, t) => {
286
+ t && (P(e.visibility) ? m[e.id] !== "checking" && X(e.id) : z(m[e.id]) && Y(e.id, null));
287
+ }, Z = async (t, { recheck: n }) => {
288
+ let r = t.id;
289
+ p(r);
243
290
  try {
244
- let n = (await W({ variables: { input: {
245
- resourceId: t,
291
+ if (P(t.visibility)) {
292
+ let t = m[r], i = !n && (t === "allowed" || t === "unverified") ? t : await X(r);
293
+ if (i === "failed") throw Error("Your share access could not be checked. Please try again.");
294
+ if (i === "denied-restricted") {
295
+ e.error(F, B(i));
296
+ return;
297
+ }
298
+ }
299
+ let i = (await q({ variables: { input: {
300
+ resourceId: r,
246
301
  role: "VIEWER"
247
302
  } } })).data?.createFileShareLink.shareUrl;
248
- if (!n) throw Error("No share link was returned.");
249
- navigator.clipboard?.writeText ? (await navigator.clipboard.writeText(n), e.success("Share link copied", "A view-only link is on your clipboard.")) : e.info("Share link ready", n);
250
- } catch (t) {
251
- e.error("Could not create share link", t instanceof Error ? t.message : "Please try again.");
303
+ if (!i) throw Error("No share link was returned.");
304
+ Y(r, "allowed"), navigator.clipboard?.writeText ? (await navigator.clipboard.writeText(i), e.success("Share link copied", "A view-only link is on your clipboard.")) : e.info("Share link ready", i);
305
+ } catch (n) {
306
+ let i = V(n, t.visibility);
307
+ if (i) {
308
+ Y(r, i), e.error(F, B(i));
309
+ return;
310
+ }
311
+ e.error("Could not create share link", n instanceof Error ? n.message : "Please try again.");
252
312
  } finally {
253
- d(null);
313
+ p(null);
254
314
  }
255
- }, J = f?.getFiles.total ?? 0, Y = G.length > 0;
256
- return /* @__PURE__ */ S(w, { children: [/* @__PURE__ */ x(E, {
315
+ }, Q = I?.getFiles.total ?? 0, $ = J.length > 0;
316
+ return /* @__PURE__ */ T(E, { children: [/* @__PURE__ */ w(O, {
257
317
  className: "flex flex-row items-center justify-between gap-2 space-y-0",
258
- children: /* @__PURE__ */ S(D, {
318
+ children: /* @__PURE__ */ T(k, {
259
319
  className: "flex items-center gap-2 text-base",
260
320
  children: [
261
- /* @__PURE__ */ x(g, { className: "h-4 w-4" }),
321
+ /* @__PURE__ */ w(b, { className: "h-4 w-4" }),
262
322
  "Attachments",
263
- J ? /* @__PURE__ */ S("span", {
323
+ Q ? /* @__PURE__ */ T("span", {
264
324
  className: "text-sm font-normal text-text-secondary",
265
325
  children: [
266
- J,
326
+ Q,
267
327
  " file",
268
- J === 1 ? "" : "s"
328
+ Q === 1 ? "" : "s"
269
329
  ]
270
330
  }) : null
271
331
  ]
272
332
  })
273
- }), /* @__PURE__ */ S(T, {
333
+ }), /* @__PURE__ */ T(D, {
274
334
  className: "space-y-3",
275
335
  children: [
276
- /* @__PURE__ */ x("input", {
277
- ref: s,
336
+ /* @__PURE__ */ w("input", {
337
+ ref: l,
278
338
  type: "file",
279
339
  className: "hidden",
280
- onChange: (e) => void K(e),
281
- "aria-label": `Upload a file for ${a} ${o}`
340
+ onChange: (e) => void ue(e),
341
+ "aria-label": `Upload a file for ${t} ${c}`
282
342
  }),
283
- v && !Y ? /* @__PURE__ */ x("div", {
343
+ L && !$ ? /* @__PURE__ */ w("div", {
284
344
  className: "space-y-2",
285
- children: Array.from({ length: 3 }).map((e, t) => /* @__PURE__ */ x("div", { className: "h-14 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer" }, t))
286
- }) : Y ? /* @__PURE__ */ x("ul", {
345
+ children: Array.from({ length: 3 }).map((e, t) => /* @__PURE__ */ w("div", { className: "h-14 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer" }, t))
346
+ }) : $ ? /* @__PURE__ */ w("ul", {
287
347
  className: "space-y-1.5",
288
- children: G.map((e) => {
289
- let t = u === e.id;
290
- return /* @__PURE__ */ S("li", {
348
+ children: J.map((e) => {
349
+ let t = f === e.id, n = m[e.id], r = n === "checking", i = z(n) ? n : null, a = i !== null;
350
+ return /* @__PURE__ */ T("li", {
291
351
  className: "flex items-center gap-3 rounded-lg border border-border-subtle bg-bg-surface p-2.5",
292
352
  children: [
293
- /* @__PURE__ */ x(H, { file: e }),
294
- /* @__PURE__ */ S("div", {
353
+ /* @__PURE__ */ w(le, { file: e }),
354
+ /* @__PURE__ */ T("div", {
295
355
  className: "min-w-0 flex-1",
296
- children: [/* @__PURE__ */ x("p", {
356
+ children: [/* @__PURE__ */ w("p", {
297
357
  className: "truncate text-sm font-medium",
298
358
  title: e.name,
299
359
  children: e.name
300
- }), /* @__PURE__ */ S("p", {
360
+ }), /* @__PURE__ */ T("p", {
301
361
  className: "truncate text-xs text-text-secondary",
302
362
  children: [
303
- L(Number(e.size)),
363
+ H(Number(e.size)),
304
364
  " · ",
305
- z(e.createdAt)
365
+ W(e.createdAt)
306
366
  ]
307
367
  })]
308
368
  }),
309
- /* @__PURE__ */ S("div", {
369
+ /* @__PURE__ */ T("div", {
310
370
  className: "hidden items-center gap-1 sm:flex",
311
- children: [e.downloadUrl ? /* @__PURE__ */ x("a", {
371
+ children: [e.downloadUrl ? /* @__PURE__ */ w("a", {
312
372
  href: e.downloadUrl,
313
373
  target: "_blank",
314
374
  rel: "noreferrer",
315
375
  className: "rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary",
316
376
  "aria-label": `Download ${e.name}`,
317
377
  title: "Download",
318
- children: /* @__PURE__ */ x(i, { className: "h-4 w-4" })
319
- }) : null, /* @__PURE__ */ x("button", {
378
+ children: /* @__PURE__ */ w(s, { className: "h-4 w-4" })
379
+ }) : null, /* @__PURE__ */ w("button", {
320
380
  type: "button",
321
- disabled: t,
322
- onClick: () => void q(e.id),
323
- className: "rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary disabled:opacity-60",
381
+ disabled: t || r,
382
+ "aria-disabled": a ? !0 : void 0,
383
+ "data-share-state": a ? "denied" : void 0,
384
+ onClick: () => void Z(e, { recheck: !0 }),
385
+ className: `rounded-md p-1.5 transition-colors disabled:opacity-60 ${a ? "cursor-not-allowed text-text-tertiary opacity-60" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"}`,
324
386
  "aria-label": `Copy share link for ${e.name}`,
325
- title: "Copy share link",
326
- children: t ? /* @__PURE__ */ x(m, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ x(p, { className: "h-4 w-4" })
387
+ title: i ? `${F}. ${B(i)}` : "Copy share link",
388
+ children: t || r ? /* @__PURE__ */ w(_, { className: "h-4 w-4 animate-spin" }) : a ? /* @__PURE__ */ w(v, {
389
+ className: "h-4 w-4",
390
+ "aria-hidden": "true"
391
+ }) : /* @__PURE__ */ w(g, { className: "h-4 w-4" })
327
392
  })]
328
393
  }),
329
- /* @__PURE__ */ x("div", {
394
+ /* @__PURE__ */ w("div", {
330
395
  className: "sm:hidden",
331
- children: /* @__PURE__ */ S(O, { children: [/* @__PURE__ */ x(j, {
332
- asChild: !0,
333
- children: /* @__PURE__ */ x("button", {
334
- type: "button",
335
- className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary",
336
- "aria-label": `Actions for ${e.name}`,
337
- children: /* @__PURE__ */ x(h, { className: "h-4 w-4" })
338
- })
339
- }), /* @__PURE__ */ S(k, {
340
- align: "end",
341
- children: [e.downloadUrl ? /* @__PURE__ */ x(A, {
396
+ children: /* @__PURE__ */ T(A, {
397
+ onOpenChange: (t) => de(e, t),
398
+ children: [/* @__PURE__ */ w(re, {
342
399
  asChild: !0,
343
- children: /* @__PURE__ */ S("a", {
344
- href: e.downloadUrl,
345
- target: "_blank",
346
- rel: "noreferrer",
347
- children: [/* @__PURE__ */ x(i, { className: "mr-2 h-4 w-4" }), "Download"]
400
+ children: /* @__PURE__ */ w("button", {
401
+ type: "button",
402
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary",
403
+ "aria-label": `Actions for ${e.name}`,
404
+ children: /* @__PURE__ */ w(y, { className: "h-4 w-4" })
348
405
  })
349
- }) : null, /* @__PURE__ */ S(A, {
350
- disabled: t,
351
- onSelect: (t) => {
352
- t.preventDefault(), q(e.id);
353
- },
354
- children: [/* @__PURE__ */ x(p, { className: "mr-2 h-4 w-4" }), "Copy share link"]
406
+ }), /* @__PURE__ */ T(j, {
407
+ align: "end",
408
+ children: [
409
+ e.downloadUrl ? /* @__PURE__ */ w(M, {
410
+ asChild: !0,
411
+ children: /* @__PURE__ */ T("a", {
412
+ href: e.downloadUrl,
413
+ target: "_blank",
414
+ rel: "noreferrer",
415
+ children: [/* @__PURE__ */ w(s, { className: "mr-2 h-4 w-4" }), "Download"]
416
+ })
417
+ }) : null,
418
+ /* @__PURE__ */ T(M, {
419
+ disabled: t || r || a,
420
+ onSelect: (t) => {
421
+ t.preventDefault(), Z(e, { recheck: !1 });
422
+ },
423
+ children: [r ? /* @__PURE__ */ w(_, { className: "mr-2 h-4 w-4 animate-spin" }) : a ? /* @__PURE__ */ w(v, {
424
+ className: "mr-2 h-4 w-4",
425
+ "aria-hidden": "true"
426
+ }) : /* @__PURE__ */ w(g, { className: "mr-2 h-4 w-4" }), r ? "Checking share access…" : a ? "No share access" : "Copy share link"]
427
+ }),
428
+ i ? /* @__PURE__ */ w("p", {
429
+ className: "max-w-56 px-2 py-1.5 text-xs text-text-secondary",
430
+ children: B(i)
431
+ }) : null
432
+ ]
355
433
  })]
356
- })] })
434
+ })
357
435
  })
358
436
  ]
359
437
  }, e.id);
360
438
  })
361
- }) : /* @__PURE__ */ x(M, {
439
+ }) : /* @__PURE__ */ w(ie, {
362
440
  illustration: "empty-data",
363
441
  title: "No files yet",
364
442
  description: "Upload photos, receipts, warranties or documents to keep them with this record."
365
443
  }),
366
- /* @__PURE__ */ S(C, {
444
+ /* @__PURE__ */ T(ne, {
367
445
  className: "w-full",
368
- disabled: c,
369
- onClick: () => s.current?.click(),
370
- children: [c ? /* @__PURE__ */ x(m, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ x(_, { className: "mr-2 h-4 w-4" }), c ? "Uploading…" : "Upload files"]
446
+ disabled: u,
447
+ onClick: () => l.current?.click(),
448
+ children: [u ? /* @__PURE__ */ w(_, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ w(ee, { className: "mr-2 h-4 w-4" }), u ? "Uploading…" : "Upload files"]
371
449
  })
372
450
  ]
373
451
  })] });
374
452
  }
375
- export { U as AttachmentsPanel };
453
+ export { q as AttachmentsPanel };
376
454
 
377
455
  //# sourceMappingURL=AttachmentsPanel.js.map