@rytass/bpm-core-react 0.11.0 → 0.12.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/CHANGELOG.md +40 -0
- package/dist/chunks/FormBuilderView-CLFlfDGe.cjs +3 -0
- package/dist/chunks/FormBuilderView-CLFlfDGe.cjs.map +1 -0
- package/dist/chunks/FormBuilderView-D4xIuPYB.js +1960 -0
- package/dist/chunks/FormBuilderView-D4xIuPYB.js.map +1 -0
- package/dist/chunks/FormRendererView-BxGT16z0.cjs +2 -0
- package/dist/chunks/FormRendererView-BxGT16z0.cjs.map +1 -0
- package/dist/chunks/FormRendererView-DUceSOXe.js +798 -0
- package/dist/chunks/FormRendererView-DUceSOXe.js.map +1 -0
- package/dist/chunks/{compose-CUOe5Ia9.js → compose-Bb-REfoB.js} +4 -4
- package/dist/chunks/{compose-CUOe5Ia9.js.map → compose-Bb-REfoB.js.map} +1 -1
- package/dist/chunks/{compose-CyRkXdeu.cjs → compose-DzRYZcqe.cjs} +2 -2
- package/dist/chunks/{compose-CyRkXdeu.cjs.map → compose-DzRYZcqe.cjs.map} +1 -1
- package/dist/chunks/{designer-Cb3WAC46.js → designer-C6oIO9Kq.js} +2 -2
- package/dist/chunks/{designer-Cb3WAC46.js.map → designer-C6oIO9Kq.js.map} +1 -1
- package/dist/chunks/{designer-zLliyrHu.cjs → designer-gFUdImeZ.cjs} +2 -2
- package/dist/chunks/{designer-zLliyrHu.cjs.map → designer-gFUdImeZ.cjs.map} +1 -1
- package/dist/chunks/{detail-Cmw-aQcu.js → detail-BgKgMvYx.js} +391 -390
- package/dist/chunks/{detail-Cmw-aQcu.js.map → detail-BgKgMvYx.js.map} +1 -1
- package/dist/chunks/{detail-Bp2Nv0cs.cjs → detail-Dnz5aaI-.cjs} +2 -2
- package/dist/chunks/{detail-Bp2Nv0cs.cjs.map → detail-Dnz5aaI-.cjs.map} +1 -1
- package/dist/pages/instances/detail/index.cjs +1 -1
- package/dist/pages/instances/detail/index.js +1 -1
- package/dist/pages/templates/compose/index.cjs +1 -1
- package/dist/pages/templates/compose/index.js +1 -1
- package/dist/pages/templates/designer/index.cjs +1 -1
- package/dist/pages/templates/designer/index.js +1 -1
- package/dist/views/forms/builder/index.cjs +1 -1
- package/dist/views/forms/builder/index.js +1 -1
- package/dist/views/forms/renderer/form-data-source-field.d.ts +16 -0
- package/dist/views/forms/renderer/index.cjs +1 -1
- package/dist/views/forms/renderer/index.js +1 -1
- package/dist/views/instances/detail/index.cjs +1 -1
- package/dist/views/instances/detail/index.js +1 -1
- package/dist/views/instances/new/index.cjs +1 -1
- package/dist/views/instances/new/index.cjs.map +1 -1
- package/dist/views/instances/new/index.js +48 -48
- package/dist/views/instances/new/index.js.map +1 -1
- package/dist/views/templates/compose/index.cjs +1 -1
- package/dist/views/templates/compose/index.js +1 -1
- package/dist/views/templates/designer/index.cjs +1 -1
- package/dist/views/templates/designer/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunks/FormBuilderView-CAqjOYAP.cjs +0 -3
- package/dist/chunks/FormBuilderView-CAqjOYAP.cjs.map +0 -1
- package/dist/chunks/FormBuilderView-TzSezeqC.js +0 -1479
- package/dist/chunks/FormBuilderView-TzSezeqC.js.map +0 -1
- package/dist/chunks/FormRendererView-BurlEDUu.js +0 -580
- package/dist/chunks/FormRendererView-BurlEDUu.js.map +0 -1
- package/dist/chunks/FormRendererView-Dde1s3I0.cjs +0 -2
- package/dist/chunks/FormRendererView-Dde1s3I0.cjs.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/detail-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/detail-Dnz5aaI-.cjs");let t=require("react/jsx-runtime");var n={title:`案件詳情 | BPM Admin`};async function r({params:n}){let{id:r}=await n;return(0,t.jsx)(e.t,{instanceId:r})}exports.default=r,exports.metadata=n;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/compose-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/compose-DzRYZcqe.cjs");let t=require("react/jsx-runtime"),n=require("next/server");var r={title:`建立模板(表單 + 流程) | BPM Admin`,description:`以精靈式流程一次完成 BPM 表單與簽核流程設計並發佈。`};async function i(){return await(0,n.connection)(),(0,t.jsx)(e.t,{aiAssistantAvailable:!!process.env.OPENAI_API_KEY,showAiAssistant:process.env.BPM_AI_ASSISTANT_ENABLED===`true`})}exports.default=i,exports.metadata=r;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as e } from "../../../chunks/compose-
|
|
1
|
+
import { t as e } from "../../../chunks/compose-Bb-REfoB.js";
|
|
2
2
|
import { jsx as t } from "react/jsx-runtime";
|
|
3
3
|
import { connection as n } from "next/server";
|
|
4
4
|
//#region src/pages/templates/compose/index.tsx
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/designer-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/designer-gFUdImeZ.cjs");let t=require("react/jsx-runtime");var n={title:`流程設計器 | BPM Admin`,description:`以視覺化設計器編輯 BPM 簽核流程節點、條件與發佈版本。`};async function r({params:n}){let{id:r}=await n;return(0,t.jsx)(e.t,{aiAssistantAvailable:!!process.env.OPENAI_API_KEY,showAiAssistant:process.env.BPM_AI_ASSISTANT_ENABLED===`true`,showDryRun:process.env.BPM_TEMPLATE_DRY_RUN_ENABLED!==`false`,templateId:r})}exports.default=r,exports.metadata=n;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/FormBuilderView-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/FormBuilderView-CLFlfDGe.cjs");exports.FormBuilderView=e.t;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "../../../chunks/FormBuilderView-
|
|
1
|
+
import { t as e } from "../../../chunks/FormBuilderView-D4xIuPYB.js";
|
|
2
2
|
export { e as FormBuilderView };
|
|
@@ -23,15 +23,31 @@ export interface FormDataSourceFieldState {
|
|
|
23
23
|
readonly retry: () => void;
|
|
24
24
|
readonly status: FormDataSourceFieldStatus;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* What makes a table cell different from a top-level field: it is addressed by
|
|
28
|
+
* a schema path, snapshotted under an instance path, and its `ROW_FIELD`
|
|
29
|
+
* bindings read from its own row rather than the form (ADR 16 §3.3, §3.5).
|
|
30
|
+
*/
|
|
31
|
+
export interface UseFormDataSourceFieldRow {
|
|
32
|
+
/** The row as it was loaded, so a returned case can tell what changed. */
|
|
33
|
+
readonly initialValues?: FormRendererValues;
|
|
34
|
+
readonly values: FormRendererValues;
|
|
35
|
+
}
|
|
26
36
|
export interface UseFormDataSourceFieldInput {
|
|
27
37
|
readonly context?: FormRendererDataSourceContext;
|
|
28
38
|
readonly field: FormFieldDefinition;
|
|
39
|
+
/** Schema path used to address the field; defaults to its own key. */
|
|
40
|
+
readonly fieldPath?: string;
|
|
29
41
|
readonly formData: FormRendererValues;
|
|
30
42
|
readonly initialFormData?: FormRendererValues;
|
|
31
43
|
readonly initialValue?: FormFieldValue | undefined;
|
|
32
44
|
readonly optionSnapshots?: FormDataSourceValueSnapshots;
|
|
33
45
|
readonly readonly: boolean;
|
|
46
|
+
/** Set only for a table cell. */
|
|
47
|
+
readonly row?: UseFormDataSourceFieldRow;
|
|
34
48
|
readonly schema: FormDefinitionSchema;
|
|
49
|
+
/** Snapshot map key; defaults to the field key. */
|
|
50
|
+
readonly snapshotKey?: string;
|
|
35
51
|
readonly uiSchema: FormUiSchema;
|
|
36
52
|
}
|
|
37
53
|
export declare function useFormDataSourceField(input: UseFormDataSourceFieldInput): FormDataSourceFieldState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/FormRendererView-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/FormRendererView-BxGT16z0.cjs");exports.FormRenderer=e.t,exports.FormRendererView=e.t,exports.isFormDataSourceFieldSubmissionBlocked=e.n,exports.readFormDataSourceFieldStatusMessage=e.r,exports.readFormDataSourceSubmissionBlockMessage=e.i,exports.useFormDataSourceField=e.a;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, i as t, n, r, t as i } from "../../../chunks/FormRendererView-
|
|
1
|
+
import { a as e, i as t, n, r, t as i } from "../../../chunks/FormRendererView-DUceSOXe.js";
|
|
2
2
|
export { i as FormRenderer, i as FormRendererView, n as isFormDataSourceFieldSubmissionBlocked, r as readFormDataSourceFieldStatusMessage, t as readFormDataSourceSubmissionBlockMessage, e as useFormDataSourceField };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/detail-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/detail-Dnz5aaI-.cjs");exports.InstanceAttachmentsSection=e.a,exports.InstanceDetailView=e.t,exports.InstanceFormSection=e.o,exports.InstanceHistorySection=e.n,exports.InstanceSignaturesSection=e.r,exports.InstanceTasksSection=e.i;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, t as a } from "../../../chunks/detail-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, t as a } from "../../../chunks/detail-BgKgMvYx.js";
|
|
2
2
|
export { e as InstanceAttachmentsSection, a as InstanceDetailView, r as InstanceFormSection, n as InstanceHistorySection, i as InstanceSignaturesSection, t as InstanceTasksSection };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/rolldown-runtime-BocRIvOZ.cjs"),t=require("../../../chunks/router-adapter--gYs13E8.cjs"),n=require("../../../chunks/auth-provider-4BeCw7cI.cjs"),r=require("../../../chunks/format-date-time-D-cx5M8z.cjs"),i=require("../../../chunks/routes-config-fDVHmvXi.cjs"),a=require("../../../chunks/FormRendererView-
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/rolldown-runtime-BocRIvOZ.cjs"),t=require("../../../chunks/router-adapter--gYs13E8.cjs"),n=require("../../../chunks/auth-provider-4BeCw7cI.cjs"),r=require("../../../chunks/format-date-time-D-cx5M8z.cjs"),i=require("../../../chunks/routes-config-fDVHmvXi.cjs"),a=require("../../../chunks/FormRendererView-BxGT16z0.cjs");let o=require("react"),s=require("@mezzanine-ui/react"),c=require("react/jsx-runtime"),l=require("@rytass/bpm-core-client/workflow"),u=require("@mezzanine-ui/react/ContentHeader");u=e.t(u,1);let d=require("@mezzanine-ui/icons"),f=require("@rytass/bpm-core-client/form");var p={display:`grid`,gap:16};function m(e={}){return(0,c.jsx)(o.Suspense,{fallback:(0,c.jsx)(g,{}),children:(0,c.jsx)(h,{templateId:e.templateId??void 0})})}function h({templateId:e}){let m=t.r(),h=i.r(),{member:g}=n.n(),v=g?.memberId??null,y=e??null,[b,x]=(0,o.useState)(null),[S,C]=(0,o.useState)({}),[w,T]=(0,o.useState)([]),[E,D]=(0,o.useState)(null),[O,k]=(0,o.useState)({}),[A,j]=(0,o.useState)(!0),[M,N]=(0,o.useState)(!1),[P,F]=(0,o.useState)({}),I=(0,o.useCallback)((e,t)=>{F(n=>{let r=n[e];return r?.hasValue===t.hasValue&&r.status===t.status?n:{...n,[e]:{hasValue:t.hasValue,status:t.status}}})},[]);(0,o.useEffect)(()=>{if(!y){B();return}V(y)},[y]);let L=(0,o.useMemo)(()=>w.map(e=>({...e,key:e.id,updatedAt:r.t(e.updatedAt),versionLabel:`v${e.version}`})),[w]),R=(0,o.useMemo)(()=>[{dataIndex:`name`,key:`name`,title:`模板名稱`,width:240},{dataIndex:`versionLabel`,key:`versionLabel`,title:`發布版本`,width:120},{dataIndex:`updatedAt`,key:`updatedAt`,title:`更新時間`,width:220}],[]),z=(0,o.useMemo)(()=>({render:e=>[{name:`發起`,onClick:()=>m.push(h.caseNew(e.id)),variant:`base-primary`}],variant:`base-secondary`,width:88}),[m]);async function B(){j(!0),D(null),x(null),F({});try{T(await(0,l.listLaunchableTemplates)())}catch(e){D(_(e))}finally{j(!1)}}async function V(e){j(!0),D(null),T([]),F({});try{x(await(0,l.readLaunchContext)(e))}catch(e){D(_(e))}finally{j(!1)}}async function H(){if(!b||!v)return;N(!0),D(null),k({});let e=a.i(Object.values(P));if(e){D(e),N(!1);return}let t=(0,f.buildFormRendererValues)(b.formVersion.schema.fields,S),n=(0,f.validateFormRendererValues)({schema:b.formVersion.schema,uiSchema:b.formVersion.uiSchema,values:t});if(!n.valid){k(n.errors),D(`請先補齊必填欄位。`),n.firstInvalidFieldKey&&(0,f.focusFormRendererField)(n.firstInvalidFieldKey),N(!1);return}try{let e=await(0,l.submitApprovalInstance)({formData:t,initiatorMemberId:v,templateId:b.template.id,title:(0,l.readFormDataCaseTitle)({fallbackTitle:b.template.name,formData:t,schema:b.formVersion.schema,uiSchema:b.formVersion.uiSchema})});m.push(h.caseDetail(e))}catch(e){D(_(e))}finally{N(!1)}}async function U(e,t){if(!v)throw Error(`尚未登入,無法上傳附件`);return{id:(await(0,l.uploadAttachment)({file:t,formFieldPath:`form.${e.fieldKey}`})).id}}return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(s.PageHeader,{children:(0,c.jsx)(u.default,{description:b?`${b.template.name} · 表單 v${b.formVersion.version}`:`選擇可發起的已發布模板後填寫表單內容。`,title:`發起簽核`,children:b?(0,c.jsx)(s.Button,{disabled:A||M,icon:d.CheckedIcon,iconType:`leading`,onClick:H,variant:`base-primary`,children:`送出`}):null})}),(0,c.jsx)(s.SectionGroup,{children:(0,c.jsx)(s.Section,{children:(0,c.jsxs)(`div`,{style:p,children:[E?(0,c.jsx)(s.Typography,{color:`text-error`,variant:`body`,children:E}):null,A?(0,c.jsx)(s.Typography,{color:`text-neutral`,variant:`body`,children:`載入中...`}):null,b?(0,c.jsx)(a.t,{dataSourceContext:{kind:`runtime`,templateId:b.template.id},errors:O,maxWidth:480,onChange:e=>{C(e),k({})},onDataSourceStateChange:I,onUploadAttachment:U,schema:b.formVersion.schema,singleColumn:!0,uiSchema:b.formVersion.uiSchema,value:S}):null,!y&&!A?L.length>0?(0,c.jsx)(s.Table,{actions:z,columns:R,dataSource:L,fullWidth:!0}):(0,c.jsx)(s.Typography,{color:`text-neutral`,variant:`body`,children:`目前沒有可發起的已發布模板。`}):null]})})})]})}function g(){return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(s.PageHeader,{children:(0,c.jsx)(u.default,{description:`選擇可發起的已發布模板後填寫表單內容。`,title:`發起簽核`})}),(0,c.jsx)(s.SectionGroup,{children:(0,c.jsx)(s.Section,{children:(0,c.jsx)(s.Typography,{color:`text-neutral`,variant:`body`,children:`載入中...`})})})]})}function _(e){return(0,f.readFormDataSourceErrorMessage)(e)||(e instanceof Error?e.message:`發生未知錯誤`)}exports.InstanceNewView=m;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/views/instances/new/InstanceNewView.tsx"],"sourcesContent":["'use client';\n\nimport {\n CSSProperties,\n ReactElement,\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n Button,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { CheckedIcon } from '@mezzanine-ui/icons';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { FormFieldDefinition } from '@rytass/bpm-core-shared/form';\nimport {\n focusFormRendererField,\n FormRendererValues,\n readFormDataSourceErrorMessage,\n validateFormRendererValues,\n} from '@rytass/bpm-core-client/form';\nimport {\n LaunchContext,\n LaunchableTemplateRecord,\n listLaunchableTemplates,\n readFormDataCaseTitle,\n readLaunchContext,\n submitApprovalInstance,\n uploadAttachment,\n} from '@rytass/bpm-core-client/workflow';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport { useAuth } from '../../../lib/auth-provider';\nimport { useRouterAdapter } from '../../../lib/router-adapter';\nimport { useBPMRoutes } from '../../../lib/routes-config';\nimport { FormRenderer } from '../../forms/renderer/FormRendererView';\nimport {\n readFormDataSourceSubmissionBlockMessage,\n type FormDataSourceFieldState,\n} from '../../forms/renderer/form-data-source-field';\n\nconst FORM_SECTION_STYLE: CSSProperties = {\n display: 'grid',\n gap: 16,\n};\n\ntype LaunchableTemplateRow = Readonly<\n Record<string, unknown> &\n LaunchableTemplateRecord & {\n key: string;\n versionLabel: string;\n }\n>;\n\nexport interface InstanceNewViewProps {\n /**\n * Optional template id pre-selected by the host. The Server Component\n * page should read `?templateId=` from its `searchParams` and pass the\n * value through this prop.\n */\n readonly templateId?: string | null;\n}\n\n/**\n * Framework-agnostic view for the BPM \"發起簽核\" launch page. Mechanical port\n * of `apps/client/src/app/instances/new/page.tsx` — picks a launchable\n * template and submits the launch form.\n */\nexport function InstanceNewView(\n props: InstanceNewViewProps = {},\n): ReactElement {\n return (\n <Suspense fallback={<NewApprovalInstanceLoading />}>\n <NewApprovalInstanceContent templateId={props.templateId ?? undefined} />\n </Suspense>\n );\n}\n\nfunction NewApprovalInstanceContent({\n templateId: templateIdProp,\n}: {\n readonly templateId?: string;\n}): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const templateId = templateIdProp ?? null;\n const [context, setContext] = useState<LaunchContext | null>(null);\n const [formValues, setFormValues] = useState<FormRendererValues>({});\n const [templates, setTemplates] = useState<\n readonly LaunchableTemplateRecord[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [formErrors, setFormErrors] = useState<\n Readonly<Record<string, string>>\n >({});\n const [loading, setLoading] = useState(true);\n const [submitting, setSubmitting] = useState(false);\n const [dataSourceStates, setDataSourceStates] = useState<\n Readonly<\n Record<string, Pick<FormDataSourceFieldState, 'hasValue' | 'status'>>\n >\n >({});\n\n const handleDataSourceStateChange = useCallback(\n (\n fieldKey: string,\n state: Pick<\n FormDataSourceFieldState,\n 'hasValue' | 'invalidValues' | 'status'\n >,\n ): void => {\n setDataSourceStates((currentStates) => {\n const currentState = currentStates[fieldKey];\n\n if (\n currentState?.hasValue === state.hasValue &&\n currentState.status === state.status\n ) {\n return currentStates;\n }\n\n return {\n ...currentStates,\n [fieldKey]: {\n hasValue: state.hasValue,\n status: state.status,\n },\n };\n });\n },\n [],\n );\n\n useEffect((): void => {\n if (!templateId) {\n void refreshTemplates();\n\n return;\n }\n\n void refreshContext(templateId);\n }, [templateId]);\n\n const rows = useMemo(\n (): LaunchableTemplateRow[] =>\n templates.map((template) => ({\n ...template,\n key: template.id,\n updatedAt: formatDateTime(template.updatedAt),\n versionLabel: `v${template.version}`,\n })),\n [templates],\n );\n const columns = useMemo(\n (): TableColumn<LaunchableTemplateRow>[] => [\n { dataIndex: 'name', key: 'name', title: '模板名稱', width: 240 },\n {\n dataIndex: 'versionLabel',\n key: 'versionLabel',\n title: '發布版本',\n width: 120,\n },\n {\n dataIndex: 'updatedAt',\n key: 'updatedAt',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<LaunchableTemplateRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<LaunchableTemplateRow>['render']> => [\n {\n name: '發起',\n onClick: (): void =>\n router.push(routes.caseNew(record.id)),\n variant: 'base-primary',\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router],\n );\n\n async function refreshTemplates(): Promise<void> {\n setLoading(true);\n setError(null);\n setContext(null);\n setDataSourceStates({});\n\n try {\n setTemplates(await listLaunchableTemplates());\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function refreshContext(nextTemplateId: string): Promise<void> {\n setLoading(true);\n setError(null);\n setTemplates([]);\n setDataSourceStates({});\n\n try {\n setContext(await readLaunchContext(nextTemplateId));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function handleSubmit(): Promise<void> {\n if (!context || !currentMemberId) {\n return;\n }\n\n setSubmitting(true);\n setError(null);\n setFormErrors({});\n\n const dataSourceBlockMessage = readFormDataSourceSubmissionBlockMessage(\n Object.values(dataSourceStates),\n );\n\n if (dataSourceBlockMessage) {\n setError(dataSourceBlockMessage);\n setSubmitting(false);\n\n return;\n }\n\n const validation = validateFormRendererValues({\n schema: context.formVersion.schema,\n uiSchema: context.formVersion.uiSchema,\n values: formValues,\n });\n\n if (!validation.valid) {\n setFormErrors(validation.errors);\n setError('請先補齊必填欄位。');\n\n if (validation.firstInvalidFieldKey) {\n focusFormRendererField(validation.firstInvalidFieldKey);\n }\n\n setSubmitting(false);\n\n return;\n }\n\n try {\n const instanceId = await submitApprovalInstance({\n formData: formValues,\n initiatorMemberId: currentMemberId,\n templateId: context.template.id,\n title: readFormDataCaseTitle({\n fallbackTitle: context.template.name,\n formData: formValues,\n schema: context.formVersion.schema,\n uiSchema: context.formVersion.uiSchema,\n }),\n });\n\n router.push(routes.caseDetail(instanceId));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSubmitting(false);\n }\n }\n\n async function handleUploadAttachment(\n field: FormFieldDefinition,\n file: File,\n ): Promise<{ readonly id: string }> {\n if (!currentMemberId) {\n throw new Error('尚未登入,無法上傳附件');\n }\n\n const attachment = await uploadAttachment({\n file,\n formFieldPath: `form.${field.fieldKey}`,\n });\n\n return { id: attachment.id };\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description={\n context\n ? `${context.template.name} · 表單 v${context.formVersion.version}`\n : '選擇可發起的已發布模板後填寫表單內容。'\n }\n title=\"發起簽核\"\n >\n {context ? (\n <Button\n disabled={loading || submitting}\n icon={CheckedIcon}\n iconType=\"leading\"\n onClick={handleSubmit}\n variant=\"base-primary\"\n >\n 送出\n </Button>\n ) : null}\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <div style={FORM_SECTION_STYLE}>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n {loading ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n ) : null}\n\n {context ? (\n <FormRenderer\n dataSourceContext={{\n kind: 'runtime',\n templateId: context.template.id,\n }}\n errors={formErrors}\n maxWidth={480}\n onChange={(values): void => {\n setFormValues(values);\n setFormErrors({});\n }}\n onDataSourceStateChange={handleDataSourceStateChange}\n onUploadAttachment={handleUploadAttachment}\n schema={context.formVersion.schema}\n singleColumn\n uiSchema={context.formVersion.uiSchema}\n value={formValues}\n />\n ) : null}\n\n {!templateId && !loading ? (\n rows.length > 0 ? (\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n />\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 目前沒有可發起的已發布模板。\n </Typography>\n )\n ) : null}\n </div>\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction NewApprovalInstanceLoading(): ReactElement {\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"選擇可發起的已發布模板後填寫表單內容。\"\n title=\"發起簽核\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readErrorMessage(error: unknown): string {\n // DataSource failures arrive as stable codes so the backend never leaks\n // upstream transport detail; translate them instead of showing the raw code.\n const dataSourceMessage = readFormDataSourceErrorMessage(error);\n\n if (dataSourceMessage) {\n return dataSourceMessage;\n }\n\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"8rBAgDA,IAAM,EAAoC,CACxC,QAAS,OACT,IAAK,EACP,EAwBA,SAAgB,EACd,EAA8B,CAAC,EACjB,CACd,OACE,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CAAU,UAAU,EAAA,EAAA,IAAA,CAAC,EAAD,CAA6B,CAAA,EAC/C,UAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CAA4B,WAAY,EAAM,YAAc,IAAA,EAAY,CAAA,CAChE,CAAA,CAEd,CAEA,SAAS,EAA2B,CAClC,WAAY,GAGG,CACf,IAAM,EAAS,EAAA,EAAiB,EAC1B,EAAS,EAAA,EAAa,EACtB,CAAE,UAAW,EAAA,EAAQ,EACrB,EAAkB,GAAQ,UAAY,KACtC,EAAa,GAAkB,KAC/B,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAA+B,IAAI,EAC3D,CAAC,EAAY,IAAA,EAAiB,EAAA,SAAA,CAA6B,CAAC,CAAC,EAC7D,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAEhC,CAAC,CAAC,EACE,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAwB,IAAI,EAChD,CAAC,EAAY,IAAA,EAAiB,EAAA,SAAA,CAElC,CAAC,CAAC,EACE,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAS,EAAI,EACrC,CAAC,EAAY,IAAA,EAAiB,EAAA,SAAA,CAAS,EAAK,EAC5C,CAAC,EAAkB,IAAA,EAAuB,EAAA,SAAA,CAI9C,CAAC,CAAC,EAEE,GAAA,EAA8B,EAAA,YAAA,EAEhC,EACA,IAIS,CACT,EAAqB,GAAkB,CACrC,IAAM,EAAe,EAAc,GASnC,OANE,GAAc,WAAa,EAAM,UACjC,EAAa,SAAW,EAAM,OAEvB,EAGF,CACL,GAAG,GACF,GAAW,CACV,SAAU,EAAM,SAChB,OAAQ,EAAM,MAChB,CACF,CACF,CAAC,CACH,EACA,CAAC,CACH,GAEA,EAAA,EAAA,UAAA,KAAsB,CACpB,GAAI,CAAC,EAAY,CACf,EAAsB,EAEtB,MACF,CAEA,EAAoB,CAAU,CAChC,EAAG,CAAC,CAAU,CAAC,EAEf,IAAM,GAAA,EAAO,EAAA,QAAA,KAET,EAAU,IAAK,IAAc,CAC3B,GAAG,EACH,IAAK,EAAS,GACd,UAAW,EAAA,EAAe,EAAS,SAAS,EAC5C,aAAc,IAAI,EAAS,SAC7B,EAAE,EACJ,CAAC,CAAS,CACZ,EACM,GAAA,EAAU,EAAA,QAAA,KAC8B,CAC1C,CAAE,UAAW,OAAQ,IAAK,OAAQ,MAAO,OAAQ,MAAO,GAAI,EAC5D,CACE,UAAW,eACX,IAAK,eACL,MAAO,OACP,MAAO,GACT,EACA,CACE,UAAW,YACX,IAAK,YACL,MAAO,OACP,MAAO,GACT,CACF,EACA,CAAC,CACH,EACM,GAAA,EAAe,EAAA,QAAA,MACyB,CAC1C,OACE,GAC8D,CAC9D,CACE,KAAM,KACN,YACE,EAAO,KAAK,EAAO,QAAQ,EAAO,EAAE,CAAC,EACvC,QAAS,cACX,CACF,EACA,QAAS,iBACT,MAAO,EACT,GACA,CAAC,CAAM,CACT,EAEA,eAAe,GAAkC,CAC/C,EAAW,EAAI,EACf,EAAS,IAAI,EACb,EAAW,IAAI,EACf,EAAoB,CAAC,CAAC,EAEtB,GAAI,CACF,EAAa,MAAA,EAAM,EAAA,wBAAA,CAAwB,CAAC,CAC9C,OAAS,EAAuB,CAC9B,EAAS,EAAiB,CAAY,CAAC,CACzC,QAAU,CACR,EAAW,EAAK,CAClB,CACF,CAEA,eAAe,EAAe,EAAuC,CACnE,EAAW,EAAI,EACf,EAAS,IAAI,EACb,EAAa,CAAC,CAAC,EACf,EAAoB,CAAC,CAAC,EAEtB,GAAI,CACF,EAAW,MAAA,EAAM,EAAA,kBAAA,CAAkB,CAAc,CAAC,CACpD,OAAS,EAAuB,CAC9B,EAAS,EAAiB,CAAY,CAAC,CACzC,QAAU,CACR,EAAW,EAAK,CAClB,CACF,CAEA,eAAe,GAA8B,CAC3C,GAAI,CAAC,GAAW,CAAC,EACf,OAGF,EAAc,EAAI,EAClB,EAAS,IAAI,EACb,EAAc,CAAC,CAAC,EAEhB,IAAM,EAAyB,EAAA,EAC7B,OAAO,OAAO,CAAgB,CAChC,EAEA,GAAI,EAAwB,CAC1B,EAAS,CAAsB,EAC/B,EAAc,EAAK,EAEnB,MACF,CAEA,IAAM,GAAA,EAAa,EAAA,2BAAA,CAA2B,CAC5C,OAAQ,EAAQ,YAAY,OAC5B,SAAU,EAAQ,YAAY,SAC9B,OAAQ,CACV,CAAC,EAED,GAAI,CAAC,EAAW,MAAO,CACrB,EAAc,EAAW,MAAM,EAC/B,EAAS,WAAW,EAEhB,EAAW,uBACb,EAAA,EAAA,uBAAA,CAAuB,EAAW,oBAAoB,EAGxD,EAAc,EAAK,EAEnB,MACF,CAEA,GAAI,CACF,IAAM,EAAa,MAAA,EAAM,EAAA,uBAAA,CAAuB,CAC9C,SAAU,EACV,kBAAmB,EACnB,WAAY,EAAQ,SAAS,GAC7B,OAAA,EAAO,EAAA,sBAAA,CAAsB,CAC3B,cAAe,EAAQ,SAAS,KAChC,SAAU,EACV,OAAQ,EAAQ,YAAY,OAC5B,SAAU,EAAQ,YAAY,QAChC,CAAC,CACH,CAAC,EAED,EAAO,KAAK,EAAO,WAAW,CAAU,CAAC,CAC3C,OAAS,EAAuB,CAC9B,EAAS,EAAiB,CAAY,CAAC,CACzC,QAAU,CACR,EAAc,EAAK,CACrB,CACF,CAEA,eAAe,EACb,EACA,EACkC,CAClC,GAAI,CAAC,EACH,MAAU,MAAM,aAAa,EAQ/B,MAAO,CAAE,IAAI,MAAA,EALY,EAAA,iBAAA,CAAiB,CACxC,OACA,cAAe,QAAQ,EAAM,UAC/B,CAAC,EAAA,CAEuB,EAAG,CAC7B,CAEA,OACE,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,EAAA,QAAD,CACE,YACE,EACI,GAAG,EAAQ,SAAS,KAAK,SAAS,EAAQ,YAAY,UACtD,sBAEN,MAAM,OAEL,SAAA,GACC,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CACE,SAAU,GAAW,EACrB,KAAM,EAAA,YACN,SAAS,UACT,QAAS,EACT,QAAQ,eACT,SAAA,IAEO,CAAA,EACN,IACS,CAAA,CACL,CAAA,GAEZ,EAAA,EAAA,IAAA,CAAC,EAAA,aAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,EAAA,QAAD,CAAA,UACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,MAAO,EAAZ,SAAA,CACG,GACC,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,MAAM,aAAa,QAAQ,OACpC,SAAA,CACS,CAAA,EACV,KAEH,GACC,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,MAAM,eAAe,QAAQ,OAAO,SAAA,QAEpC,CAAA,EACV,KAEH,GACC,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CACE,kBAAmB,CACjB,KAAM,UACN,WAAY,EAAQ,SAAS,EAC/B,EACA,OAAQ,EACR,SAAU,IACV,SAAW,GAAiB,CAC1B,EAAc,CAAM,EACpB,EAAc,CAAC,CAAC,CAClB,EACA,wBAAyB,EACzB,mBAAoB,EACpB,OAAQ,EAAQ,YAAY,OAC5B,aAAA,GACA,SAAU,EAAQ,YAAY,SAC9B,MAAO,CACR,CAAA,EACC,KAEH,CAAC,GAAc,CAAC,EACf,EAAK,OAAS,GACZ,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACE,QAAS,EACA,UACT,WAAY,EACZ,UAAA,EACD,CAAA,GAED,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,MAAM,eAAe,QAAQ,OAAO,SAAA,gBAEpC,CAAA,EAEZ,IACD,CACE,CAAA,CAAA,CAAA,CACG,CAAA,CACd,CAAA,CAAA,CAER,CAEA,SAAS,GAA2C,CAClD,OACE,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,EAAA,QAAD,CACE,YAAY,sBACZ,MAAM,MACP,CAAA,CACS,CAAA,GAEZ,EAAA,EAAA,IAAA,CAAC,EAAA,aAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,EAAA,QAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,MAAM,eAAe,QAAQ,OAAO,SAAA,QAEpC,CAAA,CACL,CAAA,CACG,CAAA,CACd,CAAA,CAAA,CAER,CAEA,SAAS,EAAiB,EAAwB,CAShD,OANM,EAAoB,EAAA,+BAAA,CAA+B,CAErD,IAIG,aAAiB,MAAQ,EAAM,QAAU,SAClD"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/views/instances/new/InstanceNewView.tsx"],"sourcesContent":["'use client';\n\nimport {\n CSSProperties,\n ReactElement,\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n Button,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { CheckedIcon } from '@mezzanine-ui/icons';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { FormFieldDefinition } from '@rytass/bpm-core-shared/form';\nimport {\n buildFormRendererValues,\n focusFormRendererField,\n FormRendererValues,\n readFormDataSourceErrorMessage,\n validateFormRendererValues,\n} from '@rytass/bpm-core-client/form';\nimport {\n LaunchContext,\n LaunchableTemplateRecord,\n listLaunchableTemplates,\n readFormDataCaseTitle,\n readLaunchContext,\n submitApprovalInstance,\n uploadAttachment,\n} from '@rytass/bpm-core-client/workflow';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport { useAuth } from '../../../lib/auth-provider';\nimport { useRouterAdapter } from '../../../lib/router-adapter';\nimport { useBPMRoutes } from '../../../lib/routes-config';\nimport { FormRenderer } from '../../forms/renderer/FormRendererView';\nimport {\n readFormDataSourceSubmissionBlockMessage,\n type FormDataSourceFieldState,\n} from '../../forms/renderer/form-data-source-field';\n\nconst FORM_SECTION_STYLE: CSSProperties = {\n display: 'grid',\n gap: 16,\n};\n\ntype LaunchableTemplateRow = Readonly<\n Record<string, unknown> &\n LaunchableTemplateRecord & {\n key: string;\n versionLabel: string;\n }\n>;\n\nexport interface InstanceNewViewProps {\n /**\n * Optional template id pre-selected by the host. The Server Component\n * page should read `?templateId=` from its `searchParams` and pass the\n * value through this prop.\n */\n readonly templateId?: string | null;\n}\n\n/**\n * Framework-agnostic view for the BPM \"發起簽核\" launch page. Mechanical port\n * of `apps/client/src/app/instances/new/page.tsx` — picks a launchable\n * template and submits the launch form.\n */\nexport function InstanceNewView(\n props: InstanceNewViewProps = {},\n): ReactElement {\n return (\n <Suspense fallback={<NewApprovalInstanceLoading />}>\n <NewApprovalInstanceContent templateId={props.templateId ?? undefined} />\n </Suspense>\n );\n}\n\nfunction NewApprovalInstanceContent({\n templateId: templateIdProp,\n}: {\n readonly templateId?: string;\n}): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const templateId = templateIdProp ?? null;\n const [context, setContext] = useState<LaunchContext | null>(null);\n const [formValues, setFormValues] = useState<FormRendererValues>({});\n const [templates, setTemplates] = useState<\n readonly LaunchableTemplateRecord[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [formErrors, setFormErrors] = useState<\n Readonly<Record<string, string>>\n >({});\n const [loading, setLoading] = useState(true);\n const [submitting, setSubmitting] = useState(false);\n const [dataSourceStates, setDataSourceStates] = useState<\n Readonly<\n Record<string, Pick<FormDataSourceFieldState, 'hasValue' | 'status'>>\n >\n >({});\n\n const handleDataSourceStateChange = useCallback(\n (\n fieldKey: string,\n state: Pick<\n FormDataSourceFieldState,\n 'hasValue' | 'invalidValues' | 'status'\n >,\n ): void => {\n setDataSourceStates((currentStates) => {\n const currentState = currentStates[fieldKey];\n\n if (\n currentState?.hasValue === state.hasValue &&\n currentState.status === state.status\n ) {\n return currentStates;\n }\n\n return {\n ...currentStates,\n [fieldKey]: {\n hasValue: state.hasValue,\n status: state.status,\n },\n };\n });\n },\n [],\n );\n\n useEffect((): void => {\n if (!templateId) {\n void refreshTemplates();\n\n return;\n }\n\n void refreshContext(templateId);\n }, [templateId]);\n\n const rows = useMemo(\n (): LaunchableTemplateRow[] =>\n templates.map((template) => ({\n ...template,\n key: template.id,\n updatedAt: formatDateTime(template.updatedAt),\n versionLabel: `v${template.version}`,\n })),\n [templates],\n );\n const columns = useMemo(\n (): TableColumn<LaunchableTemplateRow>[] => [\n { dataIndex: 'name', key: 'name', title: '模板名稱', width: 240 },\n {\n dataIndex: 'versionLabel',\n key: 'versionLabel',\n title: '發布版本',\n width: 120,\n },\n {\n dataIndex: 'updatedAt',\n key: 'updatedAt',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<LaunchableTemplateRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<LaunchableTemplateRow>['render']> => [\n {\n name: '發起',\n onClick: (): void =>\n router.push(routes.caseNew(record.id)),\n variant: 'base-primary',\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router],\n );\n\n async function refreshTemplates(): Promise<void> {\n setLoading(true);\n setError(null);\n setContext(null);\n setDataSourceStates({});\n\n try {\n setTemplates(await listLaunchableTemplates());\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function refreshContext(nextTemplateId: string): Promise<void> {\n setLoading(true);\n setError(null);\n setTemplates([]);\n setDataSourceStates({});\n\n try {\n setContext(await readLaunchContext(nextTemplateId));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function handleSubmit(): Promise<void> {\n if (!context || !currentMemberId) {\n return;\n }\n\n setSubmitting(true);\n setError(null);\n setFormErrors({});\n\n const dataSourceBlockMessage = readFormDataSourceSubmissionBlockMessage(\n Object.values(dataSourceStates),\n );\n\n if (dataSourceBlockMessage) {\n setError(dataSourceBlockMessage);\n setSubmitting(false);\n\n return;\n }\n\n // What the renderer shows, not just what has been touched: a table seeds\n // its `minRows` rows and a field can carry a `defaultValue`, and neither\n // reaches this state until the filler edits something. Validating and\n // submitting the raw state would reject a table the user can plainly see\n // rows in — and submit a default the backend never receives.\n const submittedValues = buildFormRendererValues(\n context.formVersion.schema.fields,\n formValues,\n );\n const validation = validateFormRendererValues({\n schema: context.formVersion.schema,\n uiSchema: context.formVersion.uiSchema,\n values: submittedValues,\n });\n\n if (!validation.valid) {\n setFormErrors(validation.errors);\n setError('請先補齊必填欄位。');\n\n if (validation.firstInvalidFieldKey) {\n focusFormRendererField(validation.firstInvalidFieldKey);\n }\n\n setSubmitting(false);\n\n return;\n }\n\n try {\n const instanceId = await submitApprovalInstance({\n formData: submittedValues,\n initiatorMemberId: currentMemberId,\n templateId: context.template.id,\n title: readFormDataCaseTitle({\n fallbackTitle: context.template.name,\n formData: submittedValues,\n schema: context.formVersion.schema,\n uiSchema: context.formVersion.uiSchema,\n }),\n });\n\n router.push(routes.caseDetail(instanceId));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSubmitting(false);\n }\n }\n\n async function handleUploadAttachment(\n field: FormFieldDefinition,\n file: File,\n ): Promise<{ readonly id: string }> {\n if (!currentMemberId) {\n throw new Error('尚未登入,無法上傳附件');\n }\n\n const attachment = await uploadAttachment({\n file,\n formFieldPath: `form.${field.fieldKey}`,\n });\n\n return { id: attachment.id };\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description={\n context\n ? `${context.template.name} · 表單 v${context.formVersion.version}`\n : '選擇可發起的已發布模板後填寫表單內容。'\n }\n title=\"發起簽核\"\n >\n {context ? (\n <Button\n disabled={loading || submitting}\n icon={CheckedIcon}\n iconType=\"leading\"\n onClick={handleSubmit}\n variant=\"base-primary\"\n >\n 送出\n </Button>\n ) : null}\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <div style={FORM_SECTION_STYLE}>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n {loading ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n ) : null}\n\n {context ? (\n <FormRenderer\n dataSourceContext={{\n kind: 'runtime',\n templateId: context.template.id,\n }}\n errors={formErrors}\n maxWidth={480}\n onChange={(values): void => {\n setFormValues(values);\n setFormErrors({});\n }}\n onDataSourceStateChange={handleDataSourceStateChange}\n onUploadAttachment={handleUploadAttachment}\n schema={context.formVersion.schema}\n singleColumn\n uiSchema={context.formVersion.uiSchema}\n value={formValues}\n />\n ) : null}\n\n {!templateId && !loading ? (\n rows.length > 0 ? (\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n />\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 目前沒有可發起的已發布模板。\n </Typography>\n )\n ) : null}\n </div>\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction NewApprovalInstanceLoading(): ReactElement {\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"選擇可發起的已發布模板後填寫表單內容。\"\n title=\"發起簽核\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readErrorMessage(error: unknown): string {\n // DataSource failures arrive as stable codes so the backend never leaks\n // upstream transport detail; translate them instead of showing the raw code.\n const dataSourceMessage = readFormDataSourceErrorMessage(error);\n\n if (dataSourceMessage) {\n return dataSourceMessage;\n }\n\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
|
|
@@ -3,27 +3,27 @@ import { r as e } from "../../../chunks/router-adapter-DftlFTOd.js";
|
|
|
3
3
|
import { n as t } from "../../../chunks/auth-provider-B5oPmvk2.js";
|
|
4
4
|
import { t as n } from "../../../chunks/format-date-time-CB-LxzqT.js";
|
|
5
5
|
import { r } from "../../../chunks/routes-config-RBYQtUd0.js";
|
|
6
|
-
import { i, t as a } from "../../../chunks/FormRendererView-
|
|
7
|
-
import { Suspense as o, useCallback as
|
|
8
|
-
import { Button as
|
|
9
|
-
import { Fragment as
|
|
10
|
-
import { listLaunchableTemplates as
|
|
11
|
-
import
|
|
12
|
-
import { CheckedIcon as
|
|
13
|
-
import { focusFormRendererField as D, readFormDataSourceErrorMessage as O, validateFormRendererValues as k } from "@rytass/bpm-core-client/form";
|
|
6
|
+
import { i, t as a } from "../../../chunks/FormRendererView-DUceSOXe.js";
|
|
7
|
+
import { Suspense as o, useCallback as ee, useEffect as s, useMemo as c, useState as l } from "react";
|
|
8
|
+
import { Button as u, PageHeader as d, Section as f, SectionGroup as p, Table as m, Typography as h } from "@mezzanine-ui/react";
|
|
9
|
+
import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
10
|
+
import { listLaunchableTemplates as y, readFormDataCaseTitle as b, readLaunchContext as x, submitApprovalInstance as S, uploadAttachment as C } from "@rytass/bpm-core-client/workflow";
|
|
11
|
+
import w from "@mezzanine-ui/react/ContentHeader";
|
|
12
|
+
import { CheckedIcon as T } from "@mezzanine-ui/icons";
|
|
13
|
+
import { buildFormRendererValues as E, focusFormRendererField as D, readFormDataSourceErrorMessage as O, validateFormRendererValues as k } from "@rytass/bpm-core-client/form";
|
|
14
14
|
//#region src/views/instances/new/InstanceNewView.tsx
|
|
15
15
|
var A = {
|
|
16
16
|
display: "grid",
|
|
17
17
|
gap: 16
|
|
18
18
|
};
|
|
19
19
|
function j(e = {}) {
|
|
20
|
-
return /* @__PURE__ */
|
|
21
|
-
fallback: /* @__PURE__ */
|
|
22
|
-
children: /* @__PURE__ */
|
|
20
|
+
return /* @__PURE__ */ _(o, {
|
|
21
|
+
fallback: /* @__PURE__ */ _(N, {}),
|
|
22
|
+
children: /* @__PURE__ */ _(M, { templateId: e.templateId ?? void 0 })
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
function M({ templateId: o }) {
|
|
26
|
-
let O = e(), j = r(), { member: M } = t(), N = M?.memberId ?? null, F = o ?? null, [I, L] =
|
|
26
|
+
let O = e(), j = r(), { member: M } = t(), N = M?.memberId ?? null, F = o ?? null, [I, L] = l(null), [R, z] = l({}), [B, V] = l([]), [H, U] = l(null), [W, G] = l({}), [K, q] = l(!0), [J, Y] = l(!1), [X, Z] = l({}), Q = ee((e, t) => {
|
|
27
27
|
Z((n) => {
|
|
28
28
|
let r = n[e];
|
|
29
29
|
return r?.hasValue === t.hasValue && r.status === t.status ? n : {
|
|
@@ -35,19 +35,19 @@ function M({ templateId: o }) {
|
|
|
35
35
|
};
|
|
36
36
|
});
|
|
37
37
|
}, []);
|
|
38
|
-
|
|
38
|
+
s(() => {
|
|
39
39
|
if (!F) {
|
|
40
|
-
|
|
40
|
+
re();
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
ie(F);
|
|
44
44
|
}, [F]);
|
|
45
|
-
let $ =
|
|
45
|
+
let $ = c(() => B.map((e) => ({
|
|
46
46
|
...e,
|
|
47
47
|
key: e.id,
|
|
48
48
|
updatedAt: n(e.updatedAt),
|
|
49
49
|
versionLabel: `v${e.version}`
|
|
50
|
-
})), [B]),
|
|
50
|
+
})), [B]), te = c(() => [
|
|
51
51
|
{
|
|
52
52
|
dataIndex: "name",
|
|
53
53
|
key: "name",
|
|
@@ -66,7 +66,7 @@ function M({ templateId: o }) {
|
|
|
66
66
|
title: "更新時間",
|
|
67
67
|
width: 220
|
|
68
68
|
}
|
|
69
|
-
], []),
|
|
69
|
+
], []), ne = c(() => ({
|
|
70
70
|
render: (e) => [{
|
|
71
71
|
name: "發起",
|
|
72
72
|
onClick: () => O.push(j.caseNew(e.id)),
|
|
@@ -75,27 +75,27 @@ function M({ templateId: o }) {
|
|
|
75
75
|
variant: "base-secondary",
|
|
76
76
|
width: 88
|
|
77
77
|
}), [O]);
|
|
78
|
-
async function
|
|
78
|
+
async function re() {
|
|
79
79
|
q(!0), U(null), L(null), Z({});
|
|
80
80
|
try {
|
|
81
|
-
V(await
|
|
81
|
+
V(await y());
|
|
82
82
|
} catch (e) {
|
|
83
83
|
U(P(e));
|
|
84
84
|
} finally {
|
|
85
85
|
q(!1);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
async function
|
|
88
|
+
async function ie(e) {
|
|
89
89
|
q(!0), U(null), V([]), Z({});
|
|
90
90
|
try {
|
|
91
|
-
L(await
|
|
91
|
+
L(await x(e));
|
|
92
92
|
} catch (e) {
|
|
93
93
|
U(P(e));
|
|
94
94
|
} finally {
|
|
95
95
|
q(!1);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
-
async function
|
|
98
|
+
async function ae() {
|
|
99
99
|
if (!I || !N) return;
|
|
100
100
|
Y(!0), U(null), G({});
|
|
101
101
|
let e = i(Object.values(X));
|
|
@@ -103,23 +103,23 @@ function M({ templateId: o }) {
|
|
|
103
103
|
U(e), Y(!1);
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
|
-
let t = k({
|
|
106
|
+
let t = E(I.formVersion.schema.fields, R), n = k({
|
|
107
107
|
schema: I.formVersion.schema,
|
|
108
108
|
uiSchema: I.formVersion.uiSchema,
|
|
109
|
-
values:
|
|
109
|
+
values: t
|
|
110
110
|
});
|
|
111
|
-
if (!
|
|
112
|
-
G(
|
|
111
|
+
if (!n.valid) {
|
|
112
|
+
G(n.errors), U("請先補齊必填欄位。"), n.firstInvalidFieldKey && D(n.firstInvalidFieldKey), Y(!1);
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
115
|
try {
|
|
116
|
-
let e = await
|
|
117
|
-
formData:
|
|
116
|
+
let e = await S({
|
|
117
|
+
formData: t,
|
|
118
118
|
initiatorMemberId: N,
|
|
119
119
|
templateId: I.template.id,
|
|
120
|
-
title:
|
|
120
|
+
title: b({
|
|
121
121
|
fallbackTitle: I.template.name,
|
|
122
|
-
formData:
|
|
122
|
+
formData: t,
|
|
123
123
|
schema: I.formVersion.schema,
|
|
124
124
|
uiSchema: I.formVersion.uiSchema
|
|
125
125
|
})
|
|
@@ -131,38 +131,38 @@ function M({ templateId: o }) {
|
|
|
131
131
|
Y(!1);
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
-
async function
|
|
134
|
+
async function oe(e, t) {
|
|
135
135
|
if (!N) throw Error("尚未登入,無法上傳附件");
|
|
136
|
-
return { id: (await
|
|
136
|
+
return { id: (await C({
|
|
137
137
|
file: t,
|
|
138
138
|
formFieldPath: `form.${e.fieldKey}`
|
|
139
139
|
})).id };
|
|
140
140
|
}
|
|
141
|
-
return /* @__PURE__ */
|
|
141
|
+
return /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(d, { children: /* @__PURE__ */ _(w, {
|
|
142
142
|
description: I ? `${I.template.name} · 表單 v${I.formVersion.version}` : "選擇可發起的已發布模板後填寫表單內容。",
|
|
143
143
|
title: "發起簽核",
|
|
144
|
-
children: I ? /* @__PURE__ */
|
|
144
|
+
children: I ? /* @__PURE__ */ _(u, {
|
|
145
145
|
disabled: K || J,
|
|
146
|
-
icon:
|
|
146
|
+
icon: T,
|
|
147
147
|
iconType: "leading",
|
|
148
|
-
onClick:
|
|
148
|
+
onClick: ae,
|
|
149
149
|
variant: "base-primary",
|
|
150
150
|
children: "送出"
|
|
151
151
|
}) : null
|
|
152
|
-
}) }), /* @__PURE__ */
|
|
152
|
+
}) }), /* @__PURE__ */ _(p, { children: /* @__PURE__ */ _(f, { children: /* @__PURE__ */ v("div", {
|
|
153
153
|
style: A,
|
|
154
154
|
children: [
|
|
155
|
-
H ? /* @__PURE__ */
|
|
155
|
+
H ? /* @__PURE__ */ _(h, {
|
|
156
156
|
color: "text-error",
|
|
157
157
|
variant: "body",
|
|
158
158
|
children: H
|
|
159
159
|
}) : null,
|
|
160
|
-
K ? /* @__PURE__ */
|
|
160
|
+
K ? /* @__PURE__ */ _(h, {
|
|
161
161
|
color: "text-neutral",
|
|
162
162
|
variant: "body",
|
|
163
163
|
children: "載入中..."
|
|
164
164
|
}) : null,
|
|
165
|
-
I ? /* @__PURE__ */
|
|
165
|
+
I ? /* @__PURE__ */ _(a, {
|
|
166
166
|
dataSourceContext: {
|
|
167
167
|
kind: "runtime",
|
|
168
168
|
templateId: I.template.id
|
|
@@ -173,18 +173,18 @@ function M({ templateId: o }) {
|
|
|
173
173
|
z(e), G({});
|
|
174
174
|
},
|
|
175
175
|
onDataSourceStateChange: Q,
|
|
176
|
-
onUploadAttachment:
|
|
176
|
+
onUploadAttachment: oe,
|
|
177
177
|
schema: I.formVersion.schema,
|
|
178
178
|
singleColumn: !0,
|
|
179
179
|
uiSchema: I.formVersion.uiSchema,
|
|
180
180
|
value: R
|
|
181
181
|
}) : null,
|
|
182
|
-
!F && !K ? $.length > 0 ? /* @__PURE__ */
|
|
183
|
-
actions:
|
|
184
|
-
columns:
|
|
182
|
+
!F && !K ? $.length > 0 ? /* @__PURE__ */ _(m, {
|
|
183
|
+
actions: ne,
|
|
184
|
+
columns: te,
|
|
185
185
|
dataSource: $,
|
|
186
186
|
fullWidth: !0
|
|
187
|
-
}) : /* @__PURE__ */
|
|
187
|
+
}) : /* @__PURE__ */ _(h, {
|
|
188
188
|
color: "text-neutral",
|
|
189
189
|
variant: "body",
|
|
190
190
|
children: "目前沒有可發起的已發布模板。"
|
|
@@ -193,10 +193,10 @@ function M({ templateId: o }) {
|
|
|
193
193
|
}) }) })] });
|
|
194
194
|
}
|
|
195
195
|
function N() {
|
|
196
|
-
return /* @__PURE__ */
|
|
196
|
+
return /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(d, { children: /* @__PURE__ */ _(w, {
|
|
197
197
|
description: "選擇可發起的已發布模板後填寫表單內容。",
|
|
198
198
|
title: "發起簽核"
|
|
199
|
-
}) }), /* @__PURE__ */
|
|
199
|
+
}) }), /* @__PURE__ */ _(p, { children: /* @__PURE__ */ _(f, { children: /* @__PURE__ */ _(h, {
|
|
200
200
|
color: "text-neutral",
|
|
201
201
|
variant: "body",
|
|
202
202
|
children: "載入中..."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/views/instances/new/InstanceNewView.tsx"],"sourcesContent":["'use client';\n\nimport {\n CSSProperties,\n ReactElement,\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n Button,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { CheckedIcon } from '@mezzanine-ui/icons';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { FormFieldDefinition } from '@rytass/bpm-core-shared/form';\nimport {\n focusFormRendererField,\n FormRendererValues,\n readFormDataSourceErrorMessage,\n validateFormRendererValues,\n} from '@rytass/bpm-core-client/form';\nimport {\n LaunchContext,\n LaunchableTemplateRecord,\n listLaunchableTemplates,\n readFormDataCaseTitle,\n readLaunchContext,\n submitApprovalInstance,\n uploadAttachment,\n} from '@rytass/bpm-core-client/workflow';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport { useAuth } from '../../../lib/auth-provider';\nimport { useRouterAdapter } from '../../../lib/router-adapter';\nimport { useBPMRoutes } from '../../../lib/routes-config';\nimport { FormRenderer } from '../../forms/renderer/FormRendererView';\nimport {\n readFormDataSourceSubmissionBlockMessage,\n type FormDataSourceFieldState,\n} from '../../forms/renderer/form-data-source-field';\n\nconst FORM_SECTION_STYLE: CSSProperties = {\n display: 'grid',\n gap: 16,\n};\n\ntype LaunchableTemplateRow = Readonly<\n Record<string, unknown> &\n LaunchableTemplateRecord & {\n key: string;\n versionLabel: string;\n }\n>;\n\nexport interface InstanceNewViewProps {\n /**\n * Optional template id pre-selected by the host. The Server Component\n * page should read `?templateId=` from its `searchParams` and pass the\n * value through this prop.\n */\n readonly templateId?: string | null;\n}\n\n/**\n * Framework-agnostic view for the BPM \"發起簽核\" launch page. Mechanical port\n * of `apps/client/src/app/instances/new/page.tsx` — picks a launchable\n * template and submits the launch form.\n */\nexport function InstanceNewView(\n props: InstanceNewViewProps = {},\n): ReactElement {\n return (\n <Suspense fallback={<NewApprovalInstanceLoading />}>\n <NewApprovalInstanceContent templateId={props.templateId ?? undefined} />\n </Suspense>\n );\n}\n\nfunction NewApprovalInstanceContent({\n templateId: templateIdProp,\n}: {\n readonly templateId?: string;\n}): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const templateId = templateIdProp ?? null;\n const [context, setContext] = useState<LaunchContext | null>(null);\n const [formValues, setFormValues] = useState<FormRendererValues>({});\n const [templates, setTemplates] = useState<\n readonly LaunchableTemplateRecord[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [formErrors, setFormErrors] = useState<\n Readonly<Record<string, string>>\n >({});\n const [loading, setLoading] = useState(true);\n const [submitting, setSubmitting] = useState(false);\n const [dataSourceStates, setDataSourceStates] = useState<\n Readonly<\n Record<string, Pick<FormDataSourceFieldState, 'hasValue' | 'status'>>\n >\n >({});\n\n const handleDataSourceStateChange = useCallback(\n (\n fieldKey: string,\n state: Pick<\n FormDataSourceFieldState,\n 'hasValue' | 'invalidValues' | 'status'\n >,\n ): void => {\n setDataSourceStates((currentStates) => {\n const currentState = currentStates[fieldKey];\n\n if (\n currentState?.hasValue === state.hasValue &&\n currentState.status === state.status\n ) {\n return currentStates;\n }\n\n return {\n ...currentStates,\n [fieldKey]: {\n hasValue: state.hasValue,\n status: state.status,\n },\n };\n });\n },\n [],\n );\n\n useEffect((): void => {\n if (!templateId) {\n void refreshTemplates();\n\n return;\n }\n\n void refreshContext(templateId);\n }, [templateId]);\n\n const rows = useMemo(\n (): LaunchableTemplateRow[] =>\n templates.map((template) => ({\n ...template,\n key: template.id,\n updatedAt: formatDateTime(template.updatedAt),\n versionLabel: `v${template.version}`,\n })),\n [templates],\n );\n const columns = useMemo(\n (): TableColumn<LaunchableTemplateRow>[] => [\n { dataIndex: 'name', key: 'name', title: '模板名稱', width: 240 },\n {\n dataIndex: 'versionLabel',\n key: 'versionLabel',\n title: '發布版本',\n width: 120,\n },\n {\n dataIndex: 'updatedAt',\n key: 'updatedAt',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<LaunchableTemplateRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<LaunchableTemplateRow>['render']> => [\n {\n name: '發起',\n onClick: (): void =>\n router.push(routes.caseNew(record.id)),\n variant: 'base-primary',\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router],\n );\n\n async function refreshTemplates(): Promise<void> {\n setLoading(true);\n setError(null);\n setContext(null);\n setDataSourceStates({});\n\n try {\n setTemplates(await listLaunchableTemplates());\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function refreshContext(nextTemplateId: string): Promise<void> {\n setLoading(true);\n setError(null);\n setTemplates([]);\n setDataSourceStates({});\n\n try {\n setContext(await readLaunchContext(nextTemplateId));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function handleSubmit(): Promise<void> {\n if (!context || !currentMemberId) {\n return;\n }\n\n setSubmitting(true);\n setError(null);\n setFormErrors({});\n\n const dataSourceBlockMessage = readFormDataSourceSubmissionBlockMessage(\n Object.values(dataSourceStates),\n );\n\n if (dataSourceBlockMessage) {\n setError(dataSourceBlockMessage);\n setSubmitting(false);\n\n return;\n }\n\n const validation = validateFormRendererValues({\n schema: context.formVersion.schema,\n uiSchema: context.formVersion.uiSchema,\n values: formValues,\n });\n\n if (!validation.valid) {\n setFormErrors(validation.errors);\n setError('請先補齊必填欄位。');\n\n if (validation.firstInvalidFieldKey) {\n focusFormRendererField(validation.firstInvalidFieldKey);\n }\n\n setSubmitting(false);\n\n return;\n }\n\n try {\n const instanceId = await submitApprovalInstance({\n formData: formValues,\n initiatorMemberId: currentMemberId,\n templateId: context.template.id,\n title: readFormDataCaseTitle({\n fallbackTitle: context.template.name,\n formData: formValues,\n schema: context.formVersion.schema,\n uiSchema: context.formVersion.uiSchema,\n }),\n });\n\n router.push(routes.caseDetail(instanceId));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSubmitting(false);\n }\n }\n\n async function handleUploadAttachment(\n field: FormFieldDefinition,\n file: File,\n ): Promise<{ readonly id: string }> {\n if (!currentMemberId) {\n throw new Error('尚未登入,無法上傳附件');\n }\n\n const attachment = await uploadAttachment({\n file,\n formFieldPath: `form.${field.fieldKey}`,\n });\n\n return { id: attachment.id };\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description={\n context\n ? `${context.template.name} · 表單 v${context.formVersion.version}`\n : '選擇可發起的已發布模板後填寫表單內容。'\n }\n title=\"發起簽核\"\n >\n {context ? (\n <Button\n disabled={loading || submitting}\n icon={CheckedIcon}\n iconType=\"leading\"\n onClick={handleSubmit}\n variant=\"base-primary\"\n >\n 送出\n </Button>\n ) : null}\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <div style={FORM_SECTION_STYLE}>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n {loading ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n ) : null}\n\n {context ? (\n <FormRenderer\n dataSourceContext={{\n kind: 'runtime',\n templateId: context.template.id,\n }}\n errors={formErrors}\n maxWidth={480}\n onChange={(values): void => {\n setFormValues(values);\n setFormErrors({});\n }}\n onDataSourceStateChange={handleDataSourceStateChange}\n onUploadAttachment={handleUploadAttachment}\n schema={context.formVersion.schema}\n singleColumn\n uiSchema={context.formVersion.uiSchema}\n value={formValues}\n />\n ) : null}\n\n {!templateId && !loading ? (\n rows.length > 0 ? (\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n />\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 目前沒有可發起的已發布模板。\n </Typography>\n )\n ) : null}\n </div>\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction NewApprovalInstanceLoading(): ReactElement {\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"選擇可發起的已發布模板後填寫表單內容。\"\n title=\"發起簽核\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readErrorMessage(error: unknown): string {\n // DataSource failures arrive as stable codes so the backend never leaks\n // upstream transport detail; translate them instead of showing the raw code.\n const dataSourceMessage = readFormDataSourceErrorMessage(error);\n\n if (dataSourceMessage) {\n return dataSourceMessage;\n }\n\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":";;;;;;;;;;;;;;AAgDA,IAAM,IAAoC;CACxC,SAAS;CACT,KAAK;AACP;AAwBA,SAAgB,EACd,IAA8B,CAAC,GACjB;CACd,OACE,kBAAC,GAAD;EAAU,UAAU,kBAAC,GAAD,CAA6B,CAAA;EAC/C,UAAA,kBAAC,GAAD,EAA4B,YAAY,EAAM,cAAc,KAAA,EAAY,CAAA;CAChE,CAAA;AAEd;AAEA,SAAS,EAA2B,EAClC,YAAY,KAGG;CACf,IAAM,IAAS,EAAiB,GAC1B,IAAS,EAAa,GACtB,EAAE,cAAW,EAAQ,GACrB,IAAkB,GAAQ,YAAY,MACtC,IAAa,KAAkB,MAC/B,CAAC,GAAS,KAAc,EAA+B,IAAI,GAC3D,CAAC,GAAY,KAAiB,EAA6B,CAAC,CAAC,GAC7D,CAAC,GAAW,KAAgB,EAEhC,CAAC,CAAC,GACE,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAY,KAAiB,EAElC,CAAC,CAAC,GACE,CAAC,GAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAkB,KAAuB,EAI9C,CAAC,CAAC,GAEE,IAA8B,GAEhC,GACA,MAIS;EACT,GAAqB,MAAkB;GACrC,IAAM,IAAe,EAAc;GASnC,OANE,GAAc,aAAa,EAAM,YACjC,EAAa,WAAW,EAAM,SAEvB,IAGF;IACL,GAAG;KACF,IAAW;KACV,UAAU,EAAM;KAChB,QAAQ,EAAM;IAChB;GACF;EACF,CAAC;CACH,GACA,CAAC,CACH;CAEA,QAAsB;EACpB,IAAI,CAAC,GAAY;GACf,GAAsB;GAEtB;EACF;EAEA,GAAoB,CAAU;CAChC,GAAG,CAAC,CAAU,CAAC;CAEf,IAAM,IAAO,QAET,EAAU,KAAK,OAAc;EAC3B,GAAG;EACH,KAAK,EAAS;EACd,WAAW,EAAe,EAAS,SAAS;EAC5C,cAAc,IAAI,EAAS;CAC7B,EAAE,GACJ,CAAC,CAAS,CACZ,GACM,KAAU,QAC8B;EAC1C;GAAE,WAAW;GAAQ,KAAK;GAAQ,OAAO;GAAQ,OAAO;EAAI;EAC5D;GACE,WAAW;GACX,KAAK;GACL,OAAO;GACP,OAAO;EACT;EACA;GACE,WAAW;GACX,KAAK;GACL,OAAO;GACP,OAAO;EACT;CACF,GACA,CAAC,CACH,GACM,KAAe,SACyB;EAC1C,SACE,MAC8D,CAC9D;GACE,MAAM;GACN,eACE,EAAO,KAAK,EAAO,QAAQ,EAAO,EAAE,CAAC;GACvC,SAAS;EACX,CACF;EACA,SAAS;EACT,OAAO;CACT,IACA,CAAC,CAAM,CACT;CAEA,eAAe,KAAkC;EAI/C,AAHA,EAAW,EAAI,GACf,EAAS,IAAI,GACb,EAAW,IAAI,GACf,EAAoB,CAAC,CAAC;EAEtB,IAAI;GACF,EAAa,MAAM,EAAwB,CAAC;EAC9C,SAAS,GAAuB;GAC9B,EAAS,EAAiB,CAAY,CAAC;EACzC,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CAEA,eAAe,GAAe,GAAuC;EAInE,AAHA,EAAW,EAAI,GACf,EAAS,IAAI,GACb,EAAa,CAAC,CAAC,GACf,EAAoB,CAAC,CAAC;EAEtB,IAAI;GACF,EAAW,MAAM,EAAkB,CAAc,CAAC;EACpD,SAAS,GAAuB;GAC9B,EAAS,EAAiB,CAAY,CAAC;EACzC,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CAEA,eAAe,KAA8B;EAC3C,IAAI,CAAC,KAAW,CAAC,GACf;EAKF,AAFA,EAAc,EAAI,GAClB,EAAS,IAAI,GACb,EAAc,CAAC,CAAC;EAEhB,IAAM,IAAyB,EAC7B,OAAO,OAAO,CAAgB,CAChC;EAEA,IAAI,GAAwB;GAE1B,AADA,EAAS,CAAsB,GAC/B,EAAc,EAAK;GAEnB;EACF;EAEA,IAAM,IAAa,EAA2B;GAC5C,QAAQ,EAAQ,YAAY;GAC5B,UAAU,EAAQ,YAAY;GAC9B,QAAQ;EACV,CAAC;EAED,IAAI,CAAC,EAAW,OAAO;GAQrB,AAPA,EAAc,EAAW,MAAM,GAC/B,EAAS,WAAW,GAEhB,EAAW,wBACb,EAAuB,EAAW,oBAAoB,GAGxD,EAAc,EAAK;GAEnB;EACF;EAEA,IAAI;GACF,IAAM,IAAa,MAAM,EAAuB;IAC9C,UAAU;IACV,mBAAmB;IACnB,YAAY,EAAQ,SAAS;IAC7B,OAAO,EAAsB;KAC3B,eAAe,EAAQ,SAAS;KAChC,UAAU;KACV,QAAQ,EAAQ,YAAY;KAC5B,UAAU,EAAQ,YAAY;IAChC,CAAC;GACH,CAAC;GAED,EAAO,KAAK,EAAO,WAAW,CAAU,CAAC;EAC3C,SAAS,GAAuB;GAC9B,EAAS,EAAiB,CAAY,CAAC;EACzC,UAAU;GACR,EAAc,EAAK;EACrB;CACF;CAEA,eAAe,GACb,GACA,GACkC;EAClC,IAAI,CAAC,GACH,MAAU,MAAM,aAAa;EAQ/B,OAAO,EAAE,KAAI,MALY,EAAiB;GACxC;GACA,eAAe,QAAQ,EAAM;EAC/B,CAAC,EAAA,CAEuB,GAAG;CAC7B;CAEA,OACE,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EACE,aACE,IACI,GAAG,EAAQ,SAAS,KAAK,SAAS,EAAQ,YAAY,YACtD;EAEN,OAAM;EAEL,UAAA,IACC,kBAAC,GAAD;GACE,UAAU,KAAW;GACrB,MAAM;GACN,UAAS;GACT,SAAS;GACT,SAAQ;GACT,UAAA;EAEO,CAAA,IACN;CACS,CAAA,EACL,CAAA,GAEZ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;EAAK,OAAO;EAAZ,UAAA;GACG,IACC,kBAAC,GAAD;IAAY,OAAM;IAAa,SAAQ;IACpC,UAAA;GACS,CAAA,IACV;GAEH,IACC,kBAAC,GAAD;IAAY,OAAM;IAAe,SAAQ;IAAO,UAAA;GAEpC,CAAA,IACV;GAEH,IACC,kBAAC,GAAD;IACE,mBAAmB;KACjB,MAAM;KACN,YAAY,EAAQ,SAAS;IAC/B;IACA,QAAQ;IACR,UAAU;IACV,WAAW,MAAiB;KAE1B,AADA,EAAc,CAAM,GACpB,EAAc,CAAC,CAAC;IAClB;IACA,yBAAyB;IACzB,oBAAoB;IACpB,QAAQ,EAAQ,YAAY;IAC5B,cAAA;IACA,UAAU,EAAQ,YAAY;IAC9B,OAAO;GACR,CAAA,IACC;GAEH,CAAC,KAAc,CAAC,IACf,EAAK,SAAS,IACZ,kBAAC,GAAD;IACE,SAAS;IACA;IACT,YAAY;IACZ,WAAA;GACD,CAAA,IAED,kBAAC,GAAD;IAAY,OAAM;IAAe,SAAQ;IAAO,UAAA;GAEpC,CAAA,IAEZ;EACD;CACE,CAAA,EAAA,CAAA,EACG,CAAA,CACd,EAAA,CAAA;AAER;AAEA,SAAS,IAA2C;CAClD,OACE,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EACE,aAAY;EACZ,OAAM;CACP,CAAA,EACS,CAAA,GAEZ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAY,OAAM;EAAe,SAAQ;EAAO,UAAA;CAEpC,CAAA,EACL,CAAA,EACG,CAAA,CACd,EAAA,CAAA;AAER;AAEA,SAAS,EAAiB,GAAwB;CAShD,OAN0B,EAA+B,CAErD,MAIG,aAAiB,QAAQ,EAAM,UAAU;AAClD"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/views/instances/new/InstanceNewView.tsx"],"sourcesContent":["'use client';\n\nimport {\n CSSProperties,\n ReactElement,\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n Button,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { CheckedIcon } from '@mezzanine-ui/icons';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { FormFieldDefinition } from '@rytass/bpm-core-shared/form';\nimport {\n buildFormRendererValues,\n focusFormRendererField,\n FormRendererValues,\n readFormDataSourceErrorMessage,\n validateFormRendererValues,\n} from '@rytass/bpm-core-client/form';\nimport {\n LaunchContext,\n LaunchableTemplateRecord,\n listLaunchableTemplates,\n readFormDataCaseTitle,\n readLaunchContext,\n submitApprovalInstance,\n uploadAttachment,\n} from '@rytass/bpm-core-client/workflow';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport { useAuth } from '../../../lib/auth-provider';\nimport { useRouterAdapter } from '../../../lib/router-adapter';\nimport { useBPMRoutes } from '../../../lib/routes-config';\nimport { FormRenderer } from '../../forms/renderer/FormRendererView';\nimport {\n readFormDataSourceSubmissionBlockMessage,\n type FormDataSourceFieldState,\n} from '../../forms/renderer/form-data-source-field';\n\nconst FORM_SECTION_STYLE: CSSProperties = {\n display: 'grid',\n gap: 16,\n};\n\ntype LaunchableTemplateRow = Readonly<\n Record<string, unknown> &\n LaunchableTemplateRecord & {\n key: string;\n versionLabel: string;\n }\n>;\n\nexport interface InstanceNewViewProps {\n /**\n * Optional template id pre-selected by the host. The Server Component\n * page should read `?templateId=` from its `searchParams` and pass the\n * value through this prop.\n */\n readonly templateId?: string | null;\n}\n\n/**\n * Framework-agnostic view for the BPM \"發起簽核\" launch page. Mechanical port\n * of `apps/client/src/app/instances/new/page.tsx` — picks a launchable\n * template and submits the launch form.\n */\nexport function InstanceNewView(\n props: InstanceNewViewProps = {},\n): ReactElement {\n return (\n <Suspense fallback={<NewApprovalInstanceLoading />}>\n <NewApprovalInstanceContent templateId={props.templateId ?? undefined} />\n </Suspense>\n );\n}\n\nfunction NewApprovalInstanceContent({\n templateId: templateIdProp,\n}: {\n readonly templateId?: string;\n}): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const templateId = templateIdProp ?? null;\n const [context, setContext] = useState<LaunchContext | null>(null);\n const [formValues, setFormValues] = useState<FormRendererValues>({});\n const [templates, setTemplates] = useState<\n readonly LaunchableTemplateRecord[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [formErrors, setFormErrors] = useState<\n Readonly<Record<string, string>>\n >({});\n const [loading, setLoading] = useState(true);\n const [submitting, setSubmitting] = useState(false);\n const [dataSourceStates, setDataSourceStates] = useState<\n Readonly<\n Record<string, Pick<FormDataSourceFieldState, 'hasValue' | 'status'>>\n >\n >({});\n\n const handleDataSourceStateChange = useCallback(\n (\n fieldKey: string,\n state: Pick<\n FormDataSourceFieldState,\n 'hasValue' | 'invalidValues' | 'status'\n >,\n ): void => {\n setDataSourceStates((currentStates) => {\n const currentState = currentStates[fieldKey];\n\n if (\n currentState?.hasValue === state.hasValue &&\n currentState.status === state.status\n ) {\n return currentStates;\n }\n\n return {\n ...currentStates,\n [fieldKey]: {\n hasValue: state.hasValue,\n status: state.status,\n },\n };\n });\n },\n [],\n );\n\n useEffect((): void => {\n if (!templateId) {\n void refreshTemplates();\n\n return;\n }\n\n void refreshContext(templateId);\n }, [templateId]);\n\n const rows = useMemo(\n (): LaunchableTemplateRow[] =>\n templates.map((template) => ({\n ...template,\n key: template.id,\n updatedAt: formatDateTime(template.updatedAt),\n versionLabel: `v${template.version}`,\n })),\n [templates],\n );\n const columns = useMemo(\n (): TableColumn<LaunchableTemplateRow>[] => [\n { dataIndex: 'name', key: 'name', title: '模板名稱', width: 240 },\n {\n dataIndex: 'versionLabel',\n key: 'versionLabel',\n title: '發布版本',\n width: 120,\n },\n {\n dataIndex: 'updatedAt',\n key: 'updatedAt',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<LaunchableTemplateRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<LaunchableTemplateRow>['render']> => [\n {\n name: '發起',\n onClick: (): void =>\n router.push(routes.caseNew(record.id)),\n variant: 'base-primary',\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router],\n );\n\n async function refreshTemplates(): Promise<void> {\n setLoading(true);\n setError(null);\n setContext(null);\n setDataSourceStates({});\n\n try {\n setTemplates(await listLaunchableTemplates());\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function refreshContext(nextTemplateId: string): Promise<void> {\n setLoading(true);\n setError(null);\n setTemplates([]);\n setDataSourceStates({});\n\n try {\n setContext(await readLaunchContext(nextTemplateId));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function handleSubmit(): Promise<void> {\n if (!context || !currentMemberId) {\n return;\n }\n\n setSubmitting(true);\n setError(null);\n setFormErrors({});\n\n const dataSourceBlockMessage = readFormDataSourceSubmissionBlockMessage(\n Object.values(dataSourceStates),\n );\n\n if (dataSourceBlockMessage) {\n setError(dataSourceBlockMessage);\n setSubmitting(false);\n\n return;\n }\n\n // What the renderer shows, not just what has been touched: a table seeds\n // its `minRows` rows and a field can carry a `defaultValue`, and neither\n // reaches this state until the filler edits something. Validating and\n // submitting the raw state would reject a table the user can plainly see\n // rows in — and submit a default the backend never receives.\n const submittedValues = buildFormRendererValues(\n context.formVersion.schema.fields,\n formValues,\n );\n const validation = validateFormRendererValues({\n schema: context.formVersion.schema,\n uiSchema: context.formVersion.uiSchema,\n values: submittedValues,\n });\n\n if (!validation.valid) {\n setFormErrors(validation.errors);\n setError('請先補齊必填欄位。');\n\n if (validation.firstInvalidFieldKey) {\n focusFormRendererField(validation.firstInvalidFieldKey);\n }\n\n setSubmitting(false);\n\n return;\n }\n\n try {\n const instanceId = await submitApprovalInstance({\n formData: submittedValues,\n initiatorMemberId: currentMemberId,\n templateId: context.template.id,\n title: readFormDataCaseTitle({\n fallbackTitle: context.template.name,\n formData: submittedValues,\n schema: context.formVersion.schema,\n uiSchema: context.formVersion.uiSchema,\n }),\n });\n\n router.push(routes.caseDetail(instanceId));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSubmitting(false);\n }\n }\n\n async function handleUploadAttachment(\n field: FormFieldDefinition,\n file: File,\n ): Promise<{ readonly id: string }> {\n if (!currentMemberId) {\n throw new Error('尚未登入,無法上傳附件');\n }\n\n const attachment = await uploadAttachment({\n file,\n formFieldPath: `form.${field.fieldKey}`,\n });\n\n return { id: attachment.id };\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description={\n context\n ? `${context.template.name} · 表單 v${context.formVersion.version}`\n : '選擇可發起的已發布模板後填寫表單內容。'\n }\n title=\"發起簽核\"\n >\n {context ? (\n <Button\n disabled={loading || submitting}\n icon={CheckedIcon}\n iconType=\"leading\"\n onClick={handleSubmit}\n variant=\"base-primary\"\n >\n 送出\n </Button>\n ) : null}\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <div style={FORM_SECTION_STYLE}>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n {loading ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n ) : null}\n\n {context ? (\n <FormRenderer\n dataSourceContext={{\n kind: 'runtime',\n templateId: context.template.id,\n }}\n errors={formErrors}\n maxWidth={480}\n onChange={(values): void => {\n setFormValues(values);\n setFormErrors({});\n }}\n onDataSourceStateChange={handleDataSourceStateChange}\n onUploadAttachment={handleUploadAttachment}\n schema={context.formVersion.schema}\n singleColumn\n uiSchema={context.formVersion.uiSchema}\n value={formValues}\n />\n ) : null}\n\n {!templateId && !loading ? (\n rows.length > 0 ? (\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n />\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 目前沒有可發起的已發布模板。\n </Typography>\n )\n ) : null}\n </div>\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction NewApprovalInstanceLoading(): ReactElement {\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"選擇可發起的已發布模板後填寫表單內容。\"\n title=\"發起簽核\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readErrorMessage(error: unknown): string {\n // DataSource failures arrive as stable codes so the backend never leaks\n // upstream transport detail; translate them instead of showing the raw code.\n const dataSourceMessage = readFormDataSourceErrorMessage(error);\n\n if (dataSourceMessage) {\n return dataSourceMessage;\n }\n\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":";;;;;;;;;;;;;;AAiDA,IAAM,IAAoC;CACxC,SAAS;CACT,KAAK;AACP;AAwBA,SAAgB,EACd,IAA8B,CAAC,GACjB;CACd,OACE,kBAAC,GAAD;EAAU,UAAU,kBAAC,GAAD,CAA6B,CAAA;EAC/C,UAAA,kBAAC,GAAD,EAA4B,YAAY,EAAM,cAAc,KAAA,EAAY,CAAA;CAChE,CAAA;AAEd;AAEA,SAAS,EAA2B,EAClC,YAAY,KAGG;CACf,IAAM,IAAS,EAAiB,GAC1B,IAAS,EAAa,GACtB,EAAE,cAAW,EAAQ,GACrB,IAAkB,GAAQ,YAAY,MACtC,IAAa,KAAkB,MAC/B,CAAC,GAAS,KAAc,EAA+B,IAAI,GAC3D,CAAC,GAAY,KAAiB,EAA6B,CAAC,CAAC,GAC7D,CAAC,GAAW,KAAgB,EAEhC,CAAC,CAAC,GACE,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAY,KAAiB,EAElC,CAAC,CAAC,GACE,CAAC,GAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAkB,KAAuB,EAI9C,CAAC,CAAC,GAEE,IAA8B,IAEhC,GACA,MAIS;EACT,GAAqB,MAAkB;GACrC,IAAM,IAAe,EAAc;GASnC,OANE,GAAc,aAAa,EAAM,YACjC,EAAa,WAAW,EAAM,SAEvB,IAGF;IACL,GAAG;KACF,IAAW;KACV,UAAU,EAAM;KAChB,QAAQ,EAAM;IAChB;GACF;EACF,CAAC;CACH,GACA,CAAC,CACH;CAEA,QAAsB;EACpB,IAAI,CAAC,GAAY;GACf,GAAsB;GAEtB;EACF;EAEA,GAAoB,CAAU;CAChC,GAAG,CAAC,CAAU,CAAC;CAEf,IAAM,IAAO,QAET,EAAU,KAAK,OAAc;EAC3B,GAAG;EACH,KAAK,EAAS;EACd,WAAW,EAAe,EAAS,SAAS;EAC5C,cAAc,IAAI,EAAS;CAC7B,EAAE,GACJ,CAAC,CAAS,CACZ,GACM,KAAU,QAC8B;EAC1C;GAAE,WAAW;GAAQ,KAAK;GAAQ,OAAO;GAAQ,OAAO;EAAI;EAC5D;GACE,WAAW;GACX,KAAK;GACL,OAAO;GACP,OAAO;EACT;EACA;GACE,WAAW;GACX,KAAK;GACL,OAAO;GACP,OAAO;EACT;CACF,GACA,CAAC,CACH,GACM,KAAe,SACyB;EAC1C,SACE,MAC8D,CAC9D;GACE,MAAM;GACN,eACE,EAAO,KAAK,EAAO,QAAQ,EAAO,EAAE,CAAC;GACvC,SAAS;EACX,CACF;EACA,SAAS;EACT,OAAO;CACT,IACA,CAAC,CAAM,CACT;CAEA,eAAe,KAAkC;EAI/C,AAHA,EAAW,EAAI,GACf,EAAS,IAAI,GACb,EAAW,IAAI,GACf,EAAoB,CAAC,CAAC;EAEtB,IAAI;GACF,EAAa,MAAM,EAAwB,CAAC;EAC9C,SAAS,GAAuB;GAC9B,EAAS,EAAiB,CAAY,CAAC;EACzC,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CAEA,eAAe,GAAe,GAAuC;EAInE,AAHA,EAAW,EAAI,GACf,EAAS,IAAI,GACb,EAAa,CAAC,CAAC,GACf,EAAoB,CAAC,CAAC;EAEtB,IAAI;GACF,EAAW,MAAM,EAAkB,CAAc,CAAC;EACpD,SAAS,GAAuB;GAC9B,EAAS,EAAiB,CAAY,CAAC;EACzC,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CAEA,eAAe,KAA8B;EAC3C,IAAI,CAAC,KAAW,CAAC,GACf;EAKF,AAFA,EAAc,EAAI,GAClB,EAAS,IAAI,GACb,EAAc,CAAC,CAAC;EAEhB,IAAM,IAAyB,EAC7B,OAAO,OAAO,CAAgB,CAChC;EAEA,IAAI,GAAwB;GAE1B,AADA,EAAS,CAAsB,GAC/B,EAAc,EAAK;GAEnB;EACF;EAOA,IAAM,IAAkB,EACtB,EAAQ,YAAY,OAAO,QAC3B,CACF,GACM,IAAa,EAA2B;GAC5C,QAAQ,EAAQ,YAAY;GAC5B,UAAU,EAAQ,YAAY;GAC9B,QAAQ;EACV,CAAC;EAED,IAAI,CAAC,EAAW,OAAO;GAQrB,AAPA,EAAc,EAAW,MAAM,GAC/B,EAAS,WAAW,GAEhB,EAAW,wBACb,EAAuB,EAAW,oBAAoB,GAGxD,EAAc,EAAK;GAEnB;EACF;EAEA,IAAI;GACF,IAAM,IAAa,MAAM,EAAuB;IAC9C,UAAU;IACV,mBAAmB;IACnB,YAAY,EAAQ,SAAS;IAC7B,OAAO,EAAsB;KAC3B,eAAe,EAAQ,SAAS;KAChC,UAAU;KACV,QAAQ,EAAQ,YAAY;KAC5B,UAAU,EAAQ,YAAY;IAChC,CAAC;GACH,CAAC;GAED,EAAO,KAAK,EAAO,WAAW,CAAU,CAAC;EAC3C,SAAS,GAAuB;GAC9B,EAAS,EAAiB,CAAY,CAAC;EACzC,UAAU;GACR,EAAc,EAAK;EACrB;CACF;CAEA,eAAe,GACb,GACA,GACkC;EAClC,IAAI,CAAC,GACH,MAAU,MAAM,aAAa;EAQ/B,OAAO,EAAE,KAAI,MALY,EAAiB;GACxC;GACA,eAAe,QAAQ,EAAM;EAC/B,CAAC,EAAA,CAEuB,GAAG;CAC7B;CAEA,OACE,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EACE,aACE,IACI,GAAG,EAAQ,SAAS,KAAK,SAAS,EAAQ,YAAY,YACtD;EAEN,OAAM;EAEL,UAAA,IACC,kBAAC,GAAD;GACE,UAAU,KAAW;GACrB,MAAM;GACN,UAAS;GACT,SAAS;GACT,SAAQ;GACT,UAAA;EAEO,CAAA,IACN;CACS,CAAA,EACL,CAAA,GAEZ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;EAAK,OAAO;EAAZ,UAAA;GACG,IACC,kBAAC,GAAD;IAAY,OAAM;IAAa,SAAQ;IACpC,UAAA;GACS,CAAA,IACV;GAEH,IACC,kBAAC,GAAD;IAAY,OAAM;IAAe,SAAQ;IAAO,UAAA;GAEpC,CAAA,IACV;GAEH,IACC,kBAAC,GAAD;IACE,mBAAmB;KACjB,MAAM;KACN,YAAY,EAAQ,SAAS;IAC/B;IACA,QAAQ;IACR,UAAU;IACV,WAAW,MAAiB;KAE1B,AADA,EAAc,CAAM,GACpB,EAAc,CAAC,CAAC;IAClB;IACA,yBAAyB;IACzB,oBAAoB;IACpB,QAAQ,EAAQ,YAAY;IAC5B,cAAA;IACA,UAAU,EAAQ,YAAY;IAC9B,OAAO;GACR,CAAA,IACC;GAEH,CAAC,KAAc,CAAC,IACf,EAAK,SAAS,IACZ,kBAAC,GAAD;IACE,SAAS;IACA;IACT,YAAY;IACZ,WAAA;GACD,CAAA,IAED,kBAAC,GAAD;IAAY,OAAM;IAAe,SAAQ;IAAO,UAAA;GAEpC,CAAA,IAEZ;EACD;CACE,CAAA,EAAA,CAAA,EACG,CAAA,CACd,EAAA,CAAA;AAER;AAEA,SAAS,IAA2C;CAClD,OACE,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EACE,aAAY;EACZ,OAAM;CACP,CAAA,EACS,CAAA,GAEZ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAY,OAAM;EAAe,SAAQ;EAAO,UAAA;CAEpC,CAAA,EACL,CAAA,EACG,CAAA,CACd,EAAA,CAAA;AAER;AAEA,SAAS,EAAiB,GAAwB;CAShD,OAN0B,EAA+B,CAErD,MAIG,aAAiB,QAAQ,EAAM,UAAU;AAClD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/compose-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/compose-DzRYZcqe.cjs");exports.EMPTY_COMPOSE_FORM_SCHEMA=e.n,exports.EMPTY_COMPOSE_FORM_UI_SCHEMA=e.r,exports.TemplateComposeWizardView=e.t,exports.useTemplateComposeWizard=e.i;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as e, n as t, r as n, t as r } from "../../../chunks/compose-
|
|
1
|
+
import { i as e, n as t, r as n, t as r } from "../../../chunks/compose-Bb-REfoB.js";
|
|
2
2
|
export { t as EMPTY_COMPOSE_FORM_SCHEMA, n as EMPTY_COMPOSE_FORM_UI_SCHEMA, r as TemplateComposeWizardView, e as useTemplateComposeWizard };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/designer-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/designer-gFUdImeZ.cjs");exports.TemplateDesignerView=e.t;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "../../../chunks/designer-
|
|
1
|
+
import { t as e } from "../../../chunks/designer-C6oIO9Kq.js";
|
|
2
2
|
export { e as TemplateDesignerView };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rytass/bpm-core-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "BPM approval workflow React components and views for the Rytass BPM stack. Ships AuthProvider, NotificationDrawer + bell widget, host-facing hooks (useBPMMember / useBPMLogout / useBPMRoutes), and full page-body views for inbox / instances / templates / forms / admin / settings on top of Mezzanine UI and the BPM client functions. The host owns the navigation shell — BPM does not bundle a layout.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bpm",
|
|
@@ -286,8 +286,8 @@
|
|
|
286
286
|
"@mezzanine-ui/core": ">=1.0.0",
|
|
287
287
|
"@mezzanine-ui/icons": ">=1.0.0",
|
|
288
288
|
"@mezzanine-ui/react": ">=1.0.0",
|
|
289
|
-
"@rytass/bpm-core-client": "^0.
|
|
290
|
-
"@rytass/bpm-core-shared": "^0.
|
|
289
|
+
"@rytass/bpm-core-client": "^0.12.0",
|
|
290
|
+
"@rytass/bpm-core-shared": "^0.12.0",
|
|
291
291
|
"@uiw/react-codemirror": ">=4.0.0",
|
|
292
292
|
"@xyflow/react": ">=12.0.0",
|
|
293
293
|
"ai": ">=6.0.0",
|