@burdenoff/microfe-files 2026.625.2 → 2026.625.4

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.
@@ -10,15 +10,15 @@ import { ArrowLeft as p, Loader2 as m, Lock as h } from "lucide-react";
10
10
  import { jsx as g, jsxs as _ } from "react/jsx-runtime";
11
11
  import { useLocation as v, useNavigate as y, useParams as b } from "react-router-dom";
12
12
  import { useI18n as x } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
13
- import { GlassCard as S } from "@burdenoff/fe-libs/ui";
14
- import { print as C } from "graphql";
13
+ import { GlassCard as S, PagePurpose as C } from "@burdenoff/fe-libs/ui";
14
+ import { print as w } from "graphql";
15
15
  //#region src/files/pages/ShareAccessPage.tsx
16
- function w(e) {
16
+ function T(e) {
17
17
  let t = e || c.apiGatewayUrl;
18
18
  if (!t) throw Error("Files API gateway URL is not configured");
19
19
  return t.includes("/graphql") ? t : s(t, "workspaces");
20
20
  }
21
- async function T(e, t, n) {
21
+ async function E(e, t, n) {
22
22
  let r = await fetch(e, {
23
23
  method: "POST",
24
24
  credentials: "omit",
@@ -31,7 +31,7 @@ async function T(e, t, n) {
31
31
  if (!r.ok || a || !i.data) throw Error(a || "Unable to access shared file");
32
32
  return i.data;
33
33
  }
34
- function E(e) {
34
+ function D(e) {
35
35
  let t = e.name.includes(".") && e.name.split(".").pop() || "";
36
36
  return {
37
37
  id: e.id,
@@ -48,39 +48,39 @@ function E(e) {
48
48
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
49
49
  };
50
50
  }
51
- function D() {
52
- let { t: s } = x(), c = y(), { pathname: D } = v(), { token: O } = b(), k = d(() => {
53
- if (O) return O;
54
- let e = D.match(/\/share\/([^/]+)/);
51
+ function O() {
52
+ let { t: s } = x(), c = y(), { pathname: O } = v(), { token: k } = b(), A = d(() => {
53
+ if (k) return k;
54
+ let e = O.match(/\/share\/([^/]+)/);
55
55
  return e?.[1] ? decodeURIComponent(e[1]) : "";
56
- }, [D, O]), A = e(), { apiGatewayUrl: j } = A, M = d(() => w(j), [j]), [N, P] = f(!1), [F, I] = f(""), [L, R] = f(!1), [z, B] = f(null), [V, H] = f(null), U = l(async (e) => {
57
- if (!k) throw Error("Share token is missing");
58
- P(!0);
56
+ }, [O, k]), j = e(), { apiGatewayUrl: M } = j, N = d(() => T(M), [M]), [P, F] = f(!1), [I, L] = f(""), [R, z] = f(!1), [B, V] = f(null), [H, U] = f(null), W = l(async (e) => {
57
+ if (!A) throw Error("Share token is missing");
58
+ F(!0);
59
59
  try {
60
- let n = (await T(M, C(t), { input: {
61
- token: k,
60
+ let n = (await E(N, w(t), { input: {
61
+ token: A,
62
62
  password: e || void 0
63
63
  } })).accessShare;
64
64
  if (!n?.file || !n.share) throw Error(n?.message || "Shared file is unavailable");
65
- R(!!n.share.requiresPassword), H(E(n.file)), B(null);
65
+ z(!!n.share.requiresPassword), U(D(n.file)), V(null);
66
66
  } finally {
67
- P(!1);
67
+ F(!1);
68
68
  }
69
- }, [M, k]);
69
+ }, [N, A]);
70
70
  return u(() => {
71
- U().catch((e) => {
71
+ W().catch((e) => {
72
72
  let t = e instanceof Error ? e.message : "Unable to access shared file";
73
- B(t), R(t.toLowerCase().includes("password"));
73
+ V(t), z(t.toLowerCase().includes("password"));
74
74
  });
75
- }, [U]), V ? /* @__PURE__ */ g(r, {
76
- file: V,
75
+ }, [W]), H ? /* @__PURE__ */ g(r, {
76
+ file: H,
77
77
  onClose: () => c("/files/browse"),
78
78
  onDownload: async () => {
79
- if (!V) return;
80
- let e = V.downloadUrl || V.url;
81
- e && (i("light"), await a(e), o(A, "files.share.downloaded", {
82
- fileId: V.id,
83
- fileName: V.name
79
+ if (!H) return;
80
+ let e = H.downloadUrl || H.url;
81
+ e && (i("light"), await a(e), o(j, "files.share.downloaded", {
82
+ fileId: H.id,
83
+ fileName: H.name
84
84
  }));
85
85
  }
86
86
  }) : /* @__PURE__ */ g("div", {
@@ -100,22 +100,22 @@ function D() {
100
100
  children: [/* @__PURE__ */ g("h1", {
101
101
  className: "text-xl font-semibold text-text-primary",
102
102
  children: n(s, "files.share.title", "Shared file access")
103
- }), /* @__PURE__ */ g("p", {
104
- className: "mt-2 text-sm text-text-secondary",
105
- children: n(s, "files.share.subtitle", "Open a shared file preview using the access token from the share link.")
103
+ }), /* @__PURE__ */ g(C, {
104
+ className: "mt-2",
105
+ children: n(s, "files.share.subtitle", "Someone shared a file with you via a link. Preview and download it here — if the link is password-protected, enter the password the sender gave you to unlock it.")
106
106
  })]
107
107
  }),
108
- N ? /* @__PURE__ */ _("div", {
108
+ P ? /* @__PURE__ */ _("div", {
109
109
  className: "flex items-center gap-3 rounded-lg border border-border-seam bg-bg-sunken px-4 py-3 text-sm text-text-secondary",
110
110
  children: [/* @__PURE__ */ g(m, { className: "size-4 animate-spin text-action-primary-bg" }), n(s, "files.share.verifying", "Verifying share access…")]
111
- }) : L ? /* @__PURE__ */ _("form", {
111
+ }) : R ? /* @__PURE__ */ _("form", {
112
112
  className: "space-y-4",
113
113
  onSubmit: async (e) => {
114
114
  e.preventDefault();
115
115
  try {
116
- await U(F);
116
+ await W(I);
117
117
  } catch (e) {
118
- B(e instanceof Error ? e.message : "Unable to unlock shared file");
118
+ V(e instanceof Error ? e.message : "Unable to unlock shared file");
119
119
  }
120
120
  },
121
121
  children: [
@@ -125,33 +125,33 @@ function D() {
125
125
  className: "mt-2 flex items-center gap-2 rounded-lg border border-border-seam px-3 py-2",
126
126
  children: [/* @__PURE__ */ g(h, { className: "size-4 text-text-tertiary" }), /* @__PURE__ */ g("input", {
127
127
  type: "password",
128
- value: F,
129
- onChange: (e) => I(e.target.value),
128
+ value: I,
129
+ onChange: (e) => L(e.target.value),
130
130
  "aria-label": n(s, "files.share.password.label", "Share password"),
131
131
  className: "w-full bg-transparent text-sm text-text-primary outline-none",
132
132
  placeholder: n(s, "files.share.password.placeholder", "Enter the share password")
133
133
  })]
134
134
  })]
135
135
  }),
136
- z && /* @__PURE__ */ g("div", {
136
+ B && /* @__PURE__ */ g("div", {
137
137
  className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
138
- children: z
138
+ children: B
139
139
  }),
140
140
  /* @__PURE__ */ g("button", {
141
141
  type: "submit",
142
- disabled: !F.trim(),
142
+ disabled: !I.trim(),
143
143
  className: "inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-text-inverse transition-colors hover:bg-action-primary-bg-hover disabled:cursor-not-allowed disabled:bg-bg-sunken disabled:text-text-tertiary",
144
144
  children: n(s, "files.share.unlock", "Unlock shared file")
145
145
  })
146
146
  ]
147
147
  }) : /* @__PURE__ */ _("div", {
148
148
  className: "space-y-4",
149
- children: [z && /* @__PURE__ */ g("div", {
149
+ children: [B && /* @__PURE__ */ g("div", {
150
150
  className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
151
- children: z
151
+ children: B
152
152
  }), /* @__PURE__ */ g("button", {
153
153
  type: "button",
154
- onClick: () => U(),
154
+ onClick: () => W(),
155
155
  className: "inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-text-inverse transition-colors hover:bg-action-primary-bg-hover",
156
156
  children: n(s, "files.share.retry", "Retry shared file access")
157
157
  })]
@@ -161,6 +161,6 @@ function D() {
161
161
  });
162
162
  }
163
163
  //#endregion
164
- export { D as ShareAccessPage };
164
+ export { O as ShareAccessPage };
165
165
 
166
166
  //# sourceMappingURL=ShareAccessPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShareAccessPage.js","names":[],"sources":["../../../src/files/pages/ShareAccessPage.tsx"],"sourcesContent":["import {\n type FormEvent,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport { useLocation, useNavigate, useParams } from \"react-router-dom\";\nimport { ArrowLeft, Loader2, Lock } from \"lucide-react\";\nimport { print } from \"graphql\";\nimport { AccessShareDocument } from \"../../generated/wspace-operations\";\nimport type {\n AccessShareMutation,\n AccessShareMutationVariables,\n} from \"../../generated/wspace-operations\";\nimport { buildGraphQLEndpoint, config } from \"../../config\";\nimport { FilePreviewModal } from \"../components/FilePreviewModal\";\nimport type { FileItem } from \"../types\";\nimport { useFilesContext } from \"../providers/FilesProvider\";\nimport { emitFilesEvent } from \"../utils/events\";\nimport { nativeImpact, nativeOpenUrl } from \"../../utils/nativeBridge\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ntype GraphQLResponse<TData> = {\n data?: TData;\n errors?: Array<{ message?: string }>;\n};\n\nfunction resolveWorkspaceGraphQLEndpoint(apiGatewayUrl?: string): string {\n const configuredUrl = apiGatewayUrl || config.apiGatewayUrl;\n\n if (!configuredUrl) {\n throw new Error(\"Files API gateway URL is not configured\");\n }\n\n return configuredUrl.includes(\"/graphql\")\n ? configuredUrl\n : buildGraphQLEndpoint(configuredUrl, \"workspaces\");\n}\n\nasync function requestPublicGraphQL<TData, TVariables>(\n endpoint: string,\n query: string,\n variables: TVariables,\n): Promise<TData> {\n const response = await fetch(endpoint, {\n method: \"POST\",\n credentials: \"omit\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ query, variables }),\n });\n\n const payload = (await response.json()) as GraphQLResponse<TData>;\n const errorMessage = payload.errors?.[0]?.message;\n\n if (!response.ok || errorMessage || !payload.data) {\n throw new Error(errorMessage || \"Unable to access shared file\");\n }\n\n return payload.data;\n}\n\nfunction buildSharedFile(payload: {\n id: string;\n name: string;\n workspaceId: string;\n mimeType: string;\n size: string;\n s3Key: string;\n downloadUrl?: string | null;\n}): FileItem {\n const extension = payload.name.includes(\".\")\n ? payload.name.split(\".\").pop() || \"\"\n : \"\";\n\n return {\n id: payload.id,\n name: payload.name,\n workspaceId: payload.workspaceId,\n mimeType: payload.mimeType,\n extension,\n size: Number(payload.size),\n s3Key: payload.s3Key,\n url: payload.downloadUrl ?? \"\",\n downloadUrl: payload.downloadUrl ?? undefined,\n starred: false,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n };\n}\n\nexport function ShareAccessPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { pathname } = useLocation();\n const { token: routeToken } = useParams<{ token: string }>();\n const token = useMemo(() => {\n if (routeToken) {\n return routeToken;\n }\n\n const match = pathname.match(/\\/share\\/([^/]+)/);\n return match?.[1] ? decodeURIComponent(match[1]) : \"\";\n }, [pathname, routeToken]);\n const filesContext = useFilesContext();\n const { apiGatewayUrl } = filesContext;\n const endpoint = useMemo(\n () => resolveWorkspaceGraphQLEndpoint(apiGatewayUrl),\n [apiGatewayUrl],\n );\n const [loading, setLoading] = useState(false);\n const [password, setPassword] = useState(\"\");\n const [requiresPassword, setRequiresPassword] = useState(false);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [sharedFile, setSharedFile] = useState<FileItem | null>(null);\n\n const loadShare = useCallback(\n async (sharePassword?: string) => {\n if (!token) {\n throw new Error(\"Share token is missing\");\n }\n\n setLoading(true);\n\n try {\n const data = await requestPublicGraphQL<\n AccessShareMutation,\n AccessShareMutationVariables\n >(endpoint, print(AccessShareDocument), {\n input: {\n token,\n password: sharePassword || undefined,\n },\n });\n\n const payload = data.accessShare;\n if (!payload?.file || !payload.share) {\n throw new Error(payload?.message || \"Shared file is unavailable\");\n }\n\n setRequiresPassword(Boolean(payload.share.requiresPassword));\n setSharedFile(buildSharedFile(payload.file));\n setErrorMessage(null);\n } finally {\n setLoading(false);\n }\n },\n [endpoint, token],\n );\n\n useEffect(() => {\n loadShare().catch((error) => {\n const message =\n error instanceof Error ? error.message : \"Unable to access shared file\";\n setErrorMessage(message);\n setRequiresPassword(message.toLowerCase().includes(\"password\"));\n });\n }, [loadShare]);\n\n const handleDownload = async () => {\n if (!sharedFile) {\n return;\n }\n\n const downloadUrl = sharedFile.downloadUrl || sharedFile.url;\n if (downloadUrl) {\n nativeImpact(\"light\");\n await nativeOpenUrl(downloadUrl);\n emitFilesEvent(filesContext, \"files.share.downloaded\", {\n fileId: sharedFile.id,\n fileName: sharedFile.name,\n });\n }\n };\n\n const handlePasswordSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n try {\n await loadShare(password);\n } catch (error) {\n setErrorMessage(\n error instanceof Error ? error.message : \"Unable to unlock shared file\",\n );\n }\n };\n\n if (sharedFile) {\n return (\n <FilePreviewModal\n file={sharedFile}\n onClose={() => navigate(\"/files/browse\")}\n onDownload={handleDownload}\n />\n );\n }\n\n return (\n <div className=\"flex min-h-[70vh] items-center justify-center bg-hero-mesh px-4 py-10\">\n <GlassCard treatment=\"glass\" className=\"w-full max-w-md p-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/files/browse\")}\n className=\"mb-6 inline-flex items-center gap-2 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n {tWithFallback(t, \"files.share.back\", \"Back to files\")}\n </button>\n\n <div className=\"mb-6\">\n <h1 className=\"text-xl font-semibold text-text-primary\">\n {tWithFallback(t, \"files.share.title\", \"Shared file access\")}\n </h1>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.share.subtitle\",\n \"Open a shared file preview using the access token from the share link.\",\n )}\n </p>\n </div>\n\n {loading ? (\n <div className=\"flex items-center gap-3 rounded-lg border border-border-seam bg-bg-sunken px-4 py-3 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin text-action-primary-bg\" />\n {tWithFallback(\n t,\n \"files.share.verifying\",\n \"Verifying share access…\",\n )}\n </div>\n ) : requiresPassword ? (\n <form className=\"space-y-4\" onSubmit={handlePasswordSubmit}>\n <label className=\"block text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.share.password.label\", \"Share password\")}\n <div className=\"mt-2 flex items-center gap-2 rounded-lg border border-border-seam px-3 py-2\">\n <Lock className=\"size-4 text-text-tertiary\" />\n <input\n type=\"password\"\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n aria-label={tWithFallback(\n t,\n \"files.share.password.label\",\n \"Share password\",\n )}\n className=\"w-full bg-transparent text-sm text-text-primary outline-none\"\n placeholder={tWithFallback(\n t,\n \"files.share.password.placeholder\",\n \"Enter the share password\",\n )}\n />\n </div>\n </label>\n\n {errorMessage && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {errorMessage}\n </div>\n )}\n\n <button\n type=\"submit\"\n disabled={!password.trim()}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-text-inverse transition-colors hover:bg-action-primary-bg-hover disabled:cursor-not-allowed disabled:bg-bg-sunken disabled:text-text-tertiary\"\n >\n {tWithFallback(t, \"files.share.unlock\", \"Unlock shared file\")}\n </button>\n </form>\n ) : (\n <div className=\"space-y-4\">\n {errorMessage && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {errorMessage}\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => loadShare()}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-text-inverse transition-colors hover:bg-action-primary-bg-hover\"\n >\n {tWithFallback(\n t,\n \"files.share.retry\",\n \"Retry shared file access\",\n )}\n </button>\n </div>\n )}\n </GlassCard>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ShareAccessPage.js","names":[],"sources":["../../../src/files/pages/ShareAccessPage.tsx"],"sourcesContent":["import {\n type FormEvent,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport { useLocation, useNavigate, useParams } from \"react-router-dom\";\nimport { ArrowLeft, Loader2, Lock } from \"lucide-react\";\nimport { print } from \"graphql\";\nimport { AccessShareDocument } from \"../../generated/wspace-operations\";\nimport type {\n AccessShareMutation,\n AccessShareMutationVariables,\n} from \"../../generated/wspace-operations\";\nimport { buildGraphQLEndpoint, config } from \"../../config\";\nimport { FilePreviewModal } from \"../components/FilePreviewModal\";\nimport type { FileItem } from \"../types\";\nimport { useFilesContext } from \"../providers/FilesProvider\";\nimport { emitFilesEvent } from \"../utils/events\";\nimport { nativeImpact, nativeOpenUrl } from \"../../utils/nativeBridge\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { GlassCard, PagePurpose } from \"@burdenoff/fe-libs/ui\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ntype GraphQLResponse<TData> = {\n data?: TData;\n errors?: Array<{ message?: string }>;\n};\n\nfunction resolveWorkspaceGraphQLEndpoint(apiGatewayUrl?: string): string {\n const configuredUrl = apiGatewayUrl || config.apiGatewayUrl;\n\n if (!configuredUrl) {\n throw new Error(\"Files API gateway URL is not configured\");\n }\n\n return configuredUrl.includes(\"/graphql\")\n ? configuredUrl\n : buildGraphQLEndpoint(configuredUrl, \"workspaces\");\n}\n\nasync function requestPublicGraphQL<TData, TVariables>(\n endpoint: string,\n query: string,\n variables: TVariables,\n): Promise<TData> {\n const response = await fetch(endpoint, {\n method: \"POST\",\n credentials: \"omit\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ query, variables }),\n });\n\n const payload = (await response.json()) as GraphQLResponse<TData>;\n const errorMessage = payload.errors?.[0]?.message;\n\n if (!response.ok || errorMessage || !payload.data) {\n throw new Error(errorMessage || \"Unable to access shared file\");\n }\n\n return payload.data;\n}\n\nfunction buildSharedFile(payload: {\n id: string;\n name: string;\n workspaceId: string;\n mimeType: string;\n size: string;\n s3Key: string;\n downloadUrl?: string | null;\n}): FileItem {\n const extension = payload.name.includes(\".\")\n ? payload.name.split(\".\").pop() || \"\"\n : \"\";\n\n return {\n id: payload.id,\n name: payload.name,\n workspaceId: payload.workspaceId,\n mimeType: payload.mimeType,\n extension,\n size: Number(payload.size),\n s3Key: payload.s3Key,\n url: payload.downloadUrl ?? \"\",\n downloadUrl: payload.downloadUrl ?? undefined,\n starred: false,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n };\n}\n\nexport function ShareAccessPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { pathname } = useLocation();\n const { token: routeToken } = useParams<{ token: string }>();\n const token = useMemo(() => {\n if (routeToken) {\n return routeToken;\n }\n\n const match = pathname.match(/\\/share\\/([^/]+)/);\n return match?.[1] ? decodeURIComponent(match[1]) : \"\";\n }, [pathname, routeToken]);\n const filesContext = useFilesContext();\n const { apiGatewayUrl } = filesContext;\n const endpoint = useMemo(\n () => resolveWorkspaceGraphQLEndpoint(apiGatewayUrl),\n [apiGatewayUrl],\n );\n const [loading, setLoading] = useState(false);\n const [password, setPassword] = useState(\"\");\n const [requiresPassword, setRequiresPassword] = useState(false);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [sharedFile, setSharedFile] = useState<FileItem | null>(null);\n\n const loadShare = useCallback(\n async (sharePassword?: string) => {\n if (!token) {\n throw new Error(\"Share token is missing\");\n }\n\n setLoading(true);\n\n try {\n const data = await requestPublicGraphQL<\n AccessShareMutation,\n AccessShareMutationVariables\n >(endpoint, print(AccessShareDocument), {\n input: {\n token,\n password: sharePassword || undefined,\n },\n });\n\n const payload = data.accessShare;\n if (!payload?.file || !payload.share) {\n throw new Error(payload?.message || \"Shared file is unavailable\");\n }\n\n setRequiresPassword(Boolean(payload.share.requiresPassword));\n setSharedFile(buildSharedFile(payload.file));\n setErrorMessage(null);\n } finally {\n setLoading(false);\n }\n },\n [endpoint, token],\n );\n\n useEffect(() => {\n loadShare().catch((error) => {\n const message =\n error instanceof Error ? error.message : \"Unable to access shared file\";\n setErrorMessage(message);\n setRequiresPassword(message.toLowerCase().includes(\"password\"));\n });\n }, [loadShare]);\n\n const handleDownload = async () => {\n if (!sharedFile) {\n return;\n }\n\n const downloadUrl = sharedFile.downloadUrl || sharedFile.url;\n if (downloadUrl) {\n nativeImpact(\"light\");\n await nativeOpenUrl(downloadUrl);\n emitFilesEvent(filesContext, \"files.share.downloaded\", {\n fileId: sharedFile.id,\n fileName: sharedFile.name,\n });\n }\n };\n\n const handlePasswordSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n try {\n await loadShare(password);\n } catch (error) {\n setErrorMessage(\n error instanceof Error ? error.message : \"Unable to unlock shared file\",\n );\n }\n };\n\n if (sharedFile) {\n return (\n <FilePreviewModal\n file={sharedFile}\n onClose={() => navigate(\"/files/browse\")}\n onDownload={handleDownload}\n />\n );\n }\n\n return (\n <div className=\"flex min-h-[70vh] items-center justify-center bg-hero-mesh px-4 py-10\">\n <GlassCard treatment=\"glass\" className=\"w-full max-w-md p-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/files/browse\")}\n className=\"mb-6 inline-flex items-center gap-2 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n {tWithFallback(t, \"files.share.back\", \"Back to files\")}\n </button>\n\n <div className=\"mb-6\">\n <h1 className=\"text-xl font-semibold text-text-primary\">\n {tWithFallback(t, \"files.share.title\", \"Shared file access\")}\n </h1>\n <PagePurpose className=\"mt-2\">\n {tWithFallback(\n t,\n \"files.share.subtitle\",\n \"Someone shared a file with you via a link. Preview and download it here — if the link is password-protected, enter the password the sender gave you to unlock it.\",\n )}\n </PagePurpose>\n </div>\n\n {loading ? (\n <div className=\"flex items-center gap-3 rounded-lg border border-border-seam bg-bg-sunken px-4 py-3 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin text-action-primary-bg\" />\n {tWithFallback(\n t,\n \"files.share.verifying\",\n \"Verifying share access…\",\n )}\n </div>\n ) : requiresPassword ? (\n <form className=\"space-y-4\" onSubmit={handlePasswordSubmit}>\n <label className=\"block text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.share.password.label\", \"Share password\")}\n <div className=\"mt-2 flex items-center gap-2 rounded-lg border border-border-seam px-3 py-2\">\n <Lock className=\"size-4 text-text-tertiary\" />\n <input\n type=\"password\"\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n aria-label={tWithFallback(\n t,\n \"files.share.password.label\",\n \"Share password\",\n )}\n className=\"w-full bg-transparent text-sm text-text-primary outline-none\"\n placeholder={tWithFallback(\n t,\n \"files.share.password.placeholder\",\n \"Enter the share password\",\n )}\n />\n </div>\n </label>\n\n {errorMessage && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {errorMessage}\n </div>\n )}\n\n <button\n type=\"submit\"\n disabled={!password.trim()}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-text-inverse transition-colors hover:bg-action-primary-bg-hover disabled:cursor-not-allowed disabled:bg-bg-sunken disabled:text-text-tertiary\"\n >\n {tWithFallback(t, \"files.share.unlock\", \"Unlock shared file\")}\n </button>\n </form>\n ) : (\n <div className=\"space-y-4\">\n {errorMessage && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {errorMessage}\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => loadShare()}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-medium text-text-inverse transition-colors hover:bg-action-primary-bg-hover\"\n >\n {tWithFallback(\n t,\n \"files.share.retry\",\n \"Retry shared file access\",\n )}\n </button>\n </div>\n )}\n </GlassCard>\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-files",
3
- "version": "2026.625.2",
3
+ "version": "2026.625.4",
4
4
  "description": "Burdenoff Files Microfrontend - Standalone file management module",
5
5
  "type": "module",
6
6
  "imports": {
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@apollo/client": "^4.0.13",
48
- "@burdenoff/fe-libs": "2026.625.2",
48
+ "@burdenoff/fe-libs": "2026.625.5",
49
49
  "@tanstack/react-query": "^5.90.17",
50
50
  "graphql": "^16.12.0",
51
51
  "lucide-react": "^0.468.0",