@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 +49 -0
- package/agent-guide.md +22 -0
- package/dist/attachment-messages.d.ts +27 -0
- package/dist/attachment-messages.d.ts.map +1 -0
- package/dist/attachment-messages.js +33 -0
- package/dist/attachment-messages.js.map +1 -0
- package/dist/attachments-client.d.ts +14 -0
- package/dist/attachments-client.d.ts.map +1 -0
- package/dist/attachments-client.js +160 -0
- package/dist/attachments-client.js.map +1 -0
- package/dist/client.d.ts +5 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +21 -3
- package/dist/client.js.map +1 -1
- package/dist/contracts.d.ts +42 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +4 -0
- package/dist/contracts.js.map +1 -1
- package/dist/document-messages.d.ts +17 -0
- package/dist/document-messages.d.ts.map +1 -0
- package/dist/document-messages.js +20 -0
- package/dist/document-messages.js.map +1 -0
- package/dist/documents-client.d.ts +13 -0
- package/dist/documents-client.d.ts.map +1 -0
- package/dist/documents-client.js +156 -0
- package/dist/documents-client.js.map +1 -0
- package/dist/messages.d.ts +19 -0
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +31 -0
- package/dist/messages.js.map +1 -1
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +7 -1
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
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":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,IAAI,GAAG,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;IACpE,MAAM,OAAO,GAAG,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,OAAO,GAAG,MAAM,CAAC,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;AAC3D,CAAC;AACD,0FAA0F;AAC1F,MAAM,UAAU,kBAAkB,CAAC,IAAU,EAAE,MAAgC,EAAE,KAAa;IAC5F,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,IAAI,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,YAAY;QAAE,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QACjF,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC5C,IAAI,KAAK,IAAI,MAAM,CAAC,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACvE,OAAO,KAAK,CAAC;AACf,CAAC;AASD,wFAAwF;AACxF,MAAM,UAAU,gBAAgB,CAAC,EAC/B,EAAE,EACF,QAAQ,EACR,OAAO,EACP,MAAM,EAAE,CAAC,EACT,MAAM,EACN,SAAS,GAQV;IACC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,EAAe,CAAC;IAC1D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAiB,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,EAAU,CAAC;IACjD,MAAM,SAAS,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,EACzB,GAAG,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,GAAG,QAAQ,IAAI,kBAAkB,CAAC,EAAE,CAAC,cAAc,CAAC;IAClE,KAAK,UAAU,OAAO;QACpB,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,YAAY,CAAC,SAAS,CAAC,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAiB,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC;YAC7E,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,CAAC,SAAS,CAAC,CAAC;gBACnB,YAAY,CAAC,KAAK,YAAY,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,CAAC,CAAC;YAC1F,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,MAAM,CAAC,OAAO;gBAAE,UAAU,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,gIAAgI;IAChI,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO;YAAE,KAAK,OAAO,EAAE,CAAC;QAC7B,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;YACvB,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,IAAI,EAAE,MAAM,EAAE,SAAS,KAAK,CAAC,CAAC;IAC1E,SAAS,MAAM,CAAC,KAAa;QAC3B,IAAI,QAAQ,IAAI,CAAC,IAAI,EAAE,SAAS,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,OAAO;QAC1D,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;YACvB,MAAM,OAAO,GAAmB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC1D,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;gBACvB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,CAAC;gBACX,MAAM,EAAE,kBAAkB;gBAC1B,KAAK,EAAE,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;aACpD,CAAC,CAAC,CAAC;YACJ,MAAM,MAAM,GAAG,GAAG,EAAE;gBAClB,IAAI,MAAM,CAAC,OAAO;oBAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;YACrE,CAAC,CAAC;YACF,MAAM,EAAE,CAAC;YACT,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC5C,IAAI,CAAC,MAAM,CAAC,OAAO;oBAAE,MAAM;gBAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC3B,GAAG,CAAC,MAAM,GAAG,qBAAqB,CAAC;gBACnC,MAAM,EAAE,CAAC;gBACT,IAAI,CAAC;oBACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;wBAC1C,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;wBACrC,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;wBACtB,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC;wBACxC,OAAO,CAAC,OAAO,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;wBAChC,OAAO,CAAC,MAAM,CAAC,UAAU,GAAG,CAAC,KAAK,EAAE,EAAE;4BACpC,IAAI,KAAK,CAAC,gBAAgB;gCACxB,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;4BAC/E,MAAM,EAAE,CAAC;wBACX,CAAC,CAAC;wBACF,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE;4BACpB,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,MAAM,GAAG,GAAG;gCAAE,OAAO,EAAE,CAAC;iCACxD,CAAC;gCACJ,IAAI,IAAa,CAAC;gCAClB,IAAI,CAAC;oCACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC;gCAC/C,CAAC;gCAAC,MAAM,CAAC;oCACP,2CAA2C;gCAC7C,CAAC;gCACD,MAAM,CAAC,IAAI,cAAc,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;4BACtD,CAAC;wBACH,CAAC,CAAC;wBACF,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;wBAC7E,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,GAAG,EAAE,CACzC,MAAM,CAAC,IAAI,cAAc,CAAC,EAAE,GAAG,EAAE,2BAA2B,EAAE,CAAC,CAAC,CAAC;wBACnE,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;wBAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;wBAC1B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACrB,CAAC,CAAC,CAAC;oBACH,GAAG,CAAC,MAAM,GAAG,oBAAoB,CAAC;oBAClC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC;gBACrB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC;oBACtB,GAAG,CAAC,KAAK,GAAG,KAAK,YAAY,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,CAAC;gBACxF,CAAC;wBAAS,CAAC;oBACT,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrB,CAAC;gBACD,MAAM,EAAE,CAAC;gBACT,IAAI,MAAM,CAAC,OAAO;oBAAE,MAAM,OAAO,EAAE,CAAC;YACtC,CAAC;YACD,IAAI,MAAM,CAAC,OAAO;gBAAE,MAAM,SAAS,EAAE,EAAE,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,CACL,sCACmB,OAAO,eACb,IAAI,CAAC,OAAO,IAAI,OAAO,EAClC,SAAS,EAAC,gFAAgF,aAE1F,aAAI,EAAE,EAAE,OAAO,EAAE,SAAS,EAAC,eAAe,YACvC,CAAC,CAAC,gBAAgB,GAChB,EACJ,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,YAAG,IAAI,EAAC,QAAQ,YAAE,CAAC,CAAC,OAAO,GAAK,EAC9D,KAAC,YAAY,IAAC,KAAK,EAAE,SAAS,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,GAAI,EAC3D,KAAC,MAAM,IACL,IAAI,EAAC,QAAQ,EACb,OAAO,EAAC,SAAS,EACjB,QAAQ,EAAE,IAAI,CAAC,OAAO,IAAI,OAAO,EACjC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,OAAO,EAAE,YAE5B,CAAC,CAAC,iBAAiB,GACb,EACR,IAAI,IAAI,CACP,8BACG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,sBAAI,CAAC,CAAC,KAAK,GAAK,EACvC,aAAI,SAAS,EAAC,6BAA6B,YACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACxB,cAEE,SAAS,EAAC,wDAAwD,wBAC9C,IAAI,CAAC,EAAE,aAE3B,cAAI,SAAS,EAAC,mCAAmC,aAC/C,0BACE,uBAAK,CAAC,CAAC,cAAc,GAAM,EAC3B,uBAAK,IAAI,CAAC,QAAQ,GAAM,IACpB,EACN,0BACE,uBAAK,CAAC,CAAC,cAAc,GAAM,EAC3B,uBAAK,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAM,IAChC,EACN,0BACE,uBAAK,CAAC,CAAC,cAAc,GAAM,EAC3B,uBAAK,IAAI,CAAC,mBAAmB,GAAM,IAC/B,EACN,0BACE,uBAAK,CAAC,CAAC,gBAAgB,GAAM,EAC7B,uBACG,IAAI,CAAC,MAAM,KAAK,OAAO;wDACtB,CAAC,CAAC,CAAC,CAAC,eAAe;wDACnB,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,cAAc;4DAC9B,CAAC,CAAC,CAAC,CAAC,iBAAiB;4DACrB,CAAC,CAAC,CAAC,CAAC,kBAAkB,GACvB,IACD,EACN,0BACE,uBAAK,CAAC,CAAC,UAAU,GAAM,EACvB,uBAAK,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,UAAU,GAAM,IAC7C,EACN,0BACE,uBAAK,CAAC,CAAC,SAAS,GAAM,EACtB,uBACE,eAAM,QAAQ,EAAE,IAAI,CAAC,SAAS,YAAG,IAAI,CAAC,SAAS,GAAQ,GACpD,IACD,IACH,EACL,eAAK,SAAS,EAAC,2BAA2B,aACvC,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,CAC1B,YACE,SAAS,EAAC,WAAW,EACrB,IAAI,EAAE,GAAG,KAAK,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,YAEtD,CAAC,CAAC,kBAAkB,GACnB,CACL,EACA,IAAI,CAAC,SAAS,IAAI,CACjB,KAAC,MAAM,IACL,IAAI,EAAC,QAAQ,EACb,OAAO,EAAC,SAAS,EACjB,QAAQ,EAAE,IAAI,CAAC,OAAO,EACtB,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,YAEjC,CAAC,CAAC,gBAAgB,GACZ,CACV,IACG,EACL,OAAO,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,SAAS,IAAI,CACxC,kCACc,CAAC,CAAC,uBAAuB,EACrC,SAAS,EAAC,wCAAwC,aAElD,sBAAI,CAAC,CAAC,uBAAuB,GAAK,EAClC,KAAC,MAAM,IACL,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,IAAI,CAAC,OAAO,EACtB,OAAO,EAAE,GAAG,EAAE,CACZ,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;gDACvB,MAAM,IAAI,CAAC,MAAM,EAAE;oDACjB,IAAI,EAAE,kBAAkB;oDACxB,EAAE;oDACF,YAAY,EAAE,IAAI,CAAC,EAAE;iDACtB,CAAC,CAAC;gDACH,UAAU,CAAC,SAAS,CAAC,CAAC;gDACtB,MAAM,OAAO,EAAE,CAAC;gDAChB,MAAM,SAAS,EAAE,EAAE,CAAC;4CACtB,CAAC,CAAC,YAGH,CAAC,CAAC,yBAAyB,GACrB,EACT,KAAC,MAAM,IACL,IAAI,EAAC,QAAQ,EACb,OAAO,EAAC,SAAS,EACjB,QAAQ,EAAE,IAAI,CAAC,OAAO,EACtB,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,YAEnC,CAAC,CAAC,sBAAsB,GAClB,IACA,CACZ,KA3FI,IAAI,CAAC,EAAE,CA4FT,CACN,CAAC,GACC,EACJ,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,CAC1B,8BACG,IAAI,CAAC,MAAM,IAAI,CACd,wBACG,CAAC,CAAC,mBAAmB,QAAI,IAAI,CAAC,MAAM,CAAC,SAAS,QAAI,GAAG,EACrD,IAAI,CAAC,MAAM,CAAC,yBAAyB,IACpC,CACL,EACA,IAAI,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,IAAI,YAAG,IAAI,EAAC,QAAQ,YAAE,CAAC,CAAC,eAAe,GAAK,EACzE,gBACE,GAAG,EAAE,SAAS,EACd,IAAI,EAAC,MAAM,EACX,QAAQ,sBACI,CAAC,CAAC,iBAAiB,EAC/B,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAC,SAAS,EACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;oCAClB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;oCACpD,KAAK,CAAC,aAAa,CAAC,KAAK,GAAG,EAAE,CAAC;gCACjC,CAAC,GACD,EACF,KAAC,MAAM,IAAC,IAAI,EAAC,QAAQ,EAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,YAChF,CAAC,CAAC,iBAAiB,GACb,EACT,iBACE,IAAI,EAAC,QAAQ,gBACD,CAAC,CAAC,cAAc,EAC5B,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAC,4HAA4H,EACtI,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,EACzC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EAC7C,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oCAChB,KAAK,CAAC,cAAc,EAAE,CAAC;oCACvB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;gCAC/C,CAAC,YAEA,CAAC,CAAC,cAAc,GACV,IACR,CACJ,IACA,CACJ,EACD,0BAAc,QAAQ,EAAC,SAAS,EAAC,kCAAkC,YAChE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,cAAiB,SAAS,EAAC,cAAc,aACvC,wBACG,GAAG,CAAC,IAAI,cAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IACzB,EACH,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,CACrB,0BACE,sBAAI,CAAC,CAAC,kBAAkB,GAAK,EAC5B,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACvB,sBAAe,CAAC,CAAC,IAAI,CAAC,IAAd,IAAI,CAAe,CAC5B,CAAC,IACE,CACP,EACD,mBACE,KAAK,EAAE,GAAG,CAAC,QAAQ,EACnB,GAAG,EAAE,GAAG,gBACI,GAAG,CAAC,CAAC,mBAAmB,KAAK,GAAG,CAAC,IAAI,EAAE,EACnD,SAAS,EAAC,QAAQ,GAClB,EACF,KAAC,YAAY,IAAC,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,GAAI,KAlBtC,GAAG,CAAC,EAAE,CAmBV,CACN,CAAC,GACC,IACG,CACX,CAAC;AACJ,CAAC"}
|
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;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"
|
|
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);
|