@haneinfo/frontend-instances 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (c) 2026 Haneinfo. All Rights Reserved.
2
+
3
+ This software and associated documentation files (the "Software") are the proprietary and
4
+ confidential property of Haneinfo. No part of the Software may be copied, modified, merged,
5
+ published, distributed, sublicensed, or sold without the prior written permission of Haneinfo.
6
+
7
+ The Software is provided for internal use by Haneinfo and its authorized personnel only, and is
8
+ NOT licensed for external distribution or open-source use.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
11
+ NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
12
+ NONINFRINGEMENT. IN NO EVENT SHALL HANEINFO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
14
+ THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,69 @@
1
- # Temporary Holding Version
1
+ # @haneinfo/frontend-instances
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.
package/agent-guide.md ADDED
@@ -0,0 +1,35 @@
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.
@@ -0,0 +1,62 @@
1
+ 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';
2
+ export declare function FilterEditor({ condition, fields, labels, onChange, depth, }: {
3
+ condition: Condition;
4
+ fields: QueryField[];
5
+ labels: Labels;
6
+ onChange: (value: Condition) => void;
7
+ depth?: number;
8
+ }): import("react").JSX.Element;
9
+ declare function prepareFilter(condition: Condition, fields: QueryField[]): Condition | undefined;
10
+ export { prepareFilter };
11
+ export declare function ListClient({ fields, initial, initialQuery, views: initialViews, machineCode, basePath, labels: t, action, }: {
12
+ fields: QueryField[];
13
+ initial: ListResult;
14
+ initialQuery: InstanceQuery;
15
+ views: SavedView[];
16
+ machineCode: string;
17
+ basePath: string;
18
+ labels: Labels;
19
+ action: InstanceAction;
20
+ }): import("react").JSX.Element;
21
+ export declare function DetailClient({ id, data, fields, state, submitterId, createdAt, initial, initialNumbers, labels: t, action, }: {
22
+ id: string;
23
+ data: Record<string, unknown>;
24
+ fields: FormField[];
25
+ state: string;
26
+ submitterId: string | null;
27
+ createdAt: string;
28
+ initial: Timeline;
29
+ initialNumbers?: string[];
30
+ labels: Labels;
31
+ action: InstanceAction;
32
+ }): import("react").JSX.Element;
33
+ export declare function JobStatus({ initial, labels: t, action, downloadBase, onComplete, }: {
34
+ initial: Job;
35
+ labels: Labels;
36
+ action: InstanceAction;
37
+ downloadBase: string;
38
+ onComplete?: (job: Job) => void;
39
+ }): import("react").JSX.Element;
40
+ export declare function JobReport({ result, labels: t, downloadBase, }: {
41
+ result: JobResult;
42
+ labels: Labels;
43
+ downloadBase: string;
44
+ }): import("react").JSX.Element;
45
+ export declare function ImportClient({ machines, fields: initialFields, labels: t, action, uploadAction, downloadBase, }: {
46
+ machines: {
47
+ code: string;
48
+ label: string;
49
+ }[];
50
+ fields: ImportField[];
51
+ labels: Labels;
52
+ action: InstanceAction;
53
+ uploadAction: UploadAction;
54
+ downloadBase: string;
55
+ }): import("react").JSX.Element;
56
+ export declare function ImportConfirmation({ job, labels: t, pending, onConfirm, }: {
57
+ job: Job;
58
+ labels: Labels;
59
+ pending: boolean;
60
+ onConfirm: () => void;
61
+ }): import("react").JSX.Element | null;
62
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"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"}