@haneinfo/frontend-instances 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,3 +93,52 @@ into the host's instances catalog. The new keys include the known error code all
93
93
  `instanceError` only maps recognized codes. Unknown/missing codes, even on 401/403/404, use
94
94
  requestFailed. Backend message, diagnostic message and lastError text are never rendered.
95
95
  Upgrade common and frontend-shell together so the production filter and API adapter retain code.
96
+
97
+ ## Attachment panel (draft 202610-a7c1)
98
+
99
+ Pass `showAttachments: true` to `renderInstanceDetailPage`. The client entry also exports
100
+ `AttachmentsPanel`. The host supplies the existing authenticated action for `attachments` GET and
101
+ `deleteAttachment` DELETE through `performInstanceRequest`. Successful mutations reload the list
102
+ and enclosing timeline. Merge the complete bilingual catalogs in validation/equipment-loan/attachment-panel/app/.
103
+
104
+ Mount POST `${basePath}/:id/attachments/upload` and GET
105
+ `${basePath}/:id/attachments/:attachmentId/content` in the App. Upload uses XHR multipart with
106
+ per-file progress, a synchronous mutation lock and sequential requests. It never uses a Server
107
+ Action. The route must authenticate, reject missing/mismatched Origin and Host, and stream the
108
+ unaltered body/Content-Type/Content-Length to the fixed backend route with the current token.
109
+ Abort the upstream request on disconnect. Exclude upload from Next Proxy's request-body cloning;
110
+ in Next 16.3 the proxy otherwise buffers bytes even when the route itself streams.
111
+ The example retains the existing 16 MB Server Action setting and verifies an 18 MiB route upload.
112
+
113
+ Use HTTP canUpload/canDelete only. Missing canUpload hides selection; remaining=0 disables it.
114
+ Size/extension/remaining prechecks are advisory and still submit each selected file: only backend
115
+ status and known codes determine the result. Unknown codes never expose response prose.
116
+ READY downloads through the fixed App route; PENDING_SCAN shows scanning; REJECTED shows scan
117
+ failed/content removed with no download. Rejections retain authorized record deletion and occupy
118
+ no slots. Refresh rechecks scan status. Names, uploader labels and upload/delete timeline names
119
+ are text nodes. Native buttons support keyboard selection/drop activation; progress is labelled.
120
+
121
+ ## Document panel (draft 202610-b9e2)
122
+
123
+ Pass `showDocuments: true` and `documentDownloadUrl` (the App's fixed same-origin POST route)
124
+ to `renderInstanceDetailPage`. `DocumentsPanel` is also exported from the client entry.
125
+ The existing authenticated action calls `performInstanceRequest` with `documentTemplates` to
126
+ GET `/state-machine-instances/:id/documents/templates`. Only the backend decides availability;
127
+ an empty list renders an explanation with no controls. Merge the complete English/zh-TW catalogs
128
+ in `validation/equipment-loan/document-panel/app/frontend/messages/` into the host's `instances`.
129
+
130
+ The host route must check the current session and exact Origin/Host, stream the JSON request to
131
+ POST `/state-machine-instances/:id/documents`, and return the upstream status and Content-Type,
132
+ Content-Disposition, X-Content-Type-Options, Cache-Control and Content-Length unchanged.
133
+ Propagate request aborts and downstream cancellation. The example excludes the route from Next
134
+ Proxy and keeps file bytes out of Server Actions. Never put tokens or backend URLs in links.
135
+
136
+ The panel POSTs `{ templateCode, format: 'pdf' | 'docx' }` using fetch. It accepts only a successful
137
+ HTTP response with the requested format's MIME type before creating a Blob/object URL download.
138
+ Errors read only known JSON codes, never raw messages or binary text. A synchronous lock covers
139
+ all templates and formats until download and timeline refresh finish. The object URL is released
140
+ after the browser starts the download; unmount aborts in-flight requests. UTF-8 `filename*` wins
141
+ over `filename`; unsafe/malformed names use a bounded template-code/format fallback. Blob storage
142
+ can retain up to the backend's default 10 MiB output limit plus browser-managed copies; Next does
143
+ not buffer the response. DOCUMENT history renders translated template code/version/format/actor
144
+ as text. No preview, attachment persistence or workflow transition controls are added.
package/agent-guide.md CHANGED
@@ -52,3 +52,25 @@ Use a same-origin authenticated Server Action for writes. Permanent sources have
52
52
  Translate only exact known codes in src/messages.ts; never show backend prose or job lastError.
53
53
  Both current pages and the new management pages follow this policy. Merge the complete English
54
54
  and zh-TW fragments with the existing host catalogs before building an overlay.
55
+
56
+ ## Attachments
57
+
58
+ Use showAttachments on the detail renderer, or AttachmentsPanel from the client entry. Mount the
59
+ fixed upload/content routes documented in README and the reversible attachment-panel overlay.
60
+ Use the authenticated action adapter only for listing and deletion; binary uploads must stream
61
+ through a separate same-origin authenticated route with abort propagation and no Proxy buffering.
62
+ Never infer authority or quota from rows: REJECTED remains visible but uses no slots. Its content
63
+ is removed and cannot download; permitted users may delete its record. PENDING_SCAN also cannot
64
+ download. Copy all English/zh-TW keys from the current overlay catalogs. Keep quota prechecks
65
+ advisory, confirm deletion, and refresh the backend list/timeline after changes.
66
+
67
+ ## Documents
68
+
69
+ Implemented against draft 202610-b9e2; the decision remains draft. Enable showDocuments and pass
70
+ documentDownloadUrl to the detail renderer. Use the authenticated documentTemplates action for
71
+ the backend's `{ items: [{ code, name, version }] }` list; do not infer availability or permissions.
72
+ Follow the README and reversible document-panel overlay for the authenticated same-origin POST
73
+ proxy, unchanged response headers/status, streaming and cancellation. Only matching successful
74
+ file MIME types become Blobs. Translate exact known JSON codes, never raw backend prose. Keep
75
+ one generation pending across all formats/templates, release object URLs, abort on unmount and
76
+ refresh the enclosing timeline after success. Names, codes and document history are text nodes.
@@ -0,0 +1,27 @@
1
+ export declare const attachmentMessages: {
2
+ readonly attachmentDownload: readonly ["Download file", "下載檔案"];
3
+ readonly attachmentRefresh: readonly ["Refresh attachments", "更新附件"];
4
+ readonly attachmentsTitle: readonly ["Attachments", "附件"];
5
+ readonly attachmentName: readonly ["File name", "檔名"];
6
+ readonly attachmentSize: readonly ["Size", "大小"];
7
+ readonly attachmentType: readonly ["Type", "型別"];
8
+ readonly attachmentStatus: readonly ["Status", "狀態"];
9
+ readonly uploadedBy: readonly ["Uploaded by", "上傳者"];
10
+ readonly uploadAttachments: readonly ["Choose files", "選擇檔案"];
11
+ readonly attachmentDrop: readonly ["Drop files here or press Enter to choose files", "將檔案拖放至此,或按 Enter 選擇檔案"];
12
+ readonly attachmentPrecheck: readonly ["Precheck hint only; the server decides each upload.", "預檢僅供提示,每個檔案仍由伺服器判定。"];
13
+ readonly attachmentLimit: readonly ["The attachment limit has been reached.", "附件數量已達上限。"];
14
+ readonly attachmentRemaining: readonly ["Remaining slots", "剩餘名額"];
15
+ readonly deleteAttachment: readonly ["Delete record", "刪除紀錄"];
16
+ readonly confirmDeleteAttachment: readonly ["Delete this attachment record?", "確定刪除此附件紀錄?"];
17
+ readonly cancelDeleteAttachment: readonly ["Cancel", "取消"];
18
+ readonly confirmAttachmentDeletion: readonly ["Confirm deletion", "確認刪除"];
19
+ readonly attachmentQueued: readonly ["Queued", "等待上傳"];
20
+ readonly attachmentUploading: readonly ["Uploading", "上傳中"];
21
+ readonly attachmentUploaded: readonly ["Upload accepted", "上傳成功"];
22
+ readonly attachmentReady: readonly ["Ready", "可下載"];
23
+ readonly attachmentPending: readonly ["Scanning", "掃描中"];
24
+ readonly attachmentRejected: readonly ["Scan failed; content has been removed", "未通過掃描,內容已移除"];
25
+ };
26
+ export declare const attachmentKeys: (keyof typeof attachmentMessages)[];
27
+ //# sourceMappingURL=attachment-messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-messages.d.ts","sourceRoot":"","sources":["../src/attachment-messages.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;CA8BrB,CAAC;AACX,eAAO,MAAM,cAAc,EAEtB,CAAC,MAAM,OAAO,kBAAkB,CAAC,EAAE,CAAC"}
@@ -0,0 +1,33 @@
1
+ export const attachmentMessages = {
2
+ attachmentDownload: ['Download file', '下載檔案'],
3
+ attachmentRefresh: ['Refresh attachments', '更新附件'],
4
+ attachmentsTitle: ['Attachments', '附件'],
5
+ attachmentName: ['File name', '檔名'],
6
+ attachmentSize: ['Size', '大小'],
7
+ attachmentType: ['Type', '型別'],
8
+ attachmentStatus: ['Status', '狀態'],
9
+ uploadedBy: ['Uploaded by', '上傳者'],
10
+ uploadAttachments: ['Choose files', '選擇檔案'],
11
+ attachmentDrop: [
12
+ 'Drop files here or press Enter to choose files',
13
+ '將檔案拖放至此,或按 Enter 選擇檔案',
14
+ ],
15
+ attachmentPrecheck: [
16
+ 'Precheck hint only; the server decides each upload.',
17
+ '預檢僅供提示,每個檔案仍由伺服器判定。',
18
+ ],
19
+ attachmentLimit: ['The attachment limit has been reached.', '附件數量已達上限。'],
20
+ attachmentRemaining: ['Remaining slots', '剩餘名額'],
21
+ deleteAttachment: ['Delete record', '刪除紀錄'],
22
+ confirmDeleteAttachment: ['Delete this attachment record?', '確定刪除此附件紀錄?'],
23
+ cancelDeleteAttachment: ['Cancel', '取消'],
24
+ confirmAttachmentDeletion: ['Confirm deletion', '確認刪除'],
25
+ attachmentQueued: ['Queued', '等待上傳'],
26
+ attachmentUploading: ['Uploading', '上傳中'],
27
+ attachmentUploaded: ['Upload accepted', '上傳成功'],
28
+ attachmentReady: ['Ready', '可下載'],
29
+ attachmentPending: ['Scanning', '掃描中'],
30
+ attachmentRejected: ['Scan failed; content has been removed', '未通過掃描,內容已移除'],
31
+ };
32
+ export const attachmentKeys = Object.keys(attachmentMessages);
33
+ //# sourceMappingURL=attachment-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-messages.js","sourceRoot":"","sources":["../src/attachment-messages.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,kBAAkB,EAAE,CAAC,eAAe,EAAE,MAAM,CAAC;IAC7C,iBAAiB,EAAE,CAAC,qBAAqB,EAAE,MAAM,CAAC;IAClD,gBAAgB,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC;IACvC,cAAc,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC;IAC9B,cAAc,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC;IAC9B,gBAAgB,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC;IAClC,UAAU,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC;IAClC,iBAAiB,EAAE,CAAC,cAAc,EAAE,MAAM,CAAC;IAC3C,cAAc,EAAE;QACd,gDAAgD;QAChD,uBAAuB;KACxB;IACD,kBAAkB,EAAE;QAClB,qDAAqD;QACrD,qBAAqB;KACtB;IACD,eAAe,EAAE,CAAC,wCAAwC,EAAE,WAAW,CAAC;IACxE,mBAAmB,EAAE,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAChD,gBAAgB,EAAE,CAAC,eAAe,EAAE,MAAM,CAAC;IAC3C,uBAAuB,EAAE,CAAC,gCAAgC,EAAE,YAAY,CAAC;IACzE,sBAAsB,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxC,yBAAyB,EAAE,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACvD,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;IACpC,mBAAmB,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC;IACzC,kBAAkB,EAAE,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAC/C,eAAe,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;IACjC,iBAAiB,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC;IACtC,kBAAkB,EAAE,CAAC,uCAAuC,EAAE,aAAa,CAAC;CACpE,CAAC;AACX,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CACvC,kBAAkB,CACoB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { AttachmentList, InstanceAction, Labels } from './contracts.js';
2
+ export declare function attachmentSize(bytes: number): string;
3
+ /** Advisory only: every selected file is still submitted for authoritative validation. */
4
+ export declare function attachmentPrecheck(file: File, limits: AttachmentList['limits'], index: number): ("TEMPLATE_NOT_AVAILABLE" | "TEMPLATE_INVALID" | "SUBJECT_UNAVAILABLE" | "DOCUMENT_TOO_LARGE" | "DOCUMENT_BUSY" | "DOCUMENT_TIMEOUT" | "DOCUMENT_ABORTED" | "DOCUMENT_FAILED" | "ATTACHMENT_TOO_LARGE" | "ATTACHMENT_TYPE_NOT_ALLOWED" | "ATTACHMENT_TYPE_MISMATCH" | "ATTACHMENT_LIMIT_EXCEEDED" | "ATTACHMENT_NAME_INVALID" | "ATTACHMENT_NOT_FOUND" | "ATTACHMENT_MULTIPART_INVALID" | "ATTACHMENT_UPLOAD_ABORTED" | "INVALID_INPUT" | "FORBIDDEN" | "INSTANCE_NOT_FOUND" | "MACHINE_NOT_FOUND" | "VIEW_NOT_FOUND" | "JOB_NOT_FOUND" | "UNDECLARED_QUERY_FIELD" | "INVALID_QUERY" | "INVALID_VIEW" | "INVALID_VIEW_SHARING" | "PREFLIGHT_REJECTED" | "PREFLIGHT_REQUIRED" | "DUPLICATE_KEY_EXISTING" | "DUPLICATE_KEY_IN_FILE" | "DUPLICATE_ROW" | "INVALID_DUPLICATE_KEY" | "DUPLICATE_MAPPING" | "INVALID_FIELD_MAPPING" | "MISSING_COLUMN" | "MISSING_FIELD" | "FORBIDDEN_COLUMN" | "REQUIRED" | "TYPE_MISMATCH" | "INVALID_CELL" | "INVALID_ENCODING" | "INVALID_FILE" | "INVALID_HEADERS" | "FILE_TOO_LARGE" | "EXPORT_FILE_TOO_LARGE" | "EXPORT_LIMIT_EXCEEDED" | "EXPORT_NOT_AVAILABLE" | "IMPORT_REPORT_NOT_AVAILABLE" | "DEFINITION_CHANGED" | "DEFINITION_NOT_FOUND" | "SUBJECT_REQUIRED" | "INVALID_COMMENT" | "INVALID_MENTION" | "MENTION_NOT_ALLOWED" | "IDENTITY_SEARCH_UNAVAILABLE" | "DELEGATE_INVALID" | "DELEGATOR_INVALID" | "DELEGATION_PERIOD_INVALID" | "DELEGATION_DURATION_EXCEEDED" | "DELEGATION_OVERLAP" | "DELEGATION_CYCLE" | "DELEGATION_NOT_FOUND" | "ACCESS_NOT_MANAGEABLE" | "ACCESS_LIMIT_EXCEEDED" | "ACCESS_PRINCIPAL_INVALID" | "unauthorized" | "forbidden" | "notFound" | "invalidInput" | "requestFailed")[];
5
+ /** Uploads are XHR multipart to an App-owned streaming route, never a Server Action. */
6
+ export declare function AttachmentsPanel({ id, basePath, initial, labels: t, action, onChanged, }: {
7
+ id: string;
8
+ basePath: string;
9
+ initial?: AttachmentList;
10
+ labels: Labels;
11
+ action: InstanceAction;
12
+ onChanged?: () => Promise<void>;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=attachments-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments-client.d.ts","sourceRoot":"","sources":["../src/attachments-client.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAe,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG1F,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,UAI3C;AACD,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,4jDAS7F;AASD,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,EAC/B,EAAE,EACF,QAAQ,EACR,OAAO,EACP,MAAM,EAAE,CAAC,EACT,MAAM,EACN,SAAS,GACV,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC,+BAgSA"}
@@ -0,0 +1,160 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Button } from '@haneinfo/frontend-shell';
4
+ import { useEffect, useId, useRef, useState } from 'react';
5
+ import { call, ErrorMessage, RequestFailure, useTask } from './action-client.js';
6
+ import { errorKey } from './messages.js';
7
+ export function attachmentSize(bytes) {
8
+ const unit = bytes >= 1048576 ? 'MiB' : bytes >= 1024 ? 'KiB' : 'B';
9
+ const divisor = unit === 'MiB' ? 1048576 : unit === 'KiB' ? 1024 : 1;
10
+ return `${Number((bytes / divisor).toFixed(1))} ${unit}`;
11
+ }
12
+ /** Advisory only: every selected file is still submitted for authoritative validation. */
13
+ export function attachmentPrecheck(file, limits, index) {
14
+ if (!limits)
15
+ return [];
16
+ const hints = [];
17
+ if (file.size > limits.maxFileBytes)
18
+ hints.push('ATTACHMENT_TOO_LARGE');
19
+ const extension = file.name.split('.').pop()?.toLowerCase();
20
+ if (!limits.allowedTypes.includes(extension === 'jpg' ? 'jpeg' : (extension ?? '')))
21
+ hints.push('ATTACHMENT_TYPE_NOT_ALLOWED');
22
+ if (index >= limits.remaining)
23
+ hints.push('ATTACHMENT_LIMIT_EXCEEDED');
24
+ return hints;
25
+ }
26
+ /** Uploads are XHR multipart to an App-owned streaming route, never a Server Action. */
27
+ export function AttachmentsPanel({ id, basePath, initial, labels: t, action, onChanged, }) {
28
+ const [list, setList] = useState(initial);
29
+ const [loading, setLoading] = useState(!initial);
30
+ const [loadError, setLoadError] = useState();
31
+ const [uploads, setUploads] = useState([]);
32
+ const [confirm, setConfirm] = useState();
33
+ const fileInput = useRef(null);
34
+ const active = useRef(true), xhr = useRef(null);
35
+ const task = useTask();
36
+ const heading = useId();
37
+ const route = `${basePath}/${encodeURIComponent(id)}/attachments`;
38
+ async function refresh() {
39
+ setLoading(true);
40
+ setLoadError(undefined);
41
+ try {
42
+ const next = await call(action, { kind: 'attachments', id });
43
+ if (active.current)
44
+ setList(next);
45
+ }
46
+ catch (error) {
47
+ if (active.current) {
48
+ setList(undefined);
49
+ setLoadError(error instanceof RequestFailure ? error.reason : { key: 'requestFailed' });
50
+ }
51
+ }
52
+ finally {
53
+ if (active.current)
54
+ setLoading(false);
55
+ }
56
+ }
57
+ // biome-ignore lint/correctness/useExhaustiveDependencies: host identity remounts the panel; refresh is event-owned thereafter.
58
+ useEffect(() => {
59
+ active.current = true;
60
+ if (!initial)
61
+ void refresh();
62
+ return () => {
63
+ active.current = false;
64
+ xhr.current?.abort();
65
+ };
66
+ }, [id, action]);
67
+ const disabled = task.pending || loading || list?.limits?.remaining === 0;
68
+ function upload(files) {
69
+ if (disabled || !list?.canUpload || !files.length)
70
+ return;
71
+ void task.run(async () => {
72
+ const results = files.map((file, index) => ({
73
+ id: crypto.randomUUID(),
74
+ name: file.name,
75
+ progress: 0,
76
+ status: 'attachmentQueued',
77
+ hints: attachmentPrecheck(file, list.limits, index),
78
+ }));
79
+ const update = () => {
80
+ if (active.current)
81
+ setUploads(results.map((row) => ({ ...row })));
82
+ };
83
+ update();
84
+ for (const [index, file] of files.entries()) {
85
+ if (!active.current)
86
+ break;
87
+ const row = results[index];
88
+ row.status = 'attachmentUploading';
89
+ update();
90
+ try {
91
+ await new Promise((resolve, reject) => {
92
+ const request = new XMLHttpRequest();
93
+ xhr.current = request;
94
+ request.open('POST', `${route}/upload`);
95
+ request.timeout = 5 * 60 * 1000;
96
+ request.upload.onprogress = (event) => {
97
+ if (event.lengthComputable)
98
+ row.progress = Math.min(100, Math.round((event.loaded / event.total) * 100));
99
+ update();
100
+ };
101
+ request.onload = () => {
102
+ if (request.status >= 200 && request.status < 300)
103
+ resolve();
104
+ else {
105
+ let code;
106
+ try {
107
+ code = JSON.parse(request.responseText).code;
108
+ }
109
+ catch {
110
+ /* Generic error for non-JSON responses. */
111
+ }
112
+ reject(new RequestFailure({ key: errorKey(code) }));
113
+ }
114
+ };
115
+ request.onerror = () => reject(new RequestFailure({ key: 'requestFailed' }));
116
+ request.onabort = request.ontimeout = () => reject(new RequestFailure({ key: 'ATTACHMENT_UPLOAD_ABORTED' }));
117
+ const form = new FormData();
118
+ form.append('file', file);
119
+ request.send(form);
120
+ });
121
+ row.status = 'attachmentUploaded';
122
+ row.progress = 100;
123
+ }
124
+ catch (error) {
125
+ row.status = 'failed';
126
+ row.error = error instanceof RequestFailure ? error.reason : { key: 'requestFailed' };
127
+ }
128
+ finally {
129
+ xhr.current = null;
130
+ }
131
+ update();
132
+ if (active.current)
133
+ await refresh();
134
+ }
135
+ if (active.current)
136
+ await onChanged?.();
137
+ });
138
+ }
139
+ return (_jsxs("section", { "aria-labelledby": heading, "aria-busy": task.pending || loading, className: "flex min-w-0 flex-col gap-3 rounded-lg border bg-card p-4 text-card-foreground", children: [_jsx("h2", { id: heading, className: "font-semibold", children: t.attachmentsTitle }), (loading || task.pending) && _jsx("p", { role: "status", children: t.loading }), _jsx(ErrorMessage, { error: loadError ?? task.error, labels: t }), _jsx(Button, { type: "button", variant: "outline", disabled: task.pending || loading, onClick: () => void refresh(), children: t.attachmentRefresh }), list && (_jsxs(_Fragment, { children: [!list.items.length && _jsx("p", { children: t.empty }), _jsx("ul", { className: "flex min-w-0 flex-col gap-3", children: list.items.map((item) => (_jsxs("li", { className: "min-w-0 rounded-md border p-3 [overflow-wrap:anywhere]", "data-attachment-id": item.id, children: [_jsxs("dl", { className: "grid min-w-0 gap-2 sm:grid-cols-2", children: [_jsxs("div", { children: [_jsx("dt", { children: t.attachmentName }), _jsx("dd", { children: item.fileName })] }), _jsxs("div", { children: [_jsx("dt", { children: t.attachmentSize }), _jsx("dd", { children: attachmentSize(item.size) })] }), _jsxs("div", { children: [_jsx("dt", { children: t.attachmentType }), _jsx("dd", { children: item.detectedContentType })] }), _jsxs("div", { children: [_jsx("dt", { children: t.attachmentStatus }), _jsx("dd", { children: item.status === 'READY'
140
+ ? t.attachmentReady
141
+ : item.status === 'PENDING_SCAN'
142
+ ? t.attachmentPending
143
+ : t.attachmentRejected })] }), _jsxs("div", { children: [_jsx("dt", { children: t.uploadedBy }), _jsx("dd", { children: item.uploadedByName ?? item.uploadedBy })] }), _jsxs("div", { children: [_jsx("dt", { children: t.createdAt }), _jsx("dd", { children: _jsx("time", { dateTime: item.createdAt, children: item.createdAt }) })] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [item.status === 'READY' && (_jsx("a", { className: "underline", href: `${route}/${encodeURIComponent(item.id)}/content`, children: t.attachmentDownload })), item.canDelete && (_jsx(Button, { type: "button", variant: "outline", disabled: task.pending, onClick: () => setConfirm(item.id), children: t.deleteAttachment }))] }), confirm === item.id && item.canDelete && (_jsxs("fieldset", { "aria-label": t.confirmDeleteAttachment, className: "mt-3 flex flex-wrap items-center gap-2", children: [_jsx("p", { children: t.confirmDeleteAttachment }), _jsx(Button, { type: "button", disabled: task.pending, onClick: () => void task.run(async () => {
144
+ await call(action, {
145
+ kind: 'deleteAttachment',
146
+ id,
147
+ attachmentId: item.id,
148
+ });
149
+ setConfirm(undefined);
150
+ await refresh();
151
+ await onChanged?.();
152
+ }), children: t.confirmAttachmentDeletion }), _jsx(Button, { type: "button", variant: "outline", disabled: task.pending, onClick: () => setConfirm(undefined), children: t.cancelDeleteAttachment })] }))] }, item.id))) }), list.canUpload === true && (_jsxs(_Fragment, { children: [list.limits && (_jsxs("p", { children: [t.attachmentRemaining, ": ", list.limits.remaining, " /", ' ', list.limits.maxAttachmentsPerInstance] })), list.limits?.remaining === 0 && _jsx("p", { role: "status", children: t.attachmentLimit }), _jsx("input", { ref: fileInput, type: "file", multiple: true, "aria-label": t.uploadAttachments, disabled: disabled, className: "sr-only", onChange: (event) => {
153
+ upload(Array.from(event.currentTarget.files ?? []));
154
+ event.currentTarget.value = '';
155
+ } }), _jsx(Button, { type: "button", disabled: disabled, onClick: () => fileInput.current?.click(), children: t.uploadAttachments }), _jsx("button", { type: "button", "aria-label": t.attachmentDrop, disabled: disabled, className: "min-w-0 rounded-md border border-dashed p-6 text-left focus-visible:outline-2 disabled:opacity-50 [overflow-wrap:anywhere]", onClick: () => fileInput.current?.click(), onDragOver: (event) => event.preventDefault(), onDrop: (event) => {
156
+ event.preventDefault();
157
+ upload(Array.from(event.dataTransfer.files));
158
+ }, children: t.attachmentDrop })] }))] })), _jsx("ol", { "aria-live": "polite", className: "min-w-0 [overflow-wrap:anywhere]", children: uploads.map((row) => (_jsxs("li", { className: "my-3 min-w-0", children: [_jsxs("p", { children: [row.name, " \u00B7 ", t[row.status]] }), !!row.hints.length && (_jsxs("div", { children: [_jsx("p", { children: t.attachmentPrecheck }), row.hints.map((hint) => (_jsx("p", { children: t[hint] }, hint)))] })), _jsx("progress", { value: row.progress, max: 100, "aria-label": `${t.attachmentUploading}: ${row.name}`, className: "w-full" }), _jsx(ErrorMessage, { error: row.error, labels: t })] }, row.id))) })] }));
159
+ }
160
+ //# sourceMappingURL=attachments-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments-client.js","sourceRoot":"","sources":["../src/attachments-client.tsx"],"names":[],"mappings":"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"}
package/dist/client.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export { AttachmentsPanel } from './attachments-client.js';
2
+ export { DocumentsPanel } from './documents-client.js';
1
3
  import type { AccessList } from './contracts.js';
2
4
  import { type Condition, type FormField, type ImportField, type InstanceAction, type InstanceQuery, type Job, type JobResult, type Labels, type ListResult, type QueryField, type SavedView, type Timeline, type UploadAction } from './contracts.js';
3
5
  export { call, ErrorMessage, useTask } from './action-client.js';
@@ -21,7 +23,7 @@ export declare function ListClient({ fields, initial, initialQuery, views: initi
21
23
  labels: Labels;
22
24
  action: InstanceAction;
23
25
  }): import("react").JSX.Element;
24
- export declare function DetailClient({ id, data, fields, state, submitterId, submitterName, createdAt, initial, initialNumbers, initialAccess, labels: t, action, }: {
26
+ export declare function DetailClient({ id, data, fields, state, submitterId, submitterName, createdAt, initial, initialNumbers, initialAccess, attachmentBase, documentDownloadUrl, labels: t, action, }: {
25
27
  id: string;
26
28
  data: Record<string, unknown>;
27
29
  fields: FormField[];
@@ -32,6 +34,8 @@ export declare function DetailClient({ id, data, fields, state, submitterId, sub
32
34
  initial: Timeline;
33
35
  initialNumbers?: string[];
34
36
  initialAccess?: AccessList;
37
+ attachmentBase?: string;
38
+ documentDownloadUrl?: string;
35
39
  labels: Labels;
36
40
  action: InstanceAction;
37
41
  }): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -1,5 +1,9 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { AttachmentsPanel } from './attachments-client.js';
4
+ import { DocumentsPanel } from './documents-client.js';
5
+ export { AttachmentsPanel } from './attachments-client.js';
6
+ export { DocumentsPanel } from './documents-client.js';
3
7
  import { Button, Input, ListPagination } from '@haneinfo/frontend-shell';
4
8
  import { useEffect, useId, useRef, useState } from 'react';
5
9
  import { call, ErrorMessage, RequestFailure, useTask } from './action-client.js';
@@ -226,7 +230,7 @@ function DataFields({ fields, data }) {
226
230
  .filter((f) => (f.id ?? f.key) in data)
227
231
  .map((field) => (_jsxs("div", { className: "min-w-0", children: [_jsx("dt", { className: "text-sm text-muted-foreground", children: field.label }), _jsx("dd", { className: "whitespace-pre-wrap break-words", children: textValue(data[field.id ?? field.key]) })] }, field.id ?? field.key))) }));
228
232
  }
229
- export function DetailClient({ id, data, fields, state, submitterId, submitterName, createdAt, initial, initialNumbers, initialAccess, labels: t, action, }) {
233
+ export function DetailClient({ id, data, fields, state, submitterId, submitterName, createdAt, initial, initialNumbers, initialAccess, attachmentBase, documentDownloadUrl, labels: t, action, }) {
230
234
  const task = useTask();
231
235
  const [timeline, setTimeline] = useState(initial), [body, setBody] = useState('');
232
236
  const [q, setQ] = useState(''), [people, setPeople] = useState(), [selected, setSelected] = useState('');
@@ -247,7 +251,7 @@ export function DetailClient({ id, data, fields, state, submitterId, submitterNa
247
251
  : next);
248
252
  };
249
253
  const numbers = timeline.items.filter((e) => e.kind === 'NUMBER');
250
- return (_jsxs("div", { className: "flex min-w-0 flex-col gap-4", "aria-busy": task.pending, children: [_jsx(ErrorMessage, { error: task.error, labels: t }), task.pending && _jsx("p", { role: "status", children: t.loading }), initialAccess && (_jsx(InstanceAccessPanel, { id: id, initial: initialAccess, labels: t, action: action, onChanged: () => loadTimeline(false) })), _jsxs("section", { className: `${box} flex flex-col gap-4`, children: [_jsxs("dl", { className: "grid gap-3 sm:grid-cols-2", children: [_jsxs("div", { children: [_jsx("dt", { children: t.state }), _jsx("dd", { children: state })] }), _jsxs("div", { children: [_jsx("dt", { children: t.number }), _jsx("dd", { children: initialNumbers?.length
254
+ return (_jsxs("div", { className: "flex min-w-0 flex-col gap-4", "aria-busy": task.pending, children: [_jsx(ErrorMessage, { error: task.error, labels: t }), task.pending && _jsx("p", { role: "status", children: t.loading }), initialAccess && (_jsx(InstanceAccessPanel, { id: id, initial: initialAccess, labels: t, action: action, onChanged: () => loadTimeline(false) })), attachmentBase && (_jsx(AttachmentsPanel, { id: id, basePath: attachmentBase, labels: t, action: action, onChanged: () => loadTimeline(false) })), documentDownloadUrl && (_jsx(DocumentsPanel, { id: id, downloadUrl: documentDownloadUrl, labels: t, action: action, onChanged: () => loadTimeline(false) })), _jsxs("section", { className: `${box} flex flex-col gap-4`, children: [_jsxs("dl", { className: "grid gap-3 sm:grid-cols-2", children: [_jsxs("div", { children: [_jsx("dt", { children: t.state }), _jsx("dd", { children: state })] }), _jsxs("div", { children: [_jsx("dt", { children: t.number }), _jsx("dd", { children: initialNumbers?.length
251
255
  ? initialNumbers.join(', ')
252
256
  : numbers.length
253
257
  ? numbers.map((e) => textValue(e.details.number)).join(', ')
@@ -283,7 +287,21 @@ export function DetailClient({ id, data, fields, state, submitterId, submitterNa
283
287
  (people.length ? (_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx("select", { className: selectClass, "aria-label": t.mention, value: selected, onChange: (e) => setSelected(e.target.value), children: people.map((p) => (_jsx("option", { value: p.id, children: p.name ?? t.unnamed }, p.id))) }), _jsx(Button, { type: "button", disabled: task.pending || !selected, variant: "outline", onClick: () => setBody((value) => `${value}${value ? ' ' : ''}@{${selected}} `), children: t.mention })] })) : (_jsx("p", { children: t.noPeople }))), _jsx(Button, { type: "submit", disabled: task.pending || !body.trim() || body.length > 4000, children: t.sendComment })] })] }));
284
288
  }
285
289
  function TimelineItem({ event, labels: t }) {
286
- return (_jsxs("li", { className: "min-w-0 border-l-2 pl-3", children: [_jsxs("p", { className: "text-sm text-muted-foreground", children: [_jsx("time", { dateTime: event.at, children: event.at }), " \u00B7 ", t[event.kind], " \u00B7 ", textValue(event.actorId)] }), event.kind === 'COMMENT' ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "whitespace-pre-wrap break-words", children: textValue(event.details.body) }), event.details.isAiGenerated === true && (_jsx("strong", { className: "text-sm", children: t.aiGenerated }))] })) : (_jsx("p", { className: "whitespace-pre-wrap break-words", children: textValue(event.details) }))] }));
290
+ return (_jsxs("li", { className: "min-w-0 border-l-2 pl-3 [overflow-wrap:anywhere]", children: [_jsxs("p", { className: "text-sm text-muted-foreground", children: [_jsx("time", { dateTime: event.at, children: event.at }), " \u00B7 ", t[event.kind], " \u00B7 ", textValue(event.actorId)] }), event.kind === 'COMMENT' ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "whitespace-pre-wrap break-words", children: textValue(event.details.body) }), event.details.isAiGenerated === true && (_jsx("strong", { className: "text-sm", children: t.aiGenerated }))] })) : event.kind === 'DOCUMENT' ? (_jsx("p", { className: "whitespace-pre-wrap break-words", children: documentEventText(event, t) })) : event.kind === 'ATTACHMENT' ? (_jsx("p", { className: "whitespace-pre-wrap break-words", children: attachmentEventText(event, t) })) : (_jsx("p", { className: "whitespace-pre-wrap break-words", children: textValue(event.details) }))] }));
291
+ }
292
+ function documentEventText(event, t) {
293
+ const document = event.details.decision?.document;
294
+ const format = document?.format === 'pdf' ? 'PDF' : document?.format === 'docx' ? 'Word' : '—';
295
+ return `${t.documentGeneratedEvent}: ${textValue(document?.templateCode)} · ${t.documentVersion}: ${textValue(document?.templateVersion)} · ${t.documentFormat}: ${format} · ${t.documentActor}: ${textValue(event.actorId)}`;
296
+ }
297
+ function attachmentEventText(event, t) {
298
+ const attachment = event.details.decision?.attachment;
299
+ const verb = attachment?.action === 'UPLOADED'
300
+ ? t.attachmentUploadedEvent
301
+ : attachment?.action === 'DELETED'
302
+ ? t.attachmentDeletedEvent
303
+ : t.ATTACHMENT;
304
+ return `${verb}: ${textValue(attachment?.fileName)}`;
287
305
  }
288
306
  export function JobStatus({ initial, labels: t, action, downloadBase, onComplete, }) {
289
307
  const [job, setJob] = useState(initial), [attempt, setAttempt] = useState(0), [stopped, setStopped] = useState(false);