@haneinfo/frontend-instances 0.2.0 → 0.4.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
@@ -1,73 +1,119 @@
1
- # @haneinfo/frontend-instances
2
-
3
- Reusable instance lists, collaboration and import/export pages for React 19 / Next 16.
4
- Backend packages stay free of React and NestJS is not a dependency of this package.
5
-
6
- ## Mounting
7
-
8
- Import `renderInstanceListPage`, `renderInstanceDetailPage` and `renderImportPage` from
9
- `@haneinfo/frontend-instances/server`. Each returns a server-renderable React tree. Supply:
10
-
11
- - `apiFetch<T>(path, init?)`, using the current authenticated session and an App-owned API URL.
12
- - `LinkComponent`, `t(LabelKey)`, `getCurrentUser()`, and `basePath`.
13
- - An App Server Action calling `performInstanceRequest(apiFetch, request)` and returning
14
- `{ data }` or `{ error: instanceError(error) }` after checking the session.
15
- - For imports, an authenticated `uploadAction(FormData)` calling `readImportUpload`, plus
16
- the App's `{ code, label }[]` machine choices.
17
-
18
- The list additionally takes `machineCode` and promised `searchParams: { query?: string }`.
19
- The detail takes `id`. The import takes `machines` and `uploadAction`.
20
- All UI labels are injected. System field labels use `t`; Form labels stay verbatim from the API.
21
- See `LabelKey` and the complete English/zh-TW mounting examples in
22
- `validation/equipment-loan/instance-frontend/app/` in the source repository.
23
-
24
- Add `@source "../../node_modules/@haneinfo/frontend-instances/dist";` to the App's Tailwind
25
- CSS source declarations. Mount navigation in the App's sidebar, not generated plugin files.
26
- Set the App's Server Action body limit to allow multipart uploads (the example uses 16 MB).
27
- The backend HTTP body limit remains App-owned; API rejections are shown in the UI.
28
-
29
- ## Client components and contracts
30
-
31
- `@haneinfo/frontend-instances/client` exports `ListClient`, `DetailClient`, `ImportClient`,
32
- `FilterEditor`, `JobStatus`, `JobReport`, `ImportConfirmation` and `prepareFilter`.
33
- Use the server renderers for normal mounting. `@haneinfo/frontend-instances/contracts` exports
34
- HTTP-only types and `labelKeys`. The server entry also exports the contracts, `dateRange`,
35
- `importMapping`, `normalizeQuery`, `performInstanceRequest`, `readImportUpload` and `instanceError`.
36
-
37
- Lists support PREDICATE/GROUP AND/OR, all eight operators, inclusive date ranges, up to eight
38
- sort keys, selected columns and offset/limit pagination (1–500). IN uses one literal per line.
39
- Datetime-local controls are explicitly UTC. Exports use the last applied query, not unapplied
40
- editor changes. Saved views default to private; sharing controls depend only on `canShare`.
41
-
42
- Details use pinned subject Form labels and the merged timeline; mentions come only from the
43
- instance-specific mentionable API. Following comes from the timeline or mutation response.
44
- Comments are append-only plain text and AI comments have a visible attribution.
45
-
46
- CSV and xlsx headers are read using csv-parse and ExcelJS on the server, with a 10 MB upload
47
- bound. The backend performs the complete preflight and authorization. Duplicate keys are
48
- optional, mapped top-level keys with a five-key maximum. Preflight never confirms an import.
49
- Confirmation requires a separate button click; global errors and zero valid rows disable it.
50
- Re-upload to change a completed preflight's mapping.
51
-
52
- `JobStatus` polls every two seconds, stops on terminal status or failure, and bounds automatic
53
- polling to 90 requests. A manual refresh resumes polling. Job state is owner-authorized by the
54
- backend. Binary result bytes and uploaded job params are removed before returning jobs to clients.
55
-
56
- ## Downloads and safety
57
-
58
- Mount `${basePath}/download?path=...` as an authenticated, no-store streaming GET proxy. Constrain
59
- the path to `/import-export/(imports|exports)/<id>/file`, forward the current session token to
60
- that exact backend-provided `downloadPath`, and forward its disposition/type. Never synthesize
61
- job download paths or put file bytes/tokens in the URL. The App example includes this route.
62
-
63
- Use real Next Server Actions so same-origin CSRF checks apply. All mutations have an immediate
64
- in-memory submission lock and disabled pending buttons. The backend remains authoritative for
65
- authorization and validation; the UI does not recreate RBAC. 401/403/404 display generic localized
66
- messages without raw API details. All user content is rendered as text nodes, including errors,
67
- field values, names, and timeline details. No HTML or Markdown rendering is used.
68
-
69
- No instance creation, submission, approval, rejection or transition controls are included.
70
-
71
- ## Submitter display
72
-
73
- List `$submitterId` cells and the detail submitter field display `submitterName ?? submitterId`. Both are plain React text nodes, including markup-like names. The HTTP contracts include nullable submitterName; older responses without it also fall back to the id. No new translation key, lookup request or App host-page change is required. Upgrade the state-machine and list-view backends with this frontend for names; otherwise the id remains visible.
1
+ # @haneinfo/frontend-instances
2
+
3
+ Reusable instance lists, collaboration and import/export pages for React 19 / Next 16.
4
+ Backend packages stay free of React and NestJS is not a dependency of this package.
5
+
6
+ ## Mounting
7
+
8
+ Import `renderInstanceListPage`, `renderInstanceDetailPage` and `renderImportPage` from
9
+ `@haneinfo/frontend-instances/server`. Each returns a server-renderable React tree. Supply:
10
+
11
+ - `apiFetch<T>(path, init?)`, using the current authenticated session and an App-owned API URL.
12
+ - `LinkComponent`, `t(LabelKey)`, `getCurrentUser()`, and `basePath`.
13
+ - An App Server Action calling `performInstanceRequest(apiFetch, request)` and returning
14
+ `{ data }` or `{ error: instanceError(error) }` after checking the session.
15
+ - For imports, an authenticated `uploadAction(FormData)` calling `readImportUpload`, plus
16
+ the App's `{ code, label }[]` machine choices.
17
+
18
+ The list additionally takes `machineCode` and promised `searchParams: { query?: string }`.
19
+ The detail takes `id`. The import takes `machines` and `uploadAction`.
20
+ All UI labels are injected. System field labels use `t`; Form labels stay verbatim from the API.
21
+ See `LabelKey` and the complete English/zh-TW mounting examples in
22
+ `validation/equipment-loan/instance-frontend/app/` in the source repository.
23
+
24
+ Add `@source "../../node_modules/@haneinfo/frontend-instances/dist";` to the App's Tailwind
25
+ CSS source declarations. Mount navigation in the App's sidebar, not generated plugin files.
26
+ Set the App's Server Action body limit to allow multipart uploads (the example uses 16 MB).
27
+ The backend HTTP body limit remains App-owned; API rejections are shown in the UI.
28
+
29
+ ## Client components and contracts
30
+
31
+ `@haneinfo/frontend-instances/client` exports `ListClient`, `DetailClient`, `ImportClient`,
32
+ `FilterEditor`, `JobStatus`, `JobReport`, `ImportConfirmation` and `prepareFilter`.
33
+ Use the server renderers for normal mounting. `@haneinfo/frontend-instances/contracts` exports
34
+ HTTP-only types and `labelKeys`. The server entry also exports the contracts, `dateRange`,
35
+ `importMapping`, `normalizeQuery`, `performInstanceRequest`, `readImportUpload` and `instanceError`.
36
+
37
+ Lists support PREDICATE/GROUP AND/OR, all eight operators, inclusive date ranges, up to eight
38
+ sort keys, selected columns and offset/limit pagination (1–500). IN uses one literal per line.
39
+ Datetime-local controls are explicitly UTC. Exports use the last applied query, not unapplied
40
+ editor changes. Saved views default to private; sharing controls depend only on `canShare`.
41
+
42
+ Details use pinned subject Form labels and the merged timeline; mentions come only from the
43
+ instance-specific mentionable API. Following comes from the timeline or mutation response.
44
+ Comments are append-only plain text and AI comments have a visible attribution.
45
+
46
+ CSV and xlsx headers are read using csv-parse and ExcelJS on the server, with a 10 MB upload
47
+ bound. The backend performs the complete preflight and authorization. Duplicate keys are
48
+ optional, mapped top-level keys with a five-key maximum. Preflight never confirms an import.
49
+ Confirmation requires a separate button click; global errors and zero valid rows disable it.
50
+ Re-upload to change a completed preflight's mapping.
51
+
52
+ `JobStatus` polls every two seconds, stops on terminal status or failure, and bounds automatic
53
+ polling to 90 requests. A manual refresh resumes polling. Job state is owner-authorized by the
54
+ backend. Binary result bytes and uploaded job params are removed before returning jobs to clients.
55
+
56
+ ## Downloads and safety
57
+
58
+ Mount `${basePath}/download?path=...` as an authenticated, no-store streaming GET proxy. Constrain
59
+ the path to `/import-export/(imports|exports)/<id>/file`, forward the current session token to
60
+ that exact backend-provided `downloadPath`, and forward its disposition/type. Never synthesize
61
+ job download paths or put file bytes/tokens in the URL. The App example includes this route.
62
+
63
+ Use real Next Server Actions so same-origin CSRF checks apply. All mutations have an immediate
64
+ in-memory submission lock and disabled pending buttons. The backend remains authoritative for
65
+ authorization and validation; the UI does not recreate RBAC. 401/403/404 display generic localized
66
+ messages without raw API details. All user content is rendered as text nodes, including errors,
67
+ field values, names, and timeline details. No HTML or Markdown rendering is used.
68
+
69
+ No instance creation, submission, approval, rejection or transition controls are included.
70
+
71
+ ## Submitter display
72
+
73
+ List `$submitterId` cells and the detail submitter field display `submitterName ?? submitterId`. Both are plain React text nodes, including markup-like names. The HTTP contracts include nullable submitterName; older responses without it also fall back to the id. No new translation key, lookup request or App host-page change is required. Upgrade the state-machine and list-view backends with this frontend for names; otherwise the id remains visible.
74
+
75
+
76
+ ## Delegation and access management (draft 202610-e2a9)
77
+
78
+ `renderDelegationsPage` accepts the same injected page dependencies as instance pages. The App
79
+ mounts it at `/dashboard/delegations` and supplies an authenticated Server Action calling
80
+ `performInstanceRequest`. Administrative controls depend on the successful admin list API;
81
+ only DELEGATION_NOT_FOUND is treated as a denial. Other failures render an error.
82
+ The UTC datetime inputs require an explicit end; backend rules remain authoritative.
83
+
84
+ Pass `showAccess: true` to `renderInstanceDetailPage` to load the access list. `InstanceAccessPanel`
85
+ is exported from the client entry for other hosts. It renders USER/ROLE, READ/WRITE, sources,
86
+ grantor and timestamps. Only API canManage enables management, and only GRANTED rows have
87
+ change/remove controls. Mutations refresh both the list and the enclosing activity timeline.
88
+ Pickers use bounded explicit searches and keyboard combobox selection; user search needs two
89
+ trimmed characters. Every mutation uses the supplied Server Action and a synchronous submit lock.
90
+
91
+ Merge the bilingual catalog fragments in validation/equipment-loan/delegation-access-frontend/app/
92
+ into the host's instances catalog. The new keys include the known error code allowlist.
93
+ `instanceError` only maps recognized codes. Unknown/missing codes, even on 401/403/404, use
94
+ requestFailed. Backend message, diagnostic message and lastError text are never rendered.
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.
package/agent-guide.md CHANGED
@@ -1,39 +1,65 @@
1
- # Instance frontend agent guide
2
-
3
- Use this package to mount shared instance lists, read-only details with collaboration, and
4
- human-confirmed bulk imports. Read the README for exact entry points and injected dependencies.
5
- Do not add React or Next dependencies to list-view, collaboration, import-export or background-jobs.
6
-
7
- ## HTTP contracts
8
-
9
- - List metadata: GET `/list-view/machines/:code/fields`; query: POST the same machine's `/query`.
10
- - Views: GET/POST `/list-view/views`; PATCH `/list-view/views/:id/sharing`. Preserve `canShare`.
11
- - Details: GET `/state-machine-instances/:id` uses the pinned subject Form bundle.
12
- - Timeline: GET `/state-machine-instances/:id/timeline?offset=&limit=50`, including `following`.
13
- - People: GET `/state-machine-instances/:id/mentionable?q=`, at most twenty eligible people.
14
- - Collaboration: POST `comments` with `{ body }`, using `@{id}` from the people picker;
15
- POST/DELETE `follow`. There are no comment edit/delete controls.
16
- - Imports: GET `/import-export/machines/:code/fields`; POST `/import-export/imports/preflight`
17
- with file format/base64, header-to-key mapping, and optional duplicateKey (one to five keys).
18
- - Human confirmation: POST `/import-export/imports/:id/confirm` with `{ confirmed: true }` only
19
- from an explicit button event, never from polling, render, an effect, or an AI tool.
20
- - Exports: POST `/import-export/exports` with `{ format, source: { machineCode, query } }`.
21
- - Jobs: GET `/background-jobs/:id`; downloads use the result's exact `downloadPath` through
22
- an App-owned authenticated streaming proxy. The API alone decides job ownership and expiry.
23
-
24
- The minimal types are local HTTP contracts; never import backend runtime packages for types.
25
- Keep the API adapter and authentication in the App, with Server Action CSRF protection.
26
- Keep API error values and all user-controlled content as plain text. Do not inspect role names
27
- to decide sharing or visibility. Use both English and zh-TW translations for new UI text.
28
-
29
- ## Validation
30
-
31
- Run package build/typecheck/tests and workspace build:graph, lint, specs:lint. Component tests
32
- render actual HTML, test authorization and escaping, and verify confirmation and parameters.
33
- Use the equipment-loan validation overlay for full production-build/browser acceptance. Always
34
- restore and finish it, including original manifest/lockfile bytes and dependency installation.
35
- Do not release, version, approve a decision, or commit as part of frontend validation.
36
-
37
- ## Submitter display
38
-
39
- List `$submitterId` cells and the detail submitter field display `submitterName ?? submitterId`. Both are plain React text nodes, including markup-like names. The HTTP contracts include nullable submitterName; older responses without it also fall back to the id. No new translation key, lookup request or App host-page change is required. Upgrade the state-machine and list-view backends with this frontend for names; otherwise the id remains visible.
1
+ # Instance frontend agent guide
2
+
3
+ Use this package to mount shared instance lists, read-only details with collaboration, and
4
+ human-confirmed bulk imports. Read the README for exact entry points and injected dependencies.
5
+ Do not add React or Next dependencies to list-view, collaboration, import-export or background-jobs.
6
+
7
+ ## HTTP contracts
8
+
9
+ - List metadata: GET `/list-view/machines/:code/fields`; query: POST the same machine's `/query`.
10
+ - Views: GET/POST `/list-view/views`; PATCH `/list-view/views/:id/sharing`. Preserve `canShare`.
11
+ - Details: GET `/state-machine-instances/:id` uses the pinned subject Form bundle.
12
+ - Timeline: GET `/state-machine-instances/:id/timeline?offset=&limit=50`, including `following`.
13
+ - People: GET `/state-machine-instances/:id/mentionable?q=`, at most twenty eligible people.
14
+ - Collaboration: POST `comments` with `{ body }`, using `@{id}` from the people picker;
15
+ POST/DELETE `follow`. There are no comment edit/delete controls.
16
+ - Imports: GET `/import-export/machines/:code/fields`; POST `/import-export/imports/preflight`
17
+ with file format/base64, header-to-key mapping, and optional duplicateKey (one to five keys).
18
+ - Human confirmation: POST `/import-export/imports/:id/confirm` with `{ confirmed: true }` only
19
+ from an explicit button event, never from polling, render, an effect, or an AI tool.
20
+ - Exports: POST `/import-export/exports` with `{ format, source: { machineCode, query } }`.
21
+ - Jobs: GET `/background-jobs/:id`; downloads use the result's exact `downloadPath` through
22
+ an App-owned authenticated streaming proxy. The API alone decides job ownership and expiry.
23
+
24
+ The minimal types are local HTTP contracts; never import backend runtime packages for types.
25
+ Keep the API adapter and authentication in the App, with Server Action CSRF protection.
26
+ Keep API error values and all user-controlled content as plain text. Do not inspect role names
27
+ to decide sharing or visibility. Use both English and zh-TW translations for new UI text.
28
+
29
+ ## Validation
30
+
31
+ Run package build/typecheck/tests and workspace build:graph, lint, specs:lint. Component tests
32
+ render actual HTML, test authorization and escaping, and verify confirmation and parameters.
33
+ Use the equipment-loan validation overlay for full production-build/browser acceptance. Always
34
+ restore and finish it, including original manifest/lockfile bytes and dependency installation.
35
+ Do not release, version, approve a decision, or commit as part of frontend validation.
36
+
37
+ ## Submitter display
38
+
39
+ List `$submitterId` cells and the detail submitter field display `submitterName ?? submitterId`. Both are plain React text nodes, including markup-like names. The HTTP contracts include nullable submitterName; older responses without it also fall back to the id. No new translation key, lookup request or App host-page change is required. Upgrade the state-machine and list-view backends with this frontend for names; otherwise the id remains visible.
40
+
41
+
42
+ ## Delegations, access lists and safe errors
43
+
44
+ Implemented against draft 202610-e2a9; approval/versioning remain with the maintainer.
45
+ Use renderDelegationsPage and showAccess on renderInstanceDetailPage as documented in README.
46
+ Lookup routes are GET /identity/users/lookup?q= (two trimmed characters) and
47
+ GET /rbac/roles/lookup?q= (empty allowed), at most twenty projected public labels.
48
+ GET instance access and the read-only list_instance_access MCP result now include canManage.
49
+ Never infer authority from role names or HTTP status. Admin delegation visibility comes from
50
+ GET /delegations; only DELEGATION_NOT_FOUND means this operation is unavailable to the caller.
51
+ Use a same-origin authenticated Server Action for writes. Permanent sources have no controls.
52
+ Translate only exact known codes in src/messages.ts; never show backend prose or job lastError.
53
+ Both current pages and the new management pages follow this policy. Merge the complete English
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.
@@ -0,0 +1,16 @@
1
+ import type { ActionError, InstanceAction, InstanceRequest, Labels } from './contracts.js';
2
+ export declare class RequestFailure extends Error {
3
+ readonly reason: ActionError;
4
+ constructor(reason: ActionError);
5
+ }
6
+ export declare function call<T>(action: InstanceAction, request: InstanceRequest): Promise<T>;
7
+ export declare function useTask(): {
8
+ pending: boolean;
9
+ error: ActionError | undefined;
10
+ run: (work: () => Promise<void>) => Promise<void>;
11
+ };
12
+ export declare function ErrorMessage({ error, labels }: {
13
+ error?: ActionError;
14
+ labels: Labels;
15
+ }): import("react").JSX.Element | null;
16
+ //# sourceMappingURL=action-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-client.d.ts","sourceRoot":"","sources":["../src/action-client.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC3F,qBAAa,cAAe,SAAQ,KAAK;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW;gBAAnB,MAAM,EAAE,WAAW;CAGzC;AACD,wBAAsB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAI1F;AACD,wBAAgB,OAAO;;;gBAII,MAAM,OAAO,CAAC,IAAI,CAAC;EAe7C;AACD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,sCAMtF"}
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useRef, useState } from 'react';
4
+ export class RequestFailure extends Error {
5
+ constructor(reason) {
6
+ super(reason.key);
7
+ this.reason = reason;
8
+ }
9
+ }
10
+ export async function call(action, request) {
11
+ const result = await action(request);
12
+ if (result.error)
13
+ throw new RequestFailure(result.error);
14
+ return result.data;
15
+ }
16
+ export function useTask() {
17
+ const lock = useRef(false);
18
+ const [pending, setPending] = useState(false);
19
+ const [error, setError] = useState();
20
+ async function run(work) {
21
+ if (lock.current)
22
+ return;
23
+ lock.current = true;
24
+ setPending(true);
25
+ setError(undefined);
26
+ try {
27
+ await work();
28
+ }
29
+ catch (error) {
30
+ setError(error instanceof RequestFailure ? error.reason : { key: 'requestFailed' });
31
+ }
32
+ finally {
33
+ lock.current = false;
34
+ setPending(false);
35
+ }
36
+ }
37
+ return { pending, error, run };
38
+ }
39
+ export function ErrorMessage({ error, labels }) {
40
+ return error ? (_jsx("p", { role: "alert", className: "break-words text-destructive", children: labels[error.key] })) : null;
41
+ }
42
+ //# sourceMappingURL=action-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-client.js","sourceRoot":"","sources":["../src/action-client.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AACb,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEzC,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YAAqB,MAAmB;QACtC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QADC,WAAM,GAAN,MAAM,CAAa;IAExC,CAAC;CACF;AACD,MAAM,CAAC,KAAK,UAAU,IAAI,CAAI,MAAsB,EAAE,OAAwB;IAC5E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,KAAK;QAAE,MAAM,IAAI,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,OAAO,MAAM,CAAC,IAAS,CAAC;AAC1B,CAAC;AACD,MAAM,UAAU,OAAO;IACrB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAe,CAAC;IAClD,KAAK,UAAU,GAAG,CAAC,IAAyB;QAC1C,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,SAAS,CAAC,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,KAAK,YAAY,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,CAAC,CAAC;QACtF,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YACrB,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACjC,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAA2C;IACrF,OAAO,KAAK,CAAC,CAAC,CAAC,CACb,YAAG,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,8BAA8B,YACrD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,GAChB,CACL,CAAC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC"}
@@ -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): ("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,64CAS7F;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"}