@rytass/bpm-core-react 0.17.6 → 0.17.7
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 +33 -0
- package/dist/chunks/{FormBuilderView-aBcA-_tt.cjs → FormBuilderView-CUOyS07f.cjs} +2 -2
- package/dist/chunks/{FormBuilderView-aBcA-_tt.cjs.map → FormBuilderView-CUOyS07f.cjs.map} +1 -1
- package/dist/chunks/{FormBuilderView-BYL42M0d.js → FormBuilderView-ClLnQ1e_.js} +2 -2
- package/dist/chunks/{FormBuilderView-BYL42M0d.js.map → FormBuilderView-ClLnQ1e_.js.map} +1 -1
- package/dist/chunks/{FormRendererView-cZGOOjU5.js → FormRendererView-BsTwZkqU.js} +207 -198
- package/dist/chunks/FormRendererView-BsTwZkqU.js.map +1 -0
- package/dist/chunks/FormRendererView-GpsE03np.cjs +4 -0
- package/dist/chunks/FormRendererView-GpsE03np.cjs.map +1 -0
- package/dist/chunks/approver-filled-form-D1DmqDVP.cjs +2 -0
- package/dist/chunks/approver-filled-form-D1DmqDVP.cjs.map +1 -0
- package/dist/chunks/approver-filled-form-DuQGPtNB.js +95 -0
- package/dist/chunks/approver-filled-form-DuQGPtNB.js.map +1 -0
- package/dist/chunks/{compose-DOEaPfEC.cjs → compose-DjEJi5Dv.cjs} +2 -2
- package/dist/chunks/{compose-DOEaPfEC.cjs.map → compose-DjEJi5Dv.cjs.map} +1 -1
- package/dist/chunks/{compose-v1Vcw9hE.js → compose-WekGZj74.js} +4 -4
- package/dist/chunks/{compose-v1Vcw9hE.js.map → compose-WekGZj74.js.map} +1 -1
- package/dist/chunks/{designer-DBBrktom.js → designer-D87ILhjz.js} +1664 -1182
- package/dist/chunks/designer-D87ILhjz.js.map +1 -0
- package/dist/chunks/designer-DESS0CMl.cjs +65 -0
- package/dist/chunks/designer-DESS0CMl.cjs.map +1 -0
- package/dist/chunks/detail-D49NuNCn.cjs +2 -0
- package/dist/chunks/detail-D49NuNCn.cjs.map +1 -0
- package/dist/chunks/{detail-B_bSg31B.js → detail-URwxkcro.js} +1036 -847
- package/dist/chunks/detail-URwxkcro.js.map +1 -0
- package/dist/chunks/{providers-H_vPRsSN.js → providers-B_d5tbCL.js} +178 -167
- package/dist/chunks/providers-B_d5tbCL.js.map +1 -0
- package/dist/chunks/providers-chuJvMgv.cjs +2 -0
- package/dist/chunks/providers-chuJvMgv.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/next/index.cjs +1 -1
- package/dist/next/index.js +1 -1
- package/dist/next/workflow-chat-route.cjs +1 -0
- package/dist/next/workflow-chat-route.cjs.map +1 -1
- package/dist/next/workflow-chat-route.d.cts +1 -1
- package/dist/next/workflow-chat-route.d.ts +1 -1
- package/dist/next/workflow-chat-route.js +1 -1
- package/dist/next/workflow-chat-route.js.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/FormRendererView.d.cts +16 -1
- package/dist/views/forms/renderer/FormRendererView.d.ts +16 -1
- package/dist/views/forms/renderer/index.cjs +1 -1
- package/dist/views/forms/renderer/index.js +1 -1
- package/dist/views/inbox/index.cjs +1 -1
- package/dist/views/inbox/index.cjs.map +1 -1
- package/dist/views/inbox/index.js +109 -99
- package/dist/views/inbox/index.js.map +1 -1
- package/dist/views/instances/approver-filled-form.d.cts +76 -0
- package/dist/views/instances/approver-filled-form.d.ts +76 -0
- package/dist/views/instances/detail/index.cjs +1 -1
- package/dist/views/instances/detail/index.js +1 -1
- package/dist/views/instances/detail/sections/InstanceFormSection.d.cts +30 -2
- package/dist/views/instances/detail/sections/InstanceFormSection.d.ts +30 -2
- package/dist/views/instances/detail/sections/InstanceHistorySection.d.cts +4 -1
- package/dist/views/instances/detail/sections/InstanceHistorySection.d.ts +4 -1
- package/dist/views/instances/detail/sections/InstanceTasksSection.d.cts +21 -0
- package/dist/views/instances/detail/sections/InstanceTasksSection.d.ts +21 -0
- package/dist/views/instances/detail/sections/container-helpers.d.cts +1 -1
- package/dist/views/instances/detail/sections/container-helpers.d.ts +1 -1
- package/dist/views/instances/detail/sections/shared.d.cts +11 -2
- package/dist/views/instances/detail/sections/shared.d.ts +11 -2
- 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 +88 -87
- package/dist/views/instances/new/index.js.map +1 -1
- package/dist/views/instances/viewer-field-access.d.cts +22 -0
- package/dist/views/instances/viewer-field-access.d.ts +22 -0
- package/dist/views/templates/compose/index.cjs +1 -1
- package/dist/views/templates/compose/index.js +1 -1
- package/dist/views/templates/designer/field-access-matrix/FieldAccessMatrixDrawer.d.cts +33 -0
- package/dist/views/templates/designer/field-access-matrix/FieldAccessMatrixDrawer.d.ts +33 -0
- package/dist/views/templates/designer/field-access-matrix/FieldAccessNodeSection.d.cts +20 -0
- package/dist/views/templates/designer/field-access-matrix/FieldAccessNodeSection.d.ts +20 -0
- package/dist/views/templates/designer/field-access-matrix/field-access-copy.d.cts +22 -0
- package/dist/views/templates/designer/field-access-matrix/field-access-copy.d.ts +22 -0
- 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/FormRendererView-DRFi_IH7.cjs +0 -4
- package/dist/chunks/FormRendererView-DRFi_IH7.cjs.map +0 -1
- package/dist/chunks/FormRendererView-cZGOOjU5.js.map +0 -1
- package/dist/chunks/designer-CTXMqCD2.cjs +0 -65
- package/dist/chunks/designer-CTXMqCD2.cjs.map +0 -1
- package/dist/chunks/designer-DBBrktom.js.map +0 -1
- package/dist/chunks/detail-BQSVgVlO.cjs +0 -2
- package/dist/chunks/detail-BQSVgVlO.cjs.map +0 -1
- package/dist/chunks/detail-B_bSg31B.js.map +0 -1
- package/dist/chunks/providers-DP7b0TMN.cjs +0 -2
- package/dist/chunks/providers-DP7b0TMN.cjs.map +0 -1
- package/dist/chunks/providers-H_vPRsSN.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers-B_d5tbCL.js","names":["_Fragment","Modal","Textarea","useRouterAdapter","useAuth","AuthProvider","BPMLanguageProvider","useBPMRoutes","BPMFormField","Fragment$1","Drawer","Modal$1","NotificationCenter","Textarea$1","NotificationDrawerContext","createContext","NotificationDrawerProvider","isOpen","setIsOpen","useState","open","useCallback","close","toggle","current","value","useMemo","useNotificationDrawer","useContext","NotificationUnreadContext","NotificationUnreadProvider","currentMemberId","member","unreadCount","setUnreadCount","refreshUnreadCount","next","readUnreadNotificationCount","active","useNotificationUnread","TIME_GROUP_ORDER","TIME_GROUP_LABEL","PAGE_SIZE","TOGGLE_ARCHIVED_OPTION_ID","buildNotificationOptions","canReject","readNotificationAllowReject","record","opensToFill","readNotificationHasFillableFields","RESOLVED_TITLE","resolveDisplayTitle","resolveSeverity","toSeverity","NotificationDrawer","router","routes","rows","setRows","totalCount","setTotalCount","page","setPage","loading","setLoading","bulkLoading","setBulkLoading","error","setError","notice","setNotice","filter","setFilter","includeArchived","setIncludeArchived","approveTarget","setApproveTarget","approveComment","setApproveComment","rejectTarget","setRejectTarget","rejectReason","setRejectReason","deciding","setDeciding","trimmedApproveComment","trimmedRejectReason","loadPage","result","listNotifications","nextPage","append","readErrorMessage","handleFilterChange","event","handleMarkAllRead","markAllNotificationsRead","handleLoadMore","handleMarkRead","markNotificationRead","handleArchive","label","unarchiveNotifications","archiveNotifications","handleFilterAreaSelect","option","handleOpenInstance","openApproveModal","closeApproveModal","handleApproveConfirm","target","decideTask","openRejectModal","closeRejectModal","handleRejectConfirm","handleBadgeSelect","action","isCardActivation","card","handleCardActivate","handleCardKeyDown","filteredRows","row","groupedRows","now","buckets","group","accumulator","resolveTimeGroup","items","hasMore","Fragment","openable","itemIndex","Typography","type","notificationDate","nowStartOfDay","notificationStartOfDay","yesterdayStartOfDay","payload","isRecord","Providers","CalendarLocale","DEFAULT_BPM_LANGUAGE","CalendarConfigProviderMoment","children"],"sources":["../../src/lib/notification-drawer-provider.tsx","../../src/lib/notification-unread-provider.tsx","../../src/components/notification-drawer.tsx","../../src/lib/providers.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\n\ninterface NotificationDrawerContextValue {\n readonly close: () => void;\n readonly isOpen: boolean;\n readonly open: () => void;\n readonly toggle: () => void;\n}\n\nconst NotificationDrawerContext =\n createContext<NotificationDrawerContextValue | null>(null);\n\ninterface NotificationDrawerProviderProps {\n readonly children: ReactNode;\n}\n\n/**\n * Controls the open/closed state of the BPM notification drawer. Wraps\n * children with a context that `<NotificationDrawer />` reads to mount /\n * hide itself, and that the host navigation reads (via\n * `<BPMNotificationBellButton />` or `useNotificationDrawer().open`) to\n * open the drawer when the bell icon is clicked.\n *\n * When used outside this provider, the returned hook is a safe no-op so\n * components don't crash in test or storybook environments.\n */\nexport function NotificationDrawerProvider({\n children,\n}: NotificationDrawerProviderProps): ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback((): void => {\n setIsOpen(true);\n }, []);\n const close = useCallback((): void => {\n setIsOpen(false);\n }, []);\n const toggle = useCallback((): void => {\n setIsOpen((current) => !current);\n }, []);\n\n const value = useMemo<NotificationDrawerContextValue>(\n () => ({ close, isOpen, open, toggle }),\n [close, isOpen, open, toggle],\n );\n\n return (\n <NotificationDrawerContext.Provider value={value}>\n {children}\n </NotificationDrawerContext.Provider>\n );\n}\n\n/**\n * Read the BPM notification drawer's open state and control helpers.\n * Returns a no-op stub when used outside `<NotificationDrawerProvider>`.\n */\nexport function useNotificationDrawer(): NotificationDrawerContextValue {\n const context = useContext(NotificationDrawerContext);\n if (!context) {\n return {\n close: (): void => undefined,\n isOpen: false,\n open: (): void => undefined,\n toggle: (): void => undefined,\n };\n }\n return context;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { readUnreadNotificationCount } from '@rytass/bpm-core-client/workflow';\nimport { useAuth } from './auth-provider';\n\ninterface NotificationUnreadContextValue {\n readonly refreshUnreadCount: () => Promise<number>;\n readonly unreadCount: number;\n}\n\nconst NotificationUnreadContext =\n createContext<NotificationUnreadContextValue | null>(null);\n\ninterface NotificationUnreadProviderProps {\n readonly children: ReactNode;\n}\n\n/**\n * Polls BPM for the current member's unread notification count via\n * `readUnreadNotificationCount` and exposes it through context for the\n * host navigation (`<BPMNotificationBellButton />` badge or any custom\n * trigger using `useNotificationUnread().unreadCount`) and the BPM\n * `<NotificationDrawer />` (header count). Refresh is triggered on\n * mount and whenever the auth member id changes; consumers can call\n * `refreshUnreadCount()` after acknowledging a notification.\n */\nexport function NotificationUnreadProvider({\n children,\n}: NotificationUnreadProviderProps): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [unreadCount, setUnreadCount] = useState(0);\n\n const refreshUnreadCount = useCallback(async (): Promise<number> => {\n if (!currentMemberId) {\n setUnreadCount(0);\n return 0;\n }\n const next = await readUnreadNotificationCount(currentMemberId);\n setUnreadCount(next);\n return next;\n }, [currentMemberId]);\n\n useEffect((): (() => void) => {\n let active = true;\n void (async () => {\n try {\n const next = await refreshUnreadCount();\n if (active) setUnreadCount(next);\n } catch {\n if (active) setUnreadCount(0);\n }\n })();\n return (): void => {\n active = false;\n };\n }, [refreshUnreadCount]);\n\n const value = useMemo<NotificationUnreadContextValue>(\n () => ({ refreshUnreadCount, unreadCount }),\n [refreshUnreadCount, unreadCount],\n );\n\n return (\n <NotificationUnreadContext.Provider value={value}>\n {children}\n </NotificationUnreadContext.Provider>\n );\n}\n\n/**\n * Read the current unread-notification count and a manual refresh helper.\n * Returns a zero/no-op stub when used outside\n * `<NotificationUnreadProvider>`.\n */\nexport function useNotificationUnread(): NotificationUnreadContextValue {\n const context = useContext(NotificationUnreadContext);\n if (!context) {\n return {\n refreshUnreadCount: async (): Promise<number> => 0,\n unreadCount: 0,\n };\n }\n return context;\n}\n","'use client';\n\nimport {\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ChangeEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n} from 'react';\nimport Drawer from '@mezzanine-ui/react/Drawer';\nimport Modal from '@mezzanine-ui/react/Modal';\nimport NotificationCenter from '@mezzanine-ui/react/NotificationCenter';\nimport Textarea from '@mezzanine-ui/react/Textarea';\nimport { Typography } from '@mezzanine-ui/react';\nimport type { NotificationSeverity } from '@mezzanine-ui/core/notification-center';\nimport type { DropdownOption } from '@mezzanine-ui/core/dropdown/dropdown';\nimport {\n archiveNotifications,\n decideTask,\n listNotifications,\n markAllNotificationsRead,\n markNotificationRead,\n unarchiveNotifications,\n type NotificationRecord,\n type NotificationResolution,\n type NotificationType,\n} from '@rytass/bpm-core-client/workflow';\nimport { useAuth } from '../lib/auth-provider';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { BPMFormField } from './bpm-form-field';\n\ntype FilterValue = 'all' | 'read' | 'unread';\n\ntype TimeGroup = 'today' | 'yesterday' | 'past7Days' | 'earlier';\n\nconst TIME_GROUP_ORDER: readonly TimeGroup[] = [\n 'today',\n 'yesterday',\n 'past7Days',\n 'earlier',\n];\n\nconst TIME_GROUP_LABEL: Readonly<Record<TimeGroup, string>> = {\n earlier: '更早',\n past7Days: '過去七天',\n today: '今天',\n yesterday: '昨天',\n};\n\nconst PAGE_SIZE = 50;\n\n/** Identifiers for the per-notification `...` dropdown menu entries. */\ntype NotificationAction =\n | 'approve'\n | 'archive'\n | 'reject'\n | 'unarchive'\n | 'view'\n | 'read';\n\n/** Identifier for the filter-bar dropdown entry toggling the archived view. */\nconst TOGGLE_ARCHIVED_OPTION_ID = 'toggleArchived';\n\n/**\n * Build the `...` dropdown options for one notification. Approve / reject are\n * offered only while the server still reports the notification as `actionable`\n * (an unresolved task assignment) — once the task is decided / cancelled the\n * backend flips `actionable` to false, so a stale \"同意\" can never appear.\n * A task that fills in form fields (`hasFillableFields`) cannot be approved\n * from here — the fill has to be entered on the case page — so its \"同意\"\n * becomes \"開啟案件\".\n * \"查看案件\" needs an `instanceId`; \"標為已讀\" only shows while unread.\n * Archiving is independent of read state, so the entry flips between\n * \"封存\" / \"取消封存\" for every notification.\n */\nfunction buildNotificationOptions(\n record: NotificationRecord,\n): readonly DropdownOption[] {\n const canReject = readNotificationAllowReject(record) !== false;\n const opensToFill =\n record.actionable &&\n Boolean(record.instanceId) &&\n readNotificationHasFillableFields(record);\n\n return [\n ...(record.actionable\n ? ([\n opensToFill\n ? { id: 'view', name: '開啟案件' }\n : { id: 'approve', name: '同意' },\n ...(canReject ? [{ id: 'reject', name: '拒絕' }] : []),\n ] satisfies DropdownOption[])\n : []),\n ...(record.instanceId && !opensToFill\n ? ([{ id: 'view', name: '查看案件' }] satisfies DropdownOption[])\n : []),\n ...(record.status !== 'READ'\n ? ([{ id: 'read', name: '標為已讀' }] satisfies DropdownOption[])\n : []),\n record.archivedAt\n ? { id: 'unarchive', name: '取消封存' }\n : { id: 'archive', name: '封存' },\n ];\n}\n\n/**\n * Title shown for a resolved task-assignment notification, replacing the\n * stored \"新的待簽任務\" wording so a decided card no longer reads as pending.\n * The stored `body` is kept as historical context (which case / node).\n */\nconst RESOLVED_TITLE: Readonly<Record<NotificationResolution, string>> = {\n APPROVED: '簽核任務已同意',\n REJECTED: '簽核任務已拒絕',\n RETURNED: '簽核任務已退回',\n SUPERSEDED: '簽核任務已結束',\n TRANSFERRED: '簽核任務已轉派',\n};\n\nfunction resolveDisplayTitle(record: NotificationRecord): string {\n return record.resolution ? RESOLVED_TITLE[record.resolution] : record.title;\n}\n\n/**\n * Severity (icon colour) for a notification. Once resolved, it reflects the\n * outcome — green for approved, red for rejected, neutral otherwise — instead\n * of the original by-type colour.\n */\nfunction resolveSeverity(record: NotificationRecord): NotificationSeverity {\n if (record.resolution === 'APPROVED') return 'success';\n if (record.resolution === 'REJECTED') return 'error';\n if (record.resolution) return 'info';\n return toSeverity(record.type);\n}\n\n/**\n * Right-side notification drawer mounted at the root by `<Providers>`.\n * Opens / closes via `useNotificationDrawer()`, polls\n * `listNotifications()` for the current member, supports filter\n * (`all` / `read` / `unread`), per-row mark-read, per-row archive / unarchive,\n * bulk mark-all-read, and load-more pagination. Archived notifications are\n * hidden until the filter-bar menu turns \"顯示已封存\" on. Clicking a row with an\n * `instanceId` navigates to `/instances/<id>` via the host's router adapter.\n */\nexport function NotificationDrawer(): ReactElement | null {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const { close, isOpen } = useNotificationDrawer();\n const { refreshUnreadCount } = useNotificationUnread();\n const currentMemberId = member?.memberId ?? null;\n const [rows, setRows] = useState<readonly NotificationRecord[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [page, setPage] = useState(1);\n const [loading, setLoading] = useState(false);\n const [bulkLoading, setBulkLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const [filter, setFilter] = useState<FilterValue>('all');\n const [includeArchived, setIncludeArchived] = useState(false);\n const [approveTarget, setApproveTarget] = useState<NotificationRecord | null>(\n null,\n );\n const [approveComment, setApproveComment] = useState('');\n const [rejectTarget, setRejectTarget] = useState<NotificationRecord | null>(\n null,\n );\n const [rejectReason, setRejectReason] = useState('');\n const [deciding, setDeciding] = useState(false);\n\n const trimmedApproveComment = approveComment.trim();\n const trimmedRejectReason = rejectReason.trim();\n\n const loadPage = useCallback(\n async (nextPage: number, append: boolean): Promise<boolean> => {\n if (!currentMemberId) return false;\n setLoading(true);\n setError(null);\n try {\n const result = await listNotifications({\n includeArchived,\n includeRead: true,\n page: nextPage,\n pageSize: PAGE_SIZE,\n recipientMemberId: currentMemberId,\n });\n setRows((current): readonly NotificationRecord[] =>\n append ? [...current, ...result.notifications] : result.notifications,\n );\n setTotalCount(result.totalCount);\n setPage(nextPage);\n await refreshUnreadCount();\n return true;\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n return false;\n } finally {\n setLoading(false);\n }\n },\n [currentMemberId, includeArchived, refreshUnreadCount],\n );\n\n useEffect((): void => {\n if (!isOpen || !currentMemberId) return;\n setError(null);\n setNotice(null);\n void loadPage(1, false);\n }, [isOpen, currentMemberId, loadPage]);\n\n const handleFilterChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>): void => {\n const next = event.target.value;\n if (next === 'all' || next === 'read' || next === 'unread') setFilter(next);\n },\n [],\n );\n\n const handleMarkAllRead = useCallback(async (): Promise<void> => {\n if (!currentMemberId || bulkLoading) return;\n setBulkLoading(true);\n setError(null);\n try {\n await markAllNotificationsRead({ recipientMemberId: currentMemberId });\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setBulkLoading(false);\n }\n }, [bulkLoading, currentMemberId, loadPage]);\n\n const handleLoadMore = useCallback((): void => {\n if (loading) return;\n void loadPage(page + 1, true);\n }, [loading, loadPage, page]);\n\n const handleMarkRead = useCallback(\n async (id: string): Promise<void> => {\n if (!currentMemberId) return;\n try {\n await markNotificationRead({ id, readerMemberId: currentMemberId });\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [currentMemberId, loadPage],\n );\n\n const handleArchive = useCallback(\n async (record: NotificationRecord): Promise<void> => {\n if (!currentMemberId) return;\n setError(null);\n setNotice(null);\n try {\n // Name the notification the way the row does — `title` is the raw\n // stored title, which can differ from the rendered one.\n const label = resolveDisplayTitle(record);\n\n if (record.archivedAt) {\n await unarchiveNotifications({ ids: [record.id] });\n setNotice(`已將「${label}」移出封存。`);\n } else {\n await archiveNotifications({ ids: [record.id] });\n setNotice(`已封存「${label}」。`);\n }\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [currentMemberId, loadPage],\n );\n\n const handleFilterAreaSelect = useCallback((option: DropdownOption): void => {\n if (option.id !== TOGGLE_ARCHIVED_OPTION_ID) return;\n setNotice(null);\n setIncludeArchived((current): boolean => !current);\n }, []);\n\n const handleOpenInstance = useCallback(\n async (record: NotificationRecord): Promise<void> => {\n if (!record.instanceId || !currentMemberId) return;\n try {\n if (record.status !== 'READ') {\n await markNotificationRead({\n id: record.id,\n readerMemberId: currentMemberId,\n });\n await refreshUnreadCount();\n }\n close();\n router.push(routes.caseDetail(record.instanceId));\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [close, currentMemberId, refreshUnreadCount, router, routes],\n );\n\n const openApproveModal = useCallback((record: NotificationRecord): void => {\n setError(null);\n setApproveTarget(record);\n setApproveComment('');\n }, []);\n\n const closeApproveModal = useCallback((): void => {\n if (deciding) return;\n setApproveTarget(null);\n setApproveComment('');\n }, [deciding]);\n\n const handleApproveConfirm = useCallback(async (): Promise<void> => {\n const target = approveTarget;\n if (!target?.taskId || !currentMemberId || deciding) return;\n setDeciding(true);\n setError(null);\n setNotice(null);\n try {\n await decideTask({\n action: 'APPROVED',\n comment: trimmedApproveComment || null,\n decidedByMemberId: currentMemberId,\n taskId: target.taskId,\n });\n const refreshed = await loadPage(1, false);\n if (!refreshed) return;\n setApproveTarget(null);\n setApproveComment('');\n setNotice(`已同意「${target.title}」。`);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setDeciding(false);\n }\n }, [\n approveTarget,\n currentMemberId,\n deciding,\n loadPage,\n trimmedApproveComment,\n ]);\n\n const openRejectModal = useCallback((record: NotificationRecord): void => {\n setRejectTarget(record);\n setRejectReason('');\n }, []);\n\n const closeRejectModal = useCallback((): void => {\n setRejectTarget(null);\n setRejectReason('');\n }, []);\n\n const handleRejectConfirm = useCallback(async (): Promise<void> => {\n const target = rejectTarget;\n if (!target?.taskId || !currentMemberId || !trimmedRejectReason || deciding)\n return;\n setDeciding(true);\n setError(null);\n setNotice(null);\n try {\n await decideTask({\n action: 'REJECTED',\n comment: trimmedRejectReason,\n decidedByMemberId: currentMemberId,\n taskId: target.taskId,\n });\n setRejectTarget(null);\n setRejectReason('');\n setNotice(`已拒絕「${target.title}」。`);\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setDeciding(false);\n }\n }, [currentMemberId, deciding, loadPage, rejectTarget, trimmedRejectReason]);\n\n const handleBadgeSelect = useCallback(\n (record: NotificationRecord, option: DropdownOption): void => {\n const action = option.id as NotificationAction;\n if (action === 'approve') openApproveModal(record);\n else if (action === 'reject') openRejectModal(record);\n else if (action === 'view') void handleOpenInstance(record);\n else if (action === 'read') void handleMarkRead(record.id);\n else if (action === 'archive' || action === 'unarchive')\n void handleArchive(record);\n },\n [\n handleArchive,\n handleMarkRead,\n handleOpenInstance,\n openApproveModal,\n openRejectModal,\n ],\n );\n\n /**\n * Whether an event that reached the card wrapper actually happened inside the\n * card, and not on one of its action controls.\n *\n * Two escape hatches are needed. The `...` menu button (and its `<svg>` icon —\n * hence the `Element` check, SVG nodes are not HTMLElements) sits inside the\n * card and must open the dropdown rather than navigate. The dropdown itself\n * renders into a portal, and React replays events along the component tree\n * rather than the DOM tree, so picking \"封存\" also reaches this handler even\n * though the `<li role=\"option\">` lives outside the card's DOM — and it is not\n * a `<button>`, so the first guard alone misses it.\n */\n const isCardActivation = useCallback(\n (target: EventTarget | null, card: HTMLDivElement): boolean => {\n if (!(target instanceof Node) || !card.contains(target)) return false;\n\n return !(target instanceof Element && target.closest('button'));\n },\n [],\n );\n\n const handleCardActivate = useCallback(\n (record: NotificationRecord, event: ReactMouseEvent<HTMLDivElement>): void => {\n if (!isCardActivation(event.target, event.currentTarget)) return;\n void handleOpenInstance(record);\n },\n [handleOpenInstance, isCardActivation],\n );\n\n const handleCardKeyDown = useCallback(\n (\n record: NotificationRecord,\n event: ReactKeyboardEvent<HTMLDivElement>,\n ): void => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n if (!isCardActivation(event.target, event.currentTarget)) return;\n event.preventDefault();\n void handleOpenInstance(record);\n },\n [handleOpenInstance, isCardActivation],\n );\n\n const filteredRows = useMemo(\n (): readonly NotificationRecord[] =>\n rows.filter((row): boolean => {\n if (filter === 'all') return true;\n if (filter === 'read') return row.status === 'READ';\n return row.status !== 'READ';\n }),\n [filter, rows],\n );\n\n const groupedRows = useMemo(\n (): ReadonlyArray<readonly [TimeGroup, readonly NotificationRecord[]]> => {\n const now = new Date();\n const buckets = TIME_GROUP_ORDER.reduce<\n Record<TimeGroup, NotificationRecord[]>\n >(\n (accumulator, group) => {\n accumulator[group] = [];\n return accumulator;\n },\n { earlier: [], past7Days: [], today: [], yesterday: [] },\n );\n filteredRows.forEach((row): void => {\n buckets[resolveTimeGroup(row.createdAt, now)].push(row);\n });\n return TIME_GROUP_ORDER.map(\n (group) => [group, buckets[group]] as const,\n ).filter(([, items]) => items.length > 0);\n },\n [filteredRows],\n );\n\n const hasMore = rows.length < totalCount;\n\n if (!currentMemberId) return null;\n\n return (\n <>\n <Drawer\n bottomGhostActionDisabled={bulkLoading || loading}\n bottomGhostActionLoading={bulkLoading}\n bottomGhostActionText=\"全部標為已讀\"\n bottomOnGhostActionClick={(): void => {\n void handleMarkAllRead();\n }}\n bottomOnPrimaryActionClick={(): void => {\n handleLoadMore();\n }}\n bottomPrimaryActionDisabled={!hasMore || loading}\n bottomPrimaryActionLoading={loading && hasMore}\n bottomPrimaryActionText={hasMore ? '載入更多' : '已顯示全部'}\n contentKey={`${filter}:${includeArchived}:${rows.length}`}\n filterAreaAllRadioLabel=\"全部\"\n filterAreaOnRadioChange={handleFilterChange}\n filterAreaOnSelect={handleFilterAreaSelect}\n filterAreaOptions={[\n {\n id: TOGGLE_ARCHIVED_OPTION_ID,\n name: includeArchived ? '隱藏已封存' : '顯示已封存',\n },\n ]}\n filterAreaReadRadioLabel=\"已讀\"\n filterAreaShow\n filterAreaUnreadRadioLabel=\"未讀\"\n filterAreaValue={filter}\n headerTitle=\"通知中心\"\n isBottomDisplay\n isHeaderDisplay\n onClose={close}\n open={isOpen}\n size=\"medium\"\n >\n <div role=\"list\">\n {error ? (\n <p\n role=\"alert\"\n style={{\n color: 'var(--mzn-color-text-error, #d92d20)',\n padding: '12px 16px',\n }}\n >\n {error}\n </p>\n ) : null}\n {notice ? (\n <p\n role=\"status\"\n style={{\n color: 'var(--mzn-color-text-success, #079455)',\n padding: '12px 16px',\n }}\n >\n {notice}\n </p>\n ) : null}\n {groupedRows.length === 0 ? (\n <p\n style={{\n color: 'var(--mzn-color-text-secondary, #6b7280)',\n padding: '24px 16px',\n textAlign: 'center',\n }}\n >\n {loading ? '載入中…' : '目前沒有通知'}\n </p>\n ) : null}\n {groupedRows.map(([group, items]) => (\n <Fragment key={group}>\n {items.map((record, itemIndex) => {\n const openable = record.instanceId !== null;\n\n return (\n <div\n key={record.id}\n onClick={\n openable\n ? (event: ReactMouseEvent<HTMLDivElement>): void => {\n handleCardActivate(record, event);\n }\n : undefined\n }\n onKeyDown={\n openable\n ? (event: ReactKeyboardEvent<HTMLDivElement>): void => {\n handleCardKeyDown(record, event);\n }\n : undefined\n }\n role={openable ? 'button' : undefined}\n style={openable ? { cursor: 'pointer' } : undefined}\n tabIndex={openable ? 0 : undefined}\n >\n <NotificationCenter\n appendTips={record.archivedAt ? '已封存' : undefined}\n description={record.body}\n onBadgeSelect={(option: DropdownOption): void => {\n handleBadgeSelect(record, option);\n }}\n options={[...buildNotificationOptions(record)]}\n prependTips={\n itemIndex === 0 ? TIME_GROUP_LABEL[group] : undefined\n }\n reference={record.id}\n severity={resolveSeverity(record)}\n showBadge={record.status !== 'READ'}\n timeStamp={record.createdAt}\n title={resolveDisplayTitle(record)}\n type=\"drawer\"\n />\n </div>\n );\n })}\n </Fragment>\n ))}\n </div>\n </Drawer>\n <Modal\n cancelText=\"取消\"\n confirmText=\"送出同意\"\n loading={deciding}\n modalType=\"standard\"\n onCancel={closeApproveModal}\n onClose={closeApproveModal}\n onConfirm={(): void => {\n void handleApproveConfirm();\n }}\n open={approveTarget !== null}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"可以留下同意說明,內容會記錄在簽核歷程;不填也可以直接送出。\"\n title=\"簽核意見\"\n >\n {error ? (\n <Typography color=\"text-error\" role=\"alert\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <BPMFormField label=\"簽核意見\" name=\"notificationApproveComment\">\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setApproveComment(event.target.value);\n }}\n placeholder=\"選填,例如補充核准條件或提醒事項\"\n rows={4}\n value={approveComment}\n />\n </BPMFormField>\n </Modal>\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{\n disabled: !trimmedRejectReason,\n variant: 'destructive-primary',\n }}\n confirmText=\"送出拒絕\"\n loading={deciding}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={closeRejectModal}\n onClose={closeRejectModal}\n onConfirm={(): void => {\n void handleRejectConfirm();\n }}\n open={rejectTarget !== null}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"拒絕案件時必須留下原因,供發起人與後續追蹤查看。\"\n title=\"拒絕原因\"\n >\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setRejectReason(event.target.value);\n }}\n placeholder=\"請輸入拒絕原因\"\n rows={4}\n value={rejectReason}\n />\n </Modal>\n </>\n );\n}\n\nfunction toSeverity(type: NotificationType): NotificationSeverity {\n if (type === 'SLA_OVERDUE') return 'error';\n if (type === 'SLA_WARNING') return 'warning';\n if (type === 'INSTANCE_COMPLETED') return 'success';\n return 'info';\n}\n\nfunction resolveTimeGroup(value: string, now: Date): TimeGroup {\n const notificationDate = new Date(value);\n if (Number.isNaN(notificationDate.getTime())) return 'earlier';\n const nowStartOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n const notificationStartOfDay = new Date(\n notificationDate.getFullYear(),\n notificationDate.getMonth(),\n notificationDate.getDate(),\n );\n if (notificationStartOfDay.getTime() === nowStartOfDay.getTime()) return 'today';\n const yesterdayStartOfDay = new Date(nowStartOfDay);\n yesterdayStartOfDay.setDate(yesterdayStartOfDay.getDate() - 1);\n if (notificationStartOfDay.getTime() === yesterdayStartOfDay.getTime())\n return 'yesterday';\n const diffInDays =\n (now.getTime() - notificationDate.getTime()) / (1000 * 60 * 60 * 24);\n if (diffInDays <= 7) return 'past7Days';\n return 'earlier';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n\nfunction readNotificationAllowReject(\n record: NotificationRecord,\n): boolean | null {\n try {\n const payload: unknown = JSON.parse(record.payloadJson);\n\n if (isRecord(payload) && typeof payload.allowReject === 'boolean') {\n return payload.allowReject;\n }\n\n return record.allowReject;\n } catch {\n return record.allowReject;\n }\n}\n\n/**\n * `TASK_ASSIGNED` / `TASK_TRANSFERRED` payloads carry `hasFillableFields`;\n * older notifications do not, which reads as `false`.\n */\nfunction readNotificationHasFillableFields(\n record: NotificationRecord,\n): boolean {\n try {\n const payload: unknown = JSON.parse(record.payloadJson);\n\n return isRecord(payload) && payload.hasFillableFields === true;\n } catch {\n return false;\n }\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","'use client';\n\nimport type { ReactElement, ReactNode } from 'react';\nimport {\n CalendarConfigProviderMoment,\n CalendarLocale,\n} from '@mezzanine-ui/react/moment';\nimport { AuthProvider } from './auth-provider';\nimport {\n BPMLanguageProvider,\n DEFAULT_BPM_LANGUAGE,\n type BPMLanguage,\n} from './language-provider';\nimport { NotificationDrawer } from '../components/notification-drawer';\nimport { NotificationDrawerProvider } from './notification-drawer-provider';\nimport { NotificationUnreadProvider } from './notification-unread-provider';\n\ninterface ProvidersProps {\n readonly children: ReactNode;\n /** Override Mezzanine calendar locale. Defaults to `CalendarLocale.ZH_TW`. */\n readonly locale?: CalendarLocale;\n /**\n * Language BPM views write their messages in, such as publish-lint errors.\n * Defaults to `'zh-TW'`. Pass the user's choice as state to let them switch.\n * Independent of `locale`, which only formats calendar dates.\n */\n readonly language?: BPMLanguage;\n /**\n * Public paths that should not trigger redirect to `/login` when there\n * is no session. Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n */\n readonly publicPaths?: readonly string[];\n /** Where to send unauthenticated users. Defaults to `'/login'`. */\n readonly loginPath?: string;\n}\n\n/**\n * One-stop BPM admin provider stack. Wires:\n *\n * - Mezzanine UI calendar locale (moment-based, `ZH_TW` by default)\n * - `<BPMLanguageProvider>` (message language, `'zh-TW'` by default)\n * - `<AuthProvider>` (BPM session via REST `/auth/*`)\n * - `<NotificationUnreadProvider>` (polls unread count)\n * - `<NotificationDrawerProvider>` (controls drawer open/close state)\n * - `<NotificationDrawer />` mounted at the root so the host navigation\n * bell (`<BPMNotificationBellButton />` or any custom trigger calling\n * `useNotificationDrawer().open`) can open it.\n *\n * Consumer hosts wrap this **inside** a `<RouterAdapterProvider>` (provided\n * by the `pages/*` subpath shims when consuming via Next.js, or wired by\n * hand for other frameworks).\n */\nexport function Providers({\n children,\n locale = CalendarLocale.ZH_TW,\n language = DEFAULT_BPM_LANGUAGE,\n publicPaths,\n loginPath,\n}: ProvidersProps): ReactElement {\n return (\n <CalendarConfigProviderMoment locale={locale}>\n <BPMLanguageProvider language={language}>\n <AuthProvider publicPaths={publicPaths} loginPath={loginPath}>\n <NotificationUnreadProvider>\n <NotificationDrawerProvider>\n {children}\n <NotificationDrawer />\n </NotificationDrawerProvider>\n </NotificationUnreadProvider>\n </AuthProvider>\n </BPMLanguageProvider>\n </CalendarConfigProviderMoment>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./router-adapter--gYs13E8.cjs"),n=require("./auth-provider-4BeCw7cI.cjs"),r=require("./language-provider-Bdnx3Gxl.cjs"),i=require("./routes-config-C1j_vFKo.cjs"),a=require("./bpm-form-field-Bc6k4ZEO.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/moment"),d=require("@mezzanine-ui/react/Drawer");d=e.t(d,1);let f=require("@mezzanine-ui/react/Modal");f=e.t(f,1);let p=require("@mezzanine-ui/react/NotificationCenter");p=e.t(p,1);let m=require("@mezzanine-ui/react/Textarea");m=e.t(m,1);var h=(0,o.createContext)(null);function g({children:e}){let[t,n]=(0,o.useState)(!1),r=(0,o.useCallback)(()=>{n(!0)},[]),i=(0,o.useCallback)(()=>{n(!1)},[]),a=(0,o.useCallback)(()=>{n(e=>!e)},[]),s=(0,o.useMemo)(()=>({close:i,isOpen:t,open:r,toggle:a}),[i,t,r,a]);return(0,c.jsx)(h.Provider,{value:s,children:e})}function _(){return(0,o.useContext)(h)||{close:()=>void 0,isOpen:!1,open:()=>void 0,toggle:()=>void 0}}var v=(0,o.createContext)(null);function y({children:e}){let{member:t}=n.n(),r=t?.memberId??null,[i,a]=(0,o.useState)(0),s=(0,o.useCallback)(async()=>{if(!r)return a(0),0;let e=await(0,l.readUnreadNotificationCount)(r);return a(e),e},[r]);(0,o.useEffect)(()=>{let e=!0;return(async()=>{try{let t=await s();e&&a(t)}catch{e&&a(0)}})(),()=>{e=!1}},[s]);let u=(0,o.useMemo)(()=>({refreshUnreadCount:s,unreadCount:i}),[s,i]);return(0,c.jsx)(v.Provider,{value:u,children:e})}function ee(){return(0,o.useContext)(v)||{refreshUnreadCount:async()=>0,unreadCount:0}}var b=[`today`,`yesterday`,`past7Days`,`earlier`],te={earlier:`更早`,past7Days:`過去七天`,today:`今天`,yesterday:`昨天`},ne=50,x=`toggleArchived`;function re(e){let t=E(e)!==!1,n=e.actionable&&!!e.instanceId&&D(e);return[...e.actionable?[n?{id:`view`,name:`開啟案件`}:{id:`approve`,name:`同意`},...t?[{id:`reject`,name:`拒絕`}]:[]]:[],...e.instanceId&&!n?[{id:`view`,name:`查看案件`}]:[],...e.status===`READ`?[]:[{id:`read`,name:`標為已讀`}],e.archivedAt?{id:`unarchive`,name:`取消封存`}:{id:`archive`,name:`封存`}]}var S={APPROVED:`簽核任務已同意`,REJECTED:`簽核任務已拒絕`,RETURNED:`簽核任務已退回`,SUPERSEDED:`簽核任務已結束`,TRANSFERRED:`簽核任務已轉派`};function ie(e){return e.resolution?S[e.resolution]:e.title}function ae(e){return e.resolution===`APPROVED`?`success`:e.resolution===`REJECTED`?`error`:e.resolution?`info`:w(e.type)}function C(){let e=t.r(),r=i.r(),{member:u}=n.n(),{close:h,isOpen:g}=_(),{refreshUnreadCount:v}=ee(),y=u?.memberId??null,[S,C]=(0,o.useState)([]),[w,E]=(0,o.useState)(0),[D,O]=(0,o.useState)(1),[k,A]=(0,o.useState)(!1),[j,M]=(0,o.useState)(!1),[N,P]=(0,o.useState)(null),[F,I]=(0,o.useState)(null),[L,se]=(0,o.useState)(`all`),[R,ce]=(0,o.useState)(!1),[z,B]=(0,o.useState)(null),[V,H]=(0,o.useState)(``),[U,W]=(0,o.useState)(null),[G,K]=(0,o.useState)(``),[q,J]=(0,o.useState)(!1),le=V.trim(),Y=G.trim(),X=(0,o.useCallback)(async(e,t)=>{if(!y)return!1;A(!0),P(null);try{let n=await(0,l.listNotifications)({includeArchived:R,includeRead:!0,page:e,pageSize:ne,recipientMemberId:y});return C(e=>t?[...e,...n.notifications]:n.notifications),E(n.totalCount),O(e),await v(),!0}catch(e){return P(T(e)),!1}finally{A(!1)}},[y,R,v]);(0,o.useEffect)(()=>{g&&y&&(P(null),I(null),X(1,!1))},[g,y,X]);let ue=(0,o.useCallback)(e=>{let t=e.target.value;(t===`all`||t===`read`||t===`unread`)&&se(t)},[]),de=(0,o.useCallback)(async()=>{if(y&&!j){M(!0),P(null);try{await(0,l.markAllNotificationsRead)({recipientMemberId:y}),await X(1,!1)}catch(e){P(T(e))}finally{M(!1)}}},[j,y,X]),fe=(0,o.useCallback)(()=>{k||X(D+1,!0)},[k,X,D]),pe=(0,o.useCallback)(async e=>{if(y)try{await(0,l.markNotificationRead)({id:e,readerMemberId:y}),await X(1,!1)}catch(e){P(T(e))}},[y,X]),me=(0,o.useCallback)(async e=>{if(y){P(null),I(null);try{let t=ie(e);e.archivedAt?(await(0,l.unarchiveNotifications)({ids:[e.id]}),I(`已將「${t}」移出封存。`)):(await(0,l.archiveNotifications)({ids:[e.id]}),I(`已封存「${t}」。`)),await X(1,!1)}catch(e){P(T(e))}}},[y,X]),he=(0,o.useCallback)(e=>{e.id===x&&(I(null),ce(e=>!e))},[]),Z=(0,o.useCallback)(async t=>{if(t.instanceId&&y)try{t.status!==`READ`&&(await(0,l.markNotificationRead)({id:t.id,readerMemberId:y}),await v()),h(),e.push(r.caseDetail(t.instanceId))}catch(e){P(T(e))}},[h,y,v,e,r]),ge=(0,o.useCallback)(e=>{P(null),B(e),H(``)},[]),_e=(0,o.useCallback)(()=>{q||(B(null),H(``))},[q]),ve=(0,o.useCallback)(async()=>{let e=z;if(e?.taskId&&y&&!q){J(!0),P(null),I(null);try{if(await(0,l.decideTask)({action:`APPROVED`,comment:le||null,decidedByMemberId:y,taskId:e.taskId}),!await X(1,!1))return;B(null),H(``),I(`已同意「${e.title}」。`)}catch(e){P(T(e))}finally{J(!1)}}},[z,y,q,X,le]),ye=(0,o.useCallback)(e=>{W(e),K(``)},[]),be=(0,o.useCallback)(()=>{W(null),K(``)},[]),xe=(0,o.useCallback)(async()=>{let e=U;if(e?.taskId&&y&&Y&&!q){J(!0),P(null),I(null);try{await(0,l.decideTask)({action:`REJECTED`,comment:Y,decidedByMemberId:y,taskId:e.taskId}),W(null),K(``),I(`已拒絕「${e.title}」。`),await X(1,!1)}catch(e){P(T(e))}finally{J(!1)}}},[y,q,X,U,Y]),Se=(0,o.useCallback)((e,t)=>{let n=t.id;n===`approve`?ge(e):n===`reject`?ye(e):n===`view`?Z(e):n===`read`?pe(e.id):(n===`archive`||n===`unarchive`)&&me(e)},[me,pe,Z,ge,ye]),Q=(0,o.useCallback)((e,t)=>!(e instanceof Node)||!t.contains(e)?!1:!(e instanceof Element&&e.closest(`button`)),[]),Ce=(0,o.useCallback)((e,t)=>{Q(t.target,t.currentTarget)&&Z(e)},[Z,Q]),we=(0,o.useCallback)((e,t)=>{(t.key===`Enter`||t.key===` `)&&Q(t.target,t.currentTarget)&&(t.preventDefault(),Z(e))},[Z,Q]),Te=(0,o.useMemo)(()=>S.filter(e=>L===`all`?!0:L===`read`?e.status===`READ`:e.status!==`READ`),[L,S]),Ee=(0,o.useMemo)(()=>{let e=new Date,t=b.reduce((e,t)=>(e[t]=[],e),{earlier:[],past7Days:[],today:[],yesterday:[]});return Te.forEach(n=>{t[oe(n.createdAt,e)].push(n)}),b.map(e=>[e,t[e]]).filter(([,e])=>e.length>0)},[Te]),$=S.length<w;return y?(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(d.default,{bottomGhostActionDisabled:j||k,bottomGhostActionLoading:j,bottomGhostActionText:`全部標為已讀`,bottomOnGhostActionClick:()=>{de()},bottomOnPrimaryActionClick:()=>{fe()},bottomPrimaryActionDisabled:!$||k,bottomPrimaryActionLoading:k&&$,bottomPrimaryActionText:$?`載入更多`:`已顯示全部`,contentKey:`${L}:${R}:${S.length}`,filterAreaAllRadioLabel:`全部`,filterAreaOnRadioChange:ue,filterAreaOnSelect:he,filterAreaOptions:[{id:x,name:R?`隱藏已封存`:`顯示已封存`}],filterAreaReadRadioLabel:`已讀`,filterAreaShow:!0,filterAreaUnreadRadioLabel:`未讀`,filterAreaValue:L,headerTitle:`通知中心`,isBottomDisplay:!0,isHeaderDisplay:!0,onClose:h,open:g,size:`medium`,children:(0,c.jsxs)(`div`,{role:`list`,children:[N?(0,c.jsx)(`p`,{role:`alert`,style:{color:`var(--mzn-color-text-error, #d92d20)`,padding:`12px 16px`},children:N}):null,F?(0,c.jsx)(`p`,{role:`status`,style:{color:`var(--mzn-color-text-success, #079455)`,padding:`12px 16px`},children:F}):null,Ee.length===0?(0,c.jsx)(`p`,{style:{color:`var(--mzn-color-text-secondary, #6b7280)`,padding:`24px 16px`,textAlign:`center`},children:k?`載入中…`:`目前沒有通知`}):null,Ee.map(([e,t])=>(0,c.jsx)(o.Fragment,{children:t.map((t,n)=>{let r=t.instanceId!==null;return(0,c.jsx)(`div`,{onClick:r?e=>{Ce(t,e)}:void 0,onKeyDown:r?e=>{we(t,e)}:void 0,role:r?`button`:void 0,style:r?{cursor:`pointer`}:void 0,tabIndex:r?0:void 0,children:(0,c.jsx)(p.default,{appendTips:t.archivedAt?`已封存`:void 0,description:t.body,onBadgeSelect:e=>{Se(t,e)},options:[...re(t)],prependTips:n===0?te[e]:void 0,reference:t.id,severity:ae(t),showBadge:t.status!==`READ`,timeStamp:t.createdAt,title:ie(t),type:`drawer`})},t.id)})},e))]})}),(0,c.jsxs)(f.default,{cancelText:`取消`,confirmText:`送出同意`,loading:q,modalType:`standard`,onCancel:_e,onClose:_e,onConfirm:()=>{ve()},open:z!==null,showModalFooter:!0,showModalHeader:!0,size:`regular`,supportingText:`可以留下同意說明,內容會記錄在簽核歷程;不填也可以直接送出。`,title:`簽核意見`,children:[N?(0,c.jsx)(s.Typography,{color:`text-error`,role:`alert`,variant:`body`,children:N}):null,(0,c.jsx)(a.t,{label:`簽核意見`,name:`notificationApproveComment`,children:(0,c.jsx)(m.default,{autoFocus:!0,onChange:e=>{H(e.target.value)},placeholder:`選填,例如補充核准條件或提醒事項`,rows:4,value:V})})]}),(0,c.jsx)(f.default,{cancelText:`取消`,confirmButtonProps:{disabled:!Y,variant:`destructive-primary`},confirmText:`送出拒絕`,loading:q,modalStatusType:`error`,modalType:`standard`,onCancel:be,onClose:be,onConfirm:()=>{xe()},open:U!==null,showModalFooter:!0,showModalHeader:!0,size:`regular`,supportingText:`拒絕案件時必須留下原因,供發起人與後續追蹤查看。`,title:`拒絕原因`,children:(0,c.jsx)(m.default,{autoFocus:!0,onChange:e=>{K(e.target.value)},placeholder:`請輸入拒絕原因`,rows:4,value:G})})]}):null}function w(e){return e===`SLA_OVERDUE`?`error`:e===`SLA_WARNING`?`warning`:e===`INSTANCE_COMPLETED`?`success`:`info`}function oe(e,t){let n=new Date(e);if(Number.isNaN(n.getTime()))return`earlier`;let r=new Date(t.getFullYear(),t.getMonth(),t.getDate()),i=new Date(n.getFullYear(),n.getMonth(),n.getDate());if(i.getTime()===r.getTime())return`today`;let a=new Date(r);return a.setDate(a.getDate()-1),i.getTime()===a.getTime()?`yesterday`:(t.getTime()-n.getTime())/864e5<=7?`past7Days`:`earlier`}function T(e){return e instanceof Error?e.message:`發生未知錯誤`}function E(e){try{let t=JSON.parse(e.payloadJson);return O(t)&&typeof t.allowReject==`boolean`?t.allowReject:e.allowReject}catch{return e.allowReject}}function D(e){try{let t=JSON.parse(e.payloadJson);return O(t)&&t.hasFillableFields===!0}catch{return!1}}function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function k({children:e,locale:t=u.CalendarLocale.ZH_TW,language:i=r.r,publicPaths:a,loginPath:o}){return(0,c.jsx)(u.CalendarConfigProviderMoment,{locale:t,children:(0,c.jsx)(r.t,{language:i,children:(0,c.jsx)(n.t,{publicPaths:a,loginPath:o,children:(0,c.jsx)(y,{children:(0,c.jsxs)(g,{children:[e,(0,c.jsx)(C,{})]})})})})})}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return ee}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return k}});
|
|
2
|
+
//# sourceMappingURL=providers-chuJvMgv.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers-chuJvMgv.cjs","names":["createContext","useState","useCallback","useMemo","useContext","createContext","useAuth","useState","useCallback","readUnreadNotificationCount","useMemo","useContext","useRouterAdapter","useBPMRoutes","useAuth","useState","useCallback","listNotifications","markAllNotificationsRead","markNotificationRead","unarchiveNotifications","archiveNotifications","decideTask","useMemo","_Fragment","Drawer","Fragment","NotificationCenter","Modal","Typography","BPMFormField","Textarea","CalendarLocale","DEFAULT_BPM_LANGUAGE","CalendarConfigProviderMoment","BPMLanguageProvider","AuthProvider","require_rolldown_runtime","require_router_adapter","require_auth_provider","require_language_provider","require_routes_config","require_bpm_form_field","react","_mezzanine_ui_react","react_jsx_runtime","_rytass_bpm_core_client_workflow","_mezzanine_ui_react_moment","_mezzanine_ui_react_Drawer","_mezzanine_ui_react_Modal","_mezzanine_ui_react_NotificationCenter","_mezzanine_ui_react_Textarea","NotificationDrawerContext","NotificationDrawerProvider","isOpen","setIsOpen","open","close","toggle","current","value","useNotificationDrawer","NotificationUnreadContext","NotificationUnreadProvider","currentMemberId","member","unreadCount","setUnreadCount","refreshUnreadCount","next","active","useNotificationUnread","TIME_GROUP_ORDER","TIME_GROUP_LABEL","PAGE_SIZE","TOGGLE_ARCHIVED_OPTION_ID","buildNotificationOptions","canReject","readNotificationAllowReject","record","opensToFill","readNotificationHasFillableFields","RESOLVED_TITLE","resolveDisplayTitle","resolveSeverity","toSeverity","NotificationDrawer","router","routes","rows","setRows","totalCount","setTotalCount","page","setPage","loading","setLoading","bulkLoading","setBulkLoading","error","setError","notice","setNotice","filter","setFilter","includeArchived","setIncludeArchived","approveTarget","setApproveTarget","approveComment","setApproveComment","rejectTarget","setRejectTarget","rejectReason","setRejectReason","deciding","setDeciding","trimmedApproveComment","trimmedRejectReason","loadPage","result","nextPage","append","readErrorMessage","handleFilterChange","event","handleMarkAllRead","handleLoadMore","handleMarkRead","handleArchive","label","handleFilterAreaSelect","option","handleOpenInstance","openApproveModal","closeApproveModal","handleApproveConfirm","target","openRejectModal","closeRejectModal","handleRejectConfirm","handleBadgeSelect","action","isCardActivation","card","handleCardActivate","handleCardKeyDown","filteredRows","row","groupedRows","now","buckets","group","accumulator","resolveTimeGroup","items","hasMore","openable","itemIndex","type","notificationDate","nowStartOfDay","notificationStartOfDay","yesterdayStartOfDay","payload","isRecord","Providers","children"],"sources":["../../src/lib/notification-drawer-provider.tsx","../../src/lib/notification-unread-provider.tsx","../../src/components/notification-drawer.tsx","../../src/lib/providers.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\n\ninterface NotificationDrawerContextValue {\n readonly close: () => void;\n readonly isOpen: boolean;\n readonly open: () => void;\n readonly toggle: () => void;\n}\n\nconst NotificationDrawerContext =\n createContext<NotificationDrawerContextValue | null>(null);\n\ninterface NotificationDrawerProviderProps {\n readonly children: ReactNode;\n}\n\n/**\n * Controls the open/closed state of the BPM notification drawer. Wraps\n * children with a context that `<NotificationDrawer />` reads to mount /\n * hide itself, and that the host navigation reads (via\n * `<BPMNotificationBellButton />` or `useNotificationDrawer().open`) to\n * open the drawer when the bell icon is clicked.\n *\n * When used outside this provider, the returned hook is a safe no-op so\n * components don't crash in test or storybook environments.\n */\nexport function NotificationDrawerProvider({\n children,\n}: NotificationDrawerProviderProps): ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback((): void => {\n setIsOpen(true);\n }, []);\n const close = useCallback((): void => {\n setIsOpen(false);\n }, []);\n const toggle = useCallback((): void => {\n setIsOpen((current) => !current);\n }, []);\n\n const value = useMemo<NotificationDrawerContextValue>(\n () => ({ close, isOpen, open, toggle }),\n [close, isOpen, open, toggle],\n );\n\n return (\n <NotificationDrawerContext.Provider value={value}>\n {children}\n </NotificationDrawerContext.Provider>\n );\n}\n\n/**\n * Read the BPM notification drawer's open state and control helpers.\n * Returns a no-op stub when used outside `<NotificationDrawerProvider>`.\n */\nexport function useNotificationDrawer(): NotificationDrawerContextValue {\n const context = useContext(NotificationDrawerContext);\n if (!context) {\n return {\n close: (): void => undefined,\n isOpen: false,\n open: (): void => undefined,\n toggle: (): void => undefined,\n };\n }\n return context;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { readUnreadNotificationCount } from '@rytass/bpm-core-client/workflow';\nimport { useAuth } from './auth-provider';\n\ninterface NotificationUnreadContextValue {\n readonly refreshUnreadCount: () => Promise<number>;\n readonly unreadCount: number;\n}\n\nconst NotificationUnreadContext =\n createContext<NotificationUnreadContextValue | null>(null);\n\ninterface NotificationUnreadProviderProps {\n readonly children: ReactNode;\n}\n\n/**\n * Polls BPM for the current member's unread notification count via\n * `readUnreadNotificationCount` and exposes it through context for the\n * host navigation (`<BPMNotificationBellButton />` badge or any custom\n * trigger using `useNotificationUnread().unreadCount`) and the BPM\n * `<NotificationDrawer />` (header count). Refresh is triggered on\n * mount and whenever the auth member id changes; consumers can call\n * `refreshUnreadCount()` after acknowledging a notification.\n */\nexport function NotificationUnreadProvider({\n children,\n}: NotificationUnreadProviderProps): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [unreadCount, setUnreadCount] = useState(0);\n\n const refreshUnreadCount = useCallback(async (): Promise<number> => {\n if (!currentMemberId) {\n setUnreadCount(0);\n return 0;\n }\n const next = await readUnreadNotificationCount(currentMemberId);\n setUnreadCount(next);\n return next;\n }, [currentMemberId]);\n\n useEffect((): (() => void) => {\n let active = true;\n void (async () => {\n try {\n const next = await refreshUnreadCount();\n if (active) setUnreadCount(next);\n } catch {\n if (active) setUnreadCount(0);\n }\n })();\n return (): void => {\n active = false;\n };\n }, [refreshUnreadCount]);\n\n const value = useMemo<NotificationUnreadContextValue>(\n () => ({ refreshUnreadCount, unreadCount }),\n [refreshUnreadCount, unreadCount],\n );\n\n return (\n <NotificationUnreadContext.Provider value={value}>\n {children}\n </NotificationUnreadContext.Provider>\n );\n}\n\n/**\n * Read the current unread-notification count and a manual refresh helper.\n * Returns a zero/no-op stub when used outside\n * `<NotificationUnreadProvider>`.\n */\nexport function useNotificationUnread(): NotificationUnreadContextValue {\n const context = useContext(NotificationUnreadContext);\n if (!context) {\n return {\n refreshUnreadCount: async (): Promise<number> => 0,\n unreadCount: 0,\n };\n }\n return context;\n}\n","'use client';\n\nimport {\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ChangeEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n} from 'react';\nimport Drawer from '@mezzanine-ui/react/Drawer';\nimport Modal from '@mezzanine-ui/react/Modal';\nimport NotificationCenter from '@mezzanine-ui/react/NotificationCenter';\nimport Textarea from '@mezzanine-ui/react/Textarea';\nimport { Typography } from '@mezzanine-ui/react';\nimport type { NotificationSeverity } from '@mezzanine-ui/core/notification-center';\nimport type { DropdownOption } from '@mezzanine-ui/core/dropdown/dropdown';\nimport {\n archiveNotifications,\n decideTask,\n listNotifications,\n markAllNotificationsRead,\n markNotificationRead,\n unarchiveNotifications,\n type NotificationRecord,\n type NotificationResolution,\n type NotificationType,\n} from '@rytass/bpm-core-client/workflow';\nimport { useAuth } from '../lib/auth-provider';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { BPMFormField } from './bpm-form-field';\n\ntype FilterValue = 'all' | 'read' | 'unread';\n\ntype TimeGroup = 'today' | 'yesterday' | 'past7Days' | 'earlier';\n\nconst TIME_GROUP_ORDER: readonly TimeGroup[] = [\n 'today',\n 'yesterday',\n 'past7Days',\n 'earlier',\n];\n\nconst TIME_GROUP_LABEL: Readonly<Record<TimeGroup, string>> = {\n earlier: '更早',\n past7Days: '過去七天',\n today: '今天',\n yesterday: '昨天',\n};\n\nconst PAGE_SIZE = 50;\n\n/** Identifiers for the per-notification `...` dropdown menu entries. */\ntype NotificationAction =\n | 'approve'\n | 'archive'\n | 'reject'\n | 'unarchive'\n | 'view'\n | 'read';\n\n/** Identifier for the filter-bar dropdown entry toggling the archived view. */\nconst TOGGLE_ARCHIVED_OPTION_ID = 'toggleArchived';\n\n/**\n * Build the `...` dropdown options for one notification. Approve / reject are\n * offered only while the server still reports the notification as `actionable`\n * (an unresolved task assignment) — once the task is decided / cancelled the\n * backend flips `actionable` to false, so a stale \"同意\" can never appear.\n * A task that fills in form fields (`hasFillableFields`) cannot be approved\n * from here — the fill has to be entered on the case page — so its \"同意\"\n * becomes \"開啟案件\".\n * \"查看案件\" needs an `instanceId`; \"標為已讀\" only shows while unread.\n * Archiving is independent of read state, so the entry flips between\n * \"封存\" / \"取消封存\" for every notification.\n */\nfunction buildNotificationOptions(\n record: NotificationRecord,\n): readonly DropdownOption[] {\n const canReject = readNotificationAllowReject(record) !== false;\n const opensToFill =\n record.actionable &&\n Boolean(record.instanceId) &&\n readNotificationHasFillableFields(record);\n\n return [\n ...(record.actionable\n ? ([\n opensToFill\n ? { id: 'view', name: '開啟案件' }\n : { id: 'approve', name: '同意' },\n ...(canReject ? [{ id: 'reject', name: '拒絕' }] : []),\n ] satisfies DropdownOption[])\n : []),\n ...(record.instanceId && !opensToFill\n ? ([{ id: 'view', name: '查看案件' }] satisfies DropdownOption[])\n : []),\n ...(record.status !== 'READ'\n ? ([{ id: 'read', name: '標為已讀' }] satisfies DropdownOption[])\n : []),\n record.archivedAt\n ? { id: 'unarchive', name: '取消封存' }\n : { id: 'archive', name: '封存' },\n ];\n}\n\n/**\n * Title shown for a resolved task-assignment notification, replacing the\n * stored \"新的待簽任務\" wording so a decided card no longer reads as pending.\n * The stored `body` is kept as historical context (which case / node).\n */\nconst RESOLVED_TITLE: Readonly<Record<NotificationResolution, string>> = {\n APPROVED: '簽核任務已同意',\n REJECTED: '簽核任務已拒絕',\n RETURNED: '簽核任務已退回',\n SUPERSEDED: '簽核任務已結束',\n TRANSFERRED: '簽核任務已轉派',\n};\n\nfunction resolveDisplayTitle(record: NotificationRecord): string {\n return record.resolution ? RESOLVED_TITLE[record.resolution] : record.title;\n}\n\n/**\n * Severity (icon colour) for a notification. Once resolved, it reflects the\n * outcome — green for approved, red for rejected, neutral otherwise — instead\n * of the original by-type colour.\n */\nfunction resolveSeverity(record: NotificationRecord): NotificationSeverity {\n if (record.resolution === 'APPROVED') return 'success';\n if (record.resolution === 'REJECTED') return 'error';\n if (record.resolution) return 'info';\n return toSeverity(record.type);\n}\n\n/**\n * Right-side notification drawer mounted at the root by `<Providers>`.\n * Opens / closes via `useNotificationDrawer()`, polls\n * `listNotifications()` for the current member, supports filter\n * (`all` / `read` / `unread`), per-row mark-read, per-row archive / unarchive,\n * bulk mark-all-read, and load-more pagination. Archived notifications are\n * hidden until the filter-bar menu turns \"顯示已封存\" on. Clicking a row with an\n * `instanceId` navigates to `/instances/<id>` via the host's router adapter.\n */\nexport function NotificationDrawer(): ReactElement | null {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const { close, isOpen } = useNotificationDrawer();\n const { refreshUnreadCount } = useNotificationUnread();\n const currentMemberId = member?.memberId ?? null;\n const [rows, setRows] = useState<readonly NotificationRecord[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [page, setPage] = useState(1);\n const [loading, setLoading] = useState(false);\n const [bulkLoading, setBulkLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const [filter, setFilter] = useState<FilterValue>('all');\n const [includeArchived, setIncludeArchived] = useState(false);\n const [approveTarget, setApproveTarget] = useState<NotificationRecord | null>(\n null,\n );\n const [approveComment, setApproveComment] = useState('');\n const [rejectTarget, setRejectTarget] = useState<NotificationRecord | null>(\n null,\n );\n const [rejectReason, setRejectReason] = useState('');\n const [deciding, setDeciding] = useState(false);\n\n const trimmedApproveComment = approveComment.trim();\n const trimmedRejectReason = rejectReason.trim();\n\n const loadPage = useCallback(\n async (nextPage: number, append: boolean): Promise<boolean> => {\n if (!currentMemberId) return false;\n setLoading(true);\n setError(null);\n try {\n const result = await listNotifications({\n includeArchived,\n includeRead: true,\n page: nextPage,\n pageSize: PAGE_SIZE,\n recipientMemberId: currentMemberId,\n });\n setRows((current): readonly NotificationRecord[] =>\n append ? [...current, ...result.notifications] : result.notifications,\n );\n setTotalCount(result.totalCount);\n setPage(nextPage);\n await refreshUnreadCount();\n return true;\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n return false;\n } finally {\n setLoading(false);\n }\n },\n [currentMemberId, includeArchived, refreshUnreadCount],\n );\n\n useEffect((): void => {\n if (!isOpen || !currentMemberId) return;\n setError(null);\n setNotice(null);\n void loadPage(1, false);\n }, [isOpen, currentMemberId, loadPage]);\n\n const handleFilterChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>): void => {\n const next = event.target.value;\n if (next === 'all' || next === 'read' || next === 'unread') setFilter(next);\n },\n [],\n );\n\n const handleMarkAllRead = useCallback(async (): Promise<void> => {\n if (!currentMemberId || bulkLoading) return;\n setBulkLoading(true);\n setError(null);\n try {\n await markAllNotificationsRead({ recipientMemberId: currentMemberId });\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setBulkLoading(false);\n }\n }, [bulkLoading, currentMemberId, loadPage]);\n\n const handleLoadMore = useCallback((): void => {\n if (loading) return;\n void loadPage(page + 1, true);\n }, [loading, loadPage, page]);\n\n const handleMarkRead = useCallback(\n async (id: string): Promise<void> => {\n if (!currentMemberId) return;\n try {\n await markNotificationRead({ id, readerMemberId: currentMemberId });\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [currentMemberId, loadPage],\n );\n\n const handleArchive = useCallback(\n async (record: NotificationRecord): Promise<void> => {\n if (!currentMemberId) return;\n setError(null);\n setNotice(null);\n try {\n // Name the notification the way the row does — `title` is the raw\n // stored title, which can differ from the rendered one.\n const label = resolveDisplayTitle(record);\n\n if (record.archivedAt) {\n await unarchiveNotifications({ ids: [record.id] });\n setNotice(`已將「${label}」移出封存。`);\n } else {\n await archiveNotifications({ ids: [record.id] });\n setNotice(`已封存「${label}」。`);\n }\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [currentMemberId, loadPage],\n );\n\n const handleFilterAreaSelect = useCallback((option: DropdownOption): void => {\n if (option.id !== TOGGLE_ARCHIVED_OPTION_ID) return;\n setNotice(null);\n setIncludeArchived((current): boolean => !current);\n }, []);\n\n const handleOpenInstance = useCallback(\n async (record: NotificationRecord): Promise<void> => {\n if (!record.instanceId || !currentMemberId) return;\n try {\n if (record.status !== 'READ') {\n await markNotificationRead({\n id: record.id,\n readerMemberId: currentMemberId,\n });\n await refreshUnreadCount();\n }\n close();\n router.push(routes.caseDetail(record.instanceId));\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [close, currentMemberId, refreshUnreadCount, router, routes],\n );\n\n const openApproveModal = useCallback((record: NotificationRecord): void => {\n setError(null);\n setApproveTarget(record);\n setApproveComment('');\n }, []);\n\n const closeApproveModal = useCallback((): void => {\n if (deciding) return;\n setApproveTarget(null);\n setApproveComment('');\n }, [deciding]);\n\n const handleApproveConfirm = useCallback(async (): Promise<void> => {\n const target = approveTarget;\n if (!target?.taskId || !currentMemberId || deciding) return;\n setDeciding(true);\n setError(null);\n setNotice(null);\n try {\n await decideTask({\n action: 'APPROVED',\n comment: trimmedApproveComment || null,\n decidedByMemberId: currentMemberId,\n taskId: target.taskId,\n });\n const refreshed = await loadPage(1, false);\n if (!refreshed) return;\n setApproveTarget(null);\n setApproveComment('');\n setNotice(`已同意「${target.title}」。`);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setDeciding(false);\n }\n }, [\n approveTarget,\n currentMemberId,\n deciding,\n loadPage,\n trimmedApproveComment,\n ]);\n\n const openRejectModal = useCallback((record: NotificationRecord): void => {\n setRejectTarget(record);\n setRejectReason('');\n }, []);\n\n const closeRejectModal = useCallback((): void => {\n setRejectTarget(null);\n setRejectReason('');\n }, []);\n\n const handleRejectConfirm = useCallback(async (): Promise<void> => {\n const target = rejectTarget;\n if (!target?.taskId || !currentMemberId || !trimmedRejectReason || deciding)\n return;\n setDeciding(true);\n setError(null);\n setNotice(null);\n try {\n await decideTask({\n action: 'REJECTED',\n comment: trimmedRejectReason,\n decidedByMemberId: currentMemberId,\n taskId: target.taskId,\n });\n setRejectTarget(null);\n setRejectReason('');\n setNotice(`已拒絕「${target.title}」。`);\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setDeciding(false);\n }\n }, [currentMemberId, deciding, loadPage, rejectTarget, trimmedRejectReason]);\n\n const handleBadgeSelect = useCallback(\n (record: NotificationRecord, option: DropdownOption): void => {\n const action = option.id as NotificationAction;\n if (action === 'approve') openApproveModal(record);\n else if (action === 'reject') openRejectModal(record);\n else if (action === 'view') void handleOpenInstance(record);\n else if (action === 'read') void handleMarkRead(record.id);\n else if (action === 'archive' || action === 'unarchive')\n void handleArchive(record);\n },\n [\n handleArchive,\n handleMarkRead,\n handleOpenInstance,\n openApproveModal,\n openRejectModal,\n ],\n );\n\n /**\n * Whether an event that reached the card wrapper actually happened inside the\n * card, and not on one of its action controls.\n *\n * Two escape hatches are needed. The `...` menu button (and its `<svg>` icon —\n * hence the `Element` check, SVG nodes are not HTMLElements) sits inside the\n * card and must open the dropdown rather than navigate. The dropdown itself\n * renders into a portal, and React replays events along the component tree\n * rather than the DOM tree, so picking \"封存\" also reaches this handler even\n * though the `<li role=\"option\">` lives outside the card's DOM — and it is not\n * a `<button>`, so the first guard alone misses it.\n */\n const isCardActivation = useCallback(\n (target: EventTarget | null, card: HTMLDivElement): boolean => {\n if (!(target instanceof Node) || !card.contains(target)) return false;\n\n return !(target instanceof Element && target.closest('button'));\n },\n [],\n );\n\n const handleCardActivate = useCallback(\n (record: NotificationRecord, event: ReactMouseEvent<HTMLDivElement>): void => {\n if (!isCardActivation(event.target, event.currentTarget)) return;\n void handleOpenInstance(record);\n },\n [handleOpenInstance, isCardActivation],\n );\n\n const handleCardKeyDown = useCallback(\n (\n record: NotificationRecord,\n event: ReactKeyboardEvent<HTMLDivElement>,\n ): void => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n if (!isCardActivation(event.target, event.currentTarget)) return;\n event.preventDefault();\n void handleOpenInstance(record);\n },\n [handleOpenInstance, isCardActivation],\n );\n\n const filteredRows = useMemo(\n (): readonly NotificationRecord[] =>\n rows.filter((row): boolean => {\n if (filter === 'all') return true;\n if (filter === 'read') return row.status === 'READ';\n return row.status !== 'READ';\n }),\n [filter, rows],\n );\n\n const groupedRows = useMemo(\n (): ReadonlyArray<readonly [TimeGroup, readonly NotificationRecord[]]> => {\n const now = new Date();\n const buckets = TIME_GROUP_ORDER.reduce<\n Record<TimeGroup, NotificationRecord[]>\n >(\n (accumulator, group) => {\n accumulator[group] = [];\n return accumulator;\n },\n { earlier: [], past7Days: [], today: [], yesterday: [] },\n );\n filteredRows.forEach((row): void => {\n buckets[resolveTimeGroup(row.createdAt, now)].push(row);\n });\n return TIME_GROUP_ORDER.map(\n (group) => [group, buckets[group]] as const,\n ).filter(([, items]) => items.length > 0);\n },\n [filteredRows],\n );\n\n const hasMore = rows.length < totalCount;\n\n if (!currentMemberId) return null;\n\n return (\n <>\n <Drawer\n bottomGhostActionDisabled={bulkLoading || loading}\n bottomGhostActionLoading={bulkLoading}\n bottomGhostActionText=\"全部標為已讀\"\n bottomOnGhostActionClick={(): void => {\n void handleMarkAllRead();\n }}\n bottomOnPrimaryActionClick={(): void => {\n handleLoadMore();\n }}\n bottomPrimaryActionDisabled={!hasMore || loading}\n bottomPrimaryActionLoading={loading && hasMore}\n bottomPrimaryActionText={hasMore ? '載入更多' : '已顯示全部'}\n contentKey={`${filter}:${includeArchived}:${rows.length}`}\n filterAreaAllRadioLabel=\"全部\"\n filterAreaOnRadioChange={handleFilterChange}\n filterAreaOnSelect={handleFilterAreaSelect}\n filterAreaOptions={[\n {\n id: TOGGLE_ARCHIVED_OPTION_ID,\n name: includeArchived ? '隱藏已封存' : '顯示已封存',\n },\n ]}\n filterAreaReadRadioLabel=\"已讀\"\n filterAreaShow\n filterAreaUnreadRadioLabel=\"未讀\"\n filterAreaValue={filter}\n headerTitle=\"通知中心\"\n isBottomDisplay\n isHeaderDisplay\n onClose={close}\n open={isOpen}\n size=\"medium\"\n >\n <div role=\"list\">\n {error ? (\n <p\n role=\"alert\"\n style={{\n color: 'var(--mzn-color-text-error, #d92d20)',\n padding: '12px 16px',\n }}\n >\n {error}\n </p>\n ) : null}\n {notice ? (\n <p\n role=\"status\"\n style={{\n color: 'var(--mzn-color-text-success, #079455)',\n padding: '12px 16px',\n }}\n >\n {notice}\n </p>\n ) : null}\n {groupedRows.length === 0 ? (\n <p\n style={{\n color: 'var(--mzn-color-text-secondary, #6b7280)',\n padding: '24px 16px',\n textAlign: 'center',\n }}\n >\n {loading ? '載入中…' : '目前沒有通知'}\n </p>\n ) : null}\n {groupedRows.map(([group, items]) => (\n <Fragment key={group}>\n {items.map((record, itemIndex) => {\n const openable = record.instanceId !== null;\n\n return (\n <div\n key={record.id}\n onClick={\n openable\n ? (event: ReactMouseEvent<HTMLDivElement>): void => {\n handleCardActivate(record, event);\n }\n : undefined\n }\n onKeyDown={\n openable\n ? (event: ReactKeyboardEvent<HTMLDivElement>): void => {\n handleCardKeyDown(record, event);\n }\n : undefined\n }\n role={openable ? 'button' : undefined}\n style={openable ? { cursor: 'pointer' } : undefined}\n tabIndex={openable ? 0 : undefined}\n >\n <NotificationCenter\n appendTips={record.archivedAt ? '已封存' : undefined}\n description={record.body}\n onBadgeSelect={(option: DropdownOption): void => {\n handleBadgeSelect(record, option);\n }}\n options={[...buildNotificationOptions(record)]}\n prependTips={\n itemIndex === 0 ? TIME_GROUP_LABEL[group] : undefined\n }\n reference={record.id}\n severity={resolveSeverity(record)}\n showBadge={record.status !== 'READ'}\n timeStamp={record.createdAt}\n title={resolveDisplayTitle(record)}\n type=\"drawer\"\n />\n </div>\n );\n })}\n </Fragment>\n ))}\n </div>\n </Drawer>\n <Modal\n cancelText=\"取消\"\n confirmText=\"送出同意\"\n loading={deciding}\n modalType=\"standard\"\n onCancel={closeApproveModal}\n onClose={closeApproveModal}\n onConfirm={(): void => {\n void handleApproveConfirm();\n }}\n open={approveTarget !== null}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"可以留下同意說明,內容會記錄在簽核歷程;不填也可以直接送出。\"\n title=\"簽核意見\"\n >\n {error ? (\n <Typography color=\"text-error\" role=\"alert\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <BPMFormField label=\"簽核意見\" name=\"notificationApproveComment\">\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setApproveComment(event.target.value);\n }}\n placeholder=\"選填,例如補充核准條件或提醒事項\"\n rows={4}\n value={approveComment}\n />\n </BPMFormField>\n </Modal>\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{\n disabled: !trimmedRejectReason,\n variant: 'destructive-primary',\n }}\n confirmText=\"送出拒絕\"\n loading={deciding}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={closeRejectModal}\n onClose={closeRejectModal}\n onConfirm={(): void => {\n void handleRejectConfirm();\n }}\n open={rejectTarget !== null}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"拒絕案件時必須留下原因,供發起人與後續追蹤查看。\"\n title=\"拒絕原因\"\n >\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setRejectReason(event.target.value);\n }}\n placeholder=\"請輸入拒絕原因\"\n rows={4}\n value={rejectReason}\n />\n </Modal>\n </>\n );\n}\n\nfunction toSeverity(type: NotificationType): NotificationSeverity {\n if (type === 'SLA_OVERDUE') return 'error';\n if (type === 'SLA_WARNING') return 'warning';\n if (type === 'INSTANCE_COMPLETED') return 'success';\n return 'info';\n}\n\nfunction resolveTimeGroup(value: string, now: Date): TimeGroup {\n const notificationDate = new Date(value);\n if (Number.isNaN(notificationDate.getTime())) return 'earlier';\n const nowStartOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n const notificationStartOfDay = new Date(\n notificationDate.getFullYear(),\n notificationDate.getMonth(),\n notificationDate.getDate(),\n );\n if (notificationStartOfDay.getTime() === nowStartOfDay.getTime()) return 'today';\n const yesterdayStartOfDay = new Date(nowStartOfDay);\n yesterdayStartOfDay.setDate(yesterdayStartOfDay.getDate() - 1);\n if (notificationStartOfDay.getTime() === yesterdayStartOfDay.getTime())\n return 'yesterday';\n const diffInDays =\n (now.getTime() - notificationDate.getTime()) / (1000 * 60 * 60 * 24);\n if (diffInDays <= 7) return 'past7Days';\n return 'earlier';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n\nfunction readNotificationAllowReject(\n record: NotificationRecord,\n): boolean | null {\n try {\n const payload: unknown = JSON.parse(record.payloadJson);\n\n if (isRecord(payload) && typeof payload.allowReject === 'boolean') {\n return payload.allowReject;\n }\n\n return record.allowReject;\n } catch {\n return record.allowReject;\n }\n}\n\n/**\n * `TASK_ASSIGNED` / `TASK_TRANSFERRED` payloads carry `hasFillableFields`;\n * older notifications do not, which reads as `false`.\n */\nfunction readNotificationHasFillableFields(\n record: NotificationRecord,\n): boolean {\n try {\n const payload: unknown = JSON.parse(record.payloadJson);\n\n return isRecord(payload) && payload.hasFillableFields === true;\n } catch {\n return false;\n }\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","'use client';\n\nimport type { ReactElement, ReactNode } from 'react';\nimport {\n CalendarConfigProviderMoment,\n CalendarLocale,\n} from '@mezzanine-ui/react/moment';\nimport { AuthProvider } from './auth-provider';\nimport {\n BPMLanguageProvider,\n DEFAULT_BPM_LANGUAGE,\n type BPMLanguage,\n} from './language-provider';\nimport { NotificationDrawer } from '../components/notification-drawer';\nimport { NotificationDrawerProvider } from './notification-drawer-provider';\nimport { NotificationUnreadProvider } from './notification-unread-provider';\n\ninterface ProvidersProps {\n readonly children: ReactNode;\n /** Override Mezzanine calendar locale. Defaults to `CalendarLocale.ZH_TW`. */\n readonly locale?: CalendarLocale;\n /**\n * Language BPM views write their messages in, such as publish-lint errors.\n * Defaults to `'zh-TW'`. Pass the user's choice as state to let them switch.\n * Independent of `locale`, which only formats calendar dates.\n */\n readonly language?: BPMLanguage;\n /**\n * Public paths that should not trigger redirect to `/login` when there\n * is no session. Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n */\n readonly publicPaths?: readonly string[];\n /** Where to send unauthenticated users. Defaults to `'/login'`. */\n readonly loginPath?: string;\n}\n\n/**\n * One-stop BPM admin provider stack. Wires:\n *\n * - Mezzanine UI calendar locale (moment-based, `ZH_TW` by default)\n * - `<BPMLanguageProvider>` (message language, `'zh-TW'` by default)\n * - `<AuthProvider>` (BPM session via REST `/auth/*`)\n * - `<NotificationUnreadProvider>` (polls unread count)\n * - `<NotificationDrawerProvider>` (controls drawer open/close state)\n * - `<NotificationDrawer />` mounted at the root so the host navigation\n * bell (`<BPMNotificationBellButton />` or any custom trigger calling\n * `useNotificationDrawer().open`) can open it.\n *\n * Consumer hosts wrap this **inside** a `<RouterAdapterProvider>` (provided\n * by the `pages/*` subpath shims when consuming via Next.js, or wired by\n * hand for other frameworks).\n */\nexport function Providers({\n children,\n locale = CalendarLocale.ZH_TW,\n language = DEFAULT_BPM_LANGUAGE,\n publicPaths,\n loginPath,\n}: ProvidersProps): ReactElement {\n return (\n <CalendarConfigProviderMoment locale={locale}>\n <BPMLanguageProvider language={language}>\n <AuthProvider publicPaths={publicPaths} loginPath={loginPath}>\n <NotificationUnreadProvider>\n <NotificationDrawerProvider>\n {children}\n <NotificationDrawer />\n </NotificationDrawerProvider>\n </NotificationUnreadProvider>\n </AuthProvider>\n </BPMLanguageProvider>\n </CalendarConfigProviderMoment>\n );\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});require('./index.css');const e=require("./chunks/router-adapter--gYs13E8.cjs"),t=require("./chunks/auth-provider-4BeCw7cI.cjs"),n=require("./chunks/format-date-time-D-cx5M8z.cjs"),r=require("./chunks/language-provider-Bdnx3Gxl.cjs"),i=require("./chunks/providers-
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});require('./index.css');const e=require("./chunks/router-adapter--gYs13E8.cjs"),t=require("./chunks/auth-provider-4BeCw7cI.cjs"),n=require("./chunks/format-date-time-D-cx5M8z.cjs"),r=require("./chunks/language-provider-Bdnx3Gxl.cjs"),i=require("./chunks/providers-chuJvMgv.cjs"),a=require("./chunks/routes-config-C1j_vFKo.cjs"),o=require("./chunks/bpm-form-field-Bc6k4ZEO.cjs"),s=require("./chunks/use-bpm-member-DpNAQtNC.cjs"),c=require("./chunks/admin-pickers-BRHk3qKq.cjs"),l=require("./chunks/approval-instance-list-page-ax0pm8bt.cjs"),u=require("./chunks/dashboard-page-BWQBSGDq.cjs");let d=require("@mezzanine-ui/react"),f=require("react/jsx-runtime"),p=require("@mezzanine-ui/icons");function m(){let{logout:e}=t.n();return e}var h={root:`bpm_root_jl5S-`,badge:`bpm_badge_SIKJk`};function g({label:e=`通知中心`}={}){let{open:t}=i.o(),{unreadCount:n}=i.i(),r=n>0?`${e},${n} 則未讀`:e;return(0,f.jsxs)(`span`,{className:h.root,children:[(0,f.jsx)(d.NavigationIconButton,{"aria-label":r,icon:p.NotificationUnreadIcon,onClick:()=>{t()},title:e,type:`button`}),n>0?(0,f.jsx)(`span`,{className:h.badge,children:n>99?`99+`:n}):null]})}exports.ApprovalInstanceListPage=l.t,exports.AuthProvider=t.t,exports.BPMFormField=o.t,exports.BPMLanguageProvider=r.t,exports.BPMNotificationBellButton=g,exports.BPMRoutesProvider=a.t,exports.BPM_LANGUAGES=r.n,exports.DEFAULT_BPM_LANGUAGE=r.r,exports.DashboardPage=u.t,exports.MemberPicker=c.t,exports.NotificationDrawer=i.n,exports.NotificationDrawerProvider=i.a,exports.NotificationUnreadProvider=i.r,exports.OrgUnitPicker=c.n,exports.PositionPicker=c.r,exports.Providers=i.t,exports.RouterAdapterProvider=e.t,exports.createDefaultBPMRoutes=a.n,exports.defaultBrowserSearchParams=e.n,exports.formatDateTime=n.t,exports.isBPMAdminMember=s.t,exports.readMemberOption=c.i,exports.readOrgUnitOption=c.a,exports.readPositionOption=c.o,exports.useAuth=t.n,exports.useBPMLanguage=r.i,exports.useBPMLogout=m,exports.useBPMMember=s.n,exports.useBPMRoutes=a.r,exports.useNotificationDrawer=i.o,exports.useNotificationUnread=i.i,exports.useRouterAdapter=e.r;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { n as e, r as t, t as n } from "./chunks/router-adapter-DftlFTOd.js";
|
|
|
3
3
|
import { n as r, t as i } from "./chunks/auth-provider-B5oPmvk2.js";
|
|
4
4
|
import { t as a } from "./chunks/format-date-time-CB-LxzqT.js";
|
|
5
5
|
import { i as o, n as s, r as c, t as l } from "./chunks/language-provider-B3cEbLe4.js";
|
|
6
|
-
import { a as u, i as d, n as f, o as p, r as m, t as h } from "./chunks/providers-
|
|
6
|
+
import { a as u, i as d, n as f, o as p, r as m, t as h } from "./chunks/providers-B_d5tbCL.js";
|
|
7
7
|
import { n as g, r as _, t as v } from "./chunks/routes-config-yZbESPlw.js";
|
|
8
8
|
import { t as y } from "./chunks/bpm-form-field-Cao0rMol.js";
|
|
9
9
|
import { n as b, t as x } from "./chunks/use-bpm-member-D57LKe_u.js";
|
package/dist/next/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../chunks/router-adapter--gYs13E8.cjs"),t=require("../chunks/providers-
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../chunks/router-adapter--gYs13E8.cjs"),t=require("../chunks/providers-chuJvMgv.cjs"),n=require("../chunks/routes-config-C1j_vFKo.cjs");let r=require("react"),i=require("react/jsx-runtime"),a=require("next/navigation");function o({children:n,locale:o,language:s,publicPaths:c,loginPath:l}){let u=(0,a.useRouter)(),d=(0,a.usePathname)(),f=(0,r.useMemo)(()=>({pathname:d,push:e=>u.push(e),replace:e=>u.replace(e),back:()=>u.back(),searchParams:()=>e.n()}),[u,d]);return(0,i.jsx)(e.t,{value:f,children:(0,i.jsx)(t.t,{locale:o,language:s,publicPaths:c,loginPath:l,children:n})})}exports.BPMNextProviders=o,exports.BPMRoutesProvider=n.t,exports.createDefaultBPMRoutes=n.n,exports.useBPMRoutes=n.r;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/next/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { n as e, t } from "../chunks/router-adapter-DftlFTOd.js";
|
|
3
|
-
import { t as n } from "../chunks/providers-
|
|
3
|
+
import { t as n } from "../chunks/providers-B_d5tbCL.js";
|
|
4
4
|
import { n as r, r as i, t as a } from "../chunks/routes-config-yZbESPlw.js";
|
|
5
5
|
import { useMemo as o } from "react";
|
|
6
6
|
import { jsx as s } from "react/jsx-runtime";
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require
|
|
|
12
12
|
7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。
|
|
13
13
|
8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。
|
|
14
14
|
9. 全程使用繁體中文(台灣用語)回覆。
|
|
15
|
+
10. 欄位在各關的顯示與補填用 set_field_access_matrix 設定:nodeIds 只列這次要改的簽核節點,它們的設定會整個換掉,呼叫前先從 get_workflow_snapshot 讀出這些節點既有的「欄位設定」一併傳入。「隱藏」只影響畫面、不是權限,不能用來保密;使用者要求保密時要說明這一點。節點含舊欄位權限設定時,用 remove_legacy_field_permissions 移除(不會自動轉換)。
|
|
15
16
|
|
|
16
17
|
嚴格界線:
|
|
17
18
|
- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-chat-route.cjs","names":["openai","WORKFLOW_TOOLSET","tool","jsonSchema","streamText","convertToModelMessages","ai","_ai_sdk_openai","_rytass_bpm_core_shared","DEFAULT_MODEL","resolveModel","setting","WORKFLOW_CHAT_SYSTEM_PROMPT","buildWorkflowAiSdkTools","entries","workflowTool","createWorkflowChatPOST","tools","body","request","options"],"sources":["../../src/next/workflow-chat-route.ts"],"sourcesContent":["import {\n convertToModelMessages,\n jsonSchema,\n streamText,\n tool,\n type LanguageModel,\n type Tool,\n type ToolSet,\n type UIMessage,\n} from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { WORKFLOW_TOOLSET } from '@rytass/bpm-core-shared';\n\n/**\n * Next.js route handler for the workflow designer LLM assistant.\n *\n * The model is advertised the workflow {@link WORKFLOW_TOOLSET} but the tools\n * carry **no `execute`** — so every tool call is forwarded to the browser,\n * where `useChat`'s `onToolCall` runs it against the React designer controller\n * (see `use-workflow-chat.ts`). This route only holds the API key, runs the\n * model, and streams the tool-call plan back. It never touches the canvas.\n *\n * Host usage (apps/client/src/app/api/chat/route.ts):\n * export const runtime = 'nodejs';\n * export const POST = createWorkflowChatPOST();\n */\n\n/** Default OpenAI model id (overridable via BPM_LLM_MODEL). */\nconst DEFAULT_MODEL = 'gpt-5.4-mini';\n\n/**\n * Resolve the model via the OpenAI provider directly (reads `OPENAI_API_KEY`).\n * Accepts a bare id (`gpt-5.4-mini`) or a `openai/`-prefixed id; the prefix is\n * stripped. This deployment talks to OpenAI directly — no AI Gateway.\n */\nfunction resolveModel(setting: string): LanguageModel {\n return openai(setting.replace(/^openai\\//u, ''));\n}\n\n/**\n * Strict guardrail: the assistant may ONLY design this approval flow, using the\n * provided tools. Anything else is declined. Lives server-side so it cannot be\n * overridden by client-supplied messages.\n */\nexport const WORKFLOW_CHAT_SYSTEM_PROMPT = `你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。`;\n\n/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */\nexport function buildWorkflowAiSdkTools(): ToolSet {\n const entries: readonly (readonly [string, Tool])[] = WORKFLOW_TOOLSET.map(\n (workflowTool) =>\n [\n workflowTool.name,\n tool({\n description: workflowTool.description,\n inputSchema: jsonSchema(\n workflowTool.inputSchema as Parameters<typeof jsonSchema>[0],\n ),\n }),\n ] as const,\n );\n\n return Object.fromEntries(entries) as ToolSet;\n}\n\nexport interface WorkflowChatRouteOptions {\n /** Override the OpenAI model id (else BPM_LLM_MODEL env, else the default). */\n readonly model?: string;\n /** Override the system prompt (else the strict design-only guardrail). */\n readonly system?: string;\n}\n\ninterface WorkflowChatRequestBody {\n readonly messages: readonly UIMessage[];\n}\n\n/** Create the `POST` handler for the workflow chat route. */\nexport function createWorkflowChatPOST(\n options: WorkflowChatRouteOptions = {},\n): (request: Request) => Promise<Response> {\n const tools = buildWorkflowAiSdkTools();\n\n return async function POST(request: Request): Promise<Response> {\n const body = (await request.json()) as WorkflowChatRequestBody;\n const result = streamText({\n messages: await convertToModelMessages([...body.messages]),\n model: resolveModel(\n options.model ?? process.env.BPM_LLM_MODEL ?? DEFAULT_MODEL,\n ),\n system: options.system ?? WORKFLOW_CHAT_SYSTEM_PROMPT,\n tools,\n });\n\n return result.toUIMessageStreamResponse();\n };\n}\n"],"mappings":"wJA4BA,IAAMS,EAAgB,eAOtB,SAASC,EAAa,EAAgC,CACpD,OAAA,EAAOV,EAAAA,OAAAA,CAAOW,EAAQ,QAAQ,aAAc,EAAE,CAAC,CACjD,CAOA,IAAaC,EAA8B
|
|
1
|
+
{"version":3,"file":"workflow-chat-route.cjs","names":["openai","WORKFLOW_TOOLSET","tool","jsonSchema","streamText","convertToModelMessages","ai","_ai_sdk_openai","_rytass_bpm_core_shared","DEFAULT_MODEL","resolveModel","setting","WORKFLOW_CHAT_SYSTEM_PROMPT","buildWorkflowAiSdkTools","entries","workflowTool","createWorkflowChatPOST","tools","body","request","options"],"sources":["../../src/next/workflow-chat-route.ts"],"sourcesContent":["import {\n convertToModelMessages,\n jsonSchema,\n streamText,\n tool,\n type LanguageModel,\n type Tool,\n type ToolSet,\n type UIMessage,\n} from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { WORKFLOW_TOOLSET } from '@rytass/bpm-core-shared';\n\n/**\n * Next.js route handler for the workflow designer LLM assistant.\n *\n * The model is advertised the workflow {@link WORKFLOW_TOOLSET} but the tools\n * carry **no `execute`** — so every tool call is forwarded to the browser,\n * where `useChat`'s `onToolCall` runs it against the React designer controller\n * (see `use-workflow-chat.ts`). This route only holds the API key, runs the\n * model, and streams the tool-call plan back. It never touches the canvas.\n *\n * Host usage (apps/client/src/app/api/chat/route.ts):\n * export const runtime = 'nodejs';\n * export const POST = createWorkflowChatPOST();\n */\n\n/** Default OpenAI model id (overridable via BPM_LLM_MODEL). */\nconst DEFAULT_MODEL = 'gpt-5.4-mini';\n\n/**\n * Resolve the model via the OpenAI provider directly (reads `OPENAI_API_KEY`).\n * Accepts a bare id (`gpt-5.4-mini`) or a `openai/`-prefixed id; the prefix is\n * stripped. This deployment talks to OpenAI directly — no AI Gateway.\n */\nfunction resolveModel(setting: string): LanguageModel {\n return openai(setting.replace(/^openai\\//u, ''));\n}\n\n/**\n * Strict guardrail: the assistant may ONLY design this approval flow, using the\n * provided tools. Anything else is declined. Lives server-side so it cannot be\n * overridden by client-supplied messages.\n */\nexport const WORKFLOW_CHAT_SYSTEM_PROMPT = `你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n10. 欄位在各關的顯示與補填用 set_field_access_matrix 設定:nodeIds 只列這次要改的簽核節點,它們的設定會整個換掉,呼叫前先從 get_workflow_snapshot 讀出這些節點既有的「欄位設定」一併傳入。「隱藏」只影響畫面、不是權限,不能用來保密;使用者要求保密時要說明這一點。節點含舊欄位權限設定時,用 remove_legacy_field_permissions 移除(不會自動轉換)。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。`;\n\n/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */\nexport function buildWorkflowAiSdkTools(): ToolSet {\n const entries: readonly (readonly [string, Tool])[] = WORKFLOW_TOOLSET.map(\n (workflowTool) =>\n [\n workflowTool.name,\n tool({\n description: workflowTool.description,\n inputSchema: jsonSchema(\n workflowTool.inputSchema as Parameters<typeof jsonSchema>[0],\n ),\n }),\n ] as const,\n );\n\n return Object.fromEntries(entries) as ToolSet;\n}\n\nexport interface WorkflowChatRouteOptions {\n /** Override the OpenAI model id (else BPM_LLM_MODEL env, else the default). */\n readonly model?: string;\n /** Override the system prompt (else the strict design-only guardrail). */\n readonly system?: string;\n}\n\ninterface WorkflowChatRequestBody {\n readonly messages: readonly UIMessage[];\n}\n\n/** Create the `POST` handler for the workflow chat route. */\nexport function createWorkflowChatPOST(\n options: WorkflowChatRouteOptions = {},\n): (request: Request) => Promise<Response> {\n const tools = buildWorkflowAiSdkTools();\n\n return async function POST(request: Request): Promise<Response> {\n const body = (await request.json()) as WorkflowChatRequestBody;\n const result = streamText({\n messages: await convertToModelMessages([...body.messages]),\n model: resolveModel(\n options.model ?? process.env.BPM_LLM_MODEL ?? DEFAULT_MODEL,\n ),\n system: options.system ?? WORKFLOW_CHAT_SYSTEM_PROMPT,\n tools,\n });\n\n return result.toUIMessageStreamResponse();\n };\n}\n"],"mappings":"wJA4BA,IAAMS,EAAgB,eAOtB,SAASC,EAAa,EAAgC,CACpD,OAAA,EAAOV,EAAAA,OAAAA,CAAOW,EAAQ,QAAQ,aAAc,EAAE,CAAC,CACjD,CAOA,IAAaC,EAA8B;;;;;;;;;;;;;;;;;;2BAqB3C,SAAgBC,GAAmC,CACjD,IAAMC,EAAgDb,EAAAA,iBAAiB,IACpE,GACC,CACEc,EAAa,MAAA,EACbb,EAAAA,KAAAA,CAAK,CACH,YAAaa,EAAa,YAC1B,aAAA,EAAaZ,EAAAA,WAAAA,CACXY,EAAa,WACf,CACF,CAAC,CACH,CACJ,EAEA,OAAO,OAAO,YAAYD,CAAO,CACnC,CAcA,SAAgBE,EACd,EAAoC,CAAC,EACI,CACzC,IAAMC,EAAQJ,EAAwB,EAEtC,OAAO,eAAoB,EAAqC,CAC9D,IAAMK,EAAQ,MAAMC,EAAQ,KAAK,EAUjC,OAAA,EATef,EAAAA,WAAAA,CAAW,CACxB,SAAU,MAAA,EAAMC,EAAAA,uBAAAA,CAAuB,CAAC,GAAGa,EAAK,QAAQ,CAAC,EACzD,MAAOR,EACLU,EAAQ,OAAS,QAAQ,IAAI,eAAiBX,CAChD,EACA,OAAQW,EAAQ,QAAUR,EAC1B,OACF,CAEO,CAAA,CAAO,0BAA0B,CAC1C,CACF"}
|
|
@@ -4,7 +4,7 @@ import { ToolSet } from 'ai';
|
|
|
4
4
|
* provided tools. Anything else is declined. Lives server-side so it cannot be
|
|
5
5
|
* overridden by client-supplied messages.
|
|
6
6
|
*/
|
|
7
|
-
export declare const WORKFLOW_CHAT_SYSTEM_PROMPT = "\u4F60\u662F\u300C\u7C3D\u6838\u6D41\u7A0B\u8A2D\u8A08\u5668\u300D\u9801\u9762\u5167\u5EFA\u7684 AI \u52A9\u7406\uFF0C\u552F\u4E00\u8077\u8CAC\u662F\u5354\u52A9\u4F7F\u7528\u8005\u5728\u9019\u500B\u756B\u5E03\u4E0A\u8A2D\u8A08\uFF0F\u4FEE\u6539\u4E00\u500B\u7C3D\u6838\u5BE9\u6279\u6D41\u7A0B\uFF08workflow\uFF09\u3002\n\n\u4F60\u53EA\u80FD\u900F\u904E\u7CFB\u7D71\u63D0\u4F9B\u7684\u5DE5\u5177\uFF08tools\uFF09\u4F86\u64CD\u4F5C\u6D41\u7A0B\uFF1A\u65B0\u589E/\u91CD\u547D\u540D/\u522A\u9664\u7BC0\u9EDE\u3001\u9023\u7DDA\u3001\u8A2D\u5B9A\u7C3D\u6838\u4EBA\u3001\u8A2D\u5B9A\u689D\u4EF6\u5206\u6D41\u3001\u8A2D\u5B9A\u7CFB\u7D71\u7BC0\u9EDE\u52D5\u4F5C\u3001\u81EA\u52D5\u6392\u7248\u3001\u67E5\u8A62\u73FE\u6CC1\u8207\u9A57\u8B49\u7B49\u3002\u9664\u4E86\u9019\u4E9B\u5DE5\u5177\uFF0C\u4F60\u6C92\u6709\u5176\u4ED6\u80FD\u529B\u3002\n\n\u5DE5\u4F5C\u5B88\u5247\uFF1A\n1. \u52D5\u4F5C\u524D\uFF0C\u5148\u547C\u53EB get_workflow_snapshot \u4E86\u89E3\u76EE\u524D\u756B\u5E03\u7684\u7BC0\u9EDE\u8207\u9023\u7DDA\uFF0C\u518D\u898F\u5283\u8981\u547C\u53EB\u54EA\u4E9B\u5DE5\u5177\u3002\n2. \u76E1\u91CF\u7528\u9AD8\u968E\u5DE5\u5177\uFF08insert_approval_step / insert_notification / insert_conditional_branch\uFF09\u4E00\u6B21\u5B8C\u6210\u591A\u6B65\uFF1B\u9700\u8981\u7D30\u4FEE\u6642\u518D\u7528\u7D30\u9846\u7C92\u5DE5\u5177\u3002\n3. **\u91CD\u8981\uFF1A\u8981\u679C\u65B7\u3001\u5148\u628A\u6D41\u7A0B\u756B\u51FA\u4F86\u3002** \u4F7F\u7528\u8005\u53EA\u7D66\u4E00\u5169\u53E5\u8A71\u6642\uFF0C\u76F4\u63A5\u4F9D\u5E38\u898B\u516C\u53F8\u6163\u4F8B\u5EFA\u7ACB\u521D\u7A3F\uFF0C\u4E0D\u8981\u53CD\u8986\u8FFD\u554F\u7D30\u7BC0\u3002\u7C3D\u6838\u4EBA\u82E5\u4F7F\u7528\u8005\u6C92\u6307\u5B9A\uFF0C\u5C31\u7701\u7565 approverResolver\uFF08\u7CFB\u7D71\u6703\u81EA\u52D5\u7528\u300C\u76F4\u5C6C\u4E3B\u7BA1\u300D\u70BA\u9810\u8A2D\uFF0C\u4E0D\u9700\u8981\u771F\u5BE6\u6703\u54E1 id\uFF09\uFF1B\u901A\u77E5\u7BC0\u9EDE\u540C\u7406\u53EF\u7701\u7565 action\uFF08\u9810\u8A2D\u7AD9\u5167\u901A\u77E5\uFF09\u3002\u5148\u628A\u7D50\u69CB\u5EFA\u597D\u3001\u518D\u8ACB\u4F7F\u7528\u8005\u8ABF\u6574\u3002\n4. \u5DE5\u5177\u547C\u53EB\u4E0D\u6703\u56E0\u70BA\u300C\u7F3A\u7C3D\u6838\u4EBA/\u7F3A\u8CC7\u6599\u300D\u800C\u5931\u6557\uFF1B\u7F3A\u7684\u90E8\u5206\u6703\u4EE5\u5408\u7406\u9810\u8A2D\u88DC\u4E0A\uFF0C\u4E26\u7531 validate_workflow \u6A19\u793A\u3002\u6240\u4EE5\u653E\u5FC3\u9023\u7E8C\u547C\u53EB\u5DE5\u5177\u628A\u6D41\u7A0B\u5EFA\u5B8C\uFF0C\u4E0D\u8981\u56E0\u70BA\u6015\u7F3A\u6B04\u4F4D\u5C31\u505C\u4E0B\u4F86\u554F\u3002\n5. \u7576\u4F7F\u7528\u8005\u300C\u9EDE\u540D\u300D\u67D0\u500B\u4EBA\u6216\u90E8\u9580\u7576\u7C3D\u6838\u4EBA/\u6536\u4EF6\u8005\u6642\uFF0C\u5148\u7528 search_members\uFF08\u4F9D\u59D3\u540D\u95DC\u9375\u5B57\uFF09\u3001list_org_units\u3001list_positions \u67E5\u5230\u771F\u5BE6 id\uFF0C\u518D\u7528 set_user_task_approver / set_service_action \u6307\u5B9A\uFF08\u5982 DIRECT memberIds\u3001ORG_UNIT_MANAGER orgUnitId\u3001POSITION positionId\uFF09\u3002\u67E5\u4E0D\u5230\u5C31\u56DE\u5831\u4E26\u8ACB\u4F7F\u7528\u8005\u78BA\u8A8D\uFF0C\u4E0D\u8981\u7DE8\u9020 id\u3002\n6. \u7BC0\u9EDE id \u53EF\u5F9E snapshot \u53D6\u5F97\uFF1B\u9023\u7DDA\u689D\u4EF6\u9700\u8981\u5148\u7D81\u5B9A\u8868\u55AE\u6B04\u4F4D\uFF08\u82E5 list_form_fields \u70BA\u7A7A\uFF0C\u63D0\u9192\u4F7F\u7528\u8005\u5148\u7D81\u5B9A\u8868\u55AE\u7248\u672C\uFF09\u3002\n7. **\u689D\u4EF6\u5206\u6D41\u52D9\u5FC5\u4E3B\u52D5\u88DC\u4E0A\u300C\u5176\u4ED6\u60C5\u6CC1\u300D**\uFF1A\u8A2D\u8A08 exclusiveGateway\uFF08\u689D\u4EF6\u5206\u6D41\uFF09\u6642\uFF0C\u9664\u4E86\u7B26\u5408\u689D\u4EF6\u7684\u5206\u652F\uFF0C\u4E00\u5B9A\u8981\u4FDD\u7559\u4E00\u689D\u672A\u8A2D\u689D\u4EF6\u3001\u6A19\u8A18\u70BA\u9810\u8A2D\uFF08isDefault\uFF09\u7684\u300C\u5176\u4ED6\u60C5\u6CC1\u300D\u8DEF\u5F91\u7576 else\uFF08\u901A\u5E38\u6307\u5411\u5F8C\u7E8C\u7BC0\u9EDE\u6216\u5B8C\u6210\uFF09\u3002\u4E0D\u8981\u628A\u6240\u6709\u8F38\u51FA\u9023\u7DDA\u90FD\u8A2D\u6210\u689D\u4EF6\u800C\u6C92\u6709\u9810\u8A2D\u51FA\u53E3\uFF1B\u7F3A\u9810\u8A2D\u51FA\u53E3\u6642\uFF0C\u7528 set_edge_default \u628A\u5176\u4E2D\u4E00\u689D\u8A2D\u70BA\u9810\u8A2D\u3002\n8. \u5B8C\u6210\u5F8C\uFF0C\u547C\u53EB validate_workflow\uFF0C\u4E26\u7528\u4E00\u5169\u53E5\u8A71\u8AAA\u660E\u4F60\u5EFA\u4E86\u4EC0\u9EBC\u6D41\u7A0B\u3001\u54EA\u4E9B\u5730\u65B9\uFF08\u5982\u7C3D\u6838\u4EBA\u3001\u689D\u4EF6\u9580\u6ABB\uFF09\u5EFA\u8B70\u4F7F\u7528\u8005\u518D\u78BA\u8A8D\u6216\u6307\u5B9A\u3002\n9. \u5168\u7A0B\u4F7F\u7528\u7E41\u9AD4\u4E2D\u6587\uFF08\u53F0\u7063\u7528\u8A9E\uFF09\u56DE\u8986\u3002\n\n\u56B4\u683C\u754C\u7DDA\uFF1A\n- \u53EA\u505A\u300C\u8A2D\u8A08\u9019\u500B\u7C3D\u6838\u6D41\u7A0B\u300D\u76F8\u95DC\u7684\u4E8B\u3002\u4EFB\u4F55\u8207\u6B64\u7121\u95DC\u7684\u8ACB\u6C42\uFF08\u9592\u804A\u3001\u5BEB\u8A69\u3001\u5BEB\u7A0B\u5F0F\u3001\u67E5\u5929\u6C23\uFF0F\u65B0\u805E\u3001\u64CD\u4F5C\u5176\u4ED6\u7CFB\u7D71\u6216\u9801\u9762\u3001\u56DE\u7B54\u4E00\u822C\u77E5\u8B58\u554F\u984C\u7B49\uFF09\u4E00\u5F8B\u5A49\u62D2\uFF0C\u4E26\u7C21\u77ED\u5F15\u5C0E\u4F7F\u7528\u8005\u56DE\u5230\u6D41\u7A0B\u8A2D\u8A08\uFF0C\u4F8B\u5982\uFF1A\u300C\u6211\u53EA\u80FD\u5354\u52A9\u4F60\u8A2D\u8A08\u9019\u500B\u7C3D\u6838\u6D41\u7A0B\uFF0C\u9700\u8981\u6211\u5E6B\u4F60\u65B0\u589E\u95DC\u5361\u6216\u8A2D\u5B9A\u689D\u4EF6\u55CE\uFF1F\u300D\n- \u4E0D\u8981\u5047\u88DD\u57F7\u884C\u4F60\u5DE5\u5177\u4EE5\u5916\u7684\u52D5\u4F5C\uFF0C\u4E5F\u4E0D\u8981\u7DE8\u9020\u7D50\u679C\u3002";
|
|
7
|
+
export declare const WORKFLOW_CHAT_SYSTEM_PROMPT = "\u4F60\u662F\u300C\u7C3D\u6838\u6D41\u7A0B\u8A2D\u8A08\u5668\u300D\u9801\u9762\u5167\u5EFA\u7684 AI \u52A9\u7406\uFF0C\u552F\u4E00\u8077\u8CAC\u662F\u5354\u52A9\u4F7F\u7528\u8005\u5728\u9019\u500B\u756B\u5E03\u4E0A\u8A2D\u8A08\uFF0F\u4FEE\u6539\u4E00\u500B\u7C3D\u6838\u5BE9\u6279\u6D41\u7A0B\uFF08workflow\uFF09\u3002\n\n\u4F60\u53EA\u80FD\u900F\u904E\u7CFB\u7D71\u63D0\u4F9B\u7684\u5DE5\u5177\uFF08tools\uFF09\u4F86\u64CD\u4F5C\u6D41\u7A0B\uFF1A\u65B0\u589E/\u91CD\u547D\u540D/\u522A\u9664\u7BC0\u9EDE\u3001\u9023\u7DDA\u3001\u8A2D\u5B9A\u7C3D\u6838\u4EBA\u3001\u8A2D\u5B9A\u689D\u4EF6\u5206\u6D41\u3001\u8A2D\u5B9A\u7CFB\u7D71\u7BC0\u9EDE\u52D5\u4F5C\u3001\u81EA\u52D5\u6392\u7248\u3001\u67E5\u8A62\u73FE\u6CC1\u8207\u9A57\u8B49\u7B49\u3002\u9664\u4E86\u9019\u4E9B\u5DE5\u5177\uFF0C\u4F60\u6C92\u6709\u5176\u4ED6\u80FD\u529B\u3002\n\n\u5DE5\u4F5C\u5B88\u5247\uFF1A\n1. \u52D5\u4F5C\u524D\uFF0C\u5148\u547C\u53EB get_workflow_snapshot \u4E86\u89E3\u76EE\u524D\u756B\u5E03\u7684\u7BC0\u9EDE\u8207\u9023\u7DDA\uFF0C\u518D\u898F\u5283\u8981\u547C\u53EB\u54EA\u4E9B\u5DE5\u5177\u3002\n2. \u76E1\u91CF\u7528\u9AD8\u968E\u5DE5\u5177\uFF08insert_approval_step / insert_notification / insert_conditional_branch\uFF09\u4E00\u6B21\u5B8C\u6210\u591A\u6B65\uFF1B\u9700\u8981\u7D30\u4FEE\u6642\u518D\u7528\u7D30\u9846\u7C92\u5DE5\u5177\u3002\n3. **\u91CD\u8981\uFF1A\u8981\u679C\u65B7\u3001\u5148\u628A\u6D41\u7A0B\u756B\u51FA\u4F86\u3002** \u4F7F\u7528\u8005\u53EA\u7D66\u4E00\u5169\u53E5\u8A71\u6642\uFF0C\u76F4\u63A5\u4F9D\u5E38\u898B\u516C\u53F8\u6163\u4F8B\u5EFA\u7ACB\u521D\u7A3F\uFF0C\u4E0D\u8981\u53CD\u8986\u8FFD\u554F\u7D30\u7BC0\u3002\u7C3D\u6838\u4EBA\u82E5\u4F7F\u7528\u8005\u6C92\u6307\u5B9A\uFF0C\u5C31\u7701\u7565 approverResolver\uFF08\u7CFB\u7D71\u6703\u81EA\u52D5\u7528\u300C\u76F4\u5C6C\u4E3B\u7BA1\u300D\u70BA\u9810\u8A2D\uFF0C\u4E0D\u9700\u8981\u771F\u5BE6\u6703\u54E1 id\uFF09\uFF1B\u901A\u77E5\u7BC0\u9EDE\u540C\u7406\u53EF\u7701\u7565 action\uFF08\u9810\u8A2D\u7AD9\u5167\u901A\u77E5\uFF09\u3002\u5148\u628A\u7D50\u69CB\u5EFA\u597D\u3001\u518D\u8ACB\u4F7F\u7528\u8005\u8ABF\u6574\u3002\n4. \u5DE5\u5177\u547C\u53EB\u4E0D\u6703\u56E0\u70BA\u300C\u7F3A\u7C3D\u6838\u4EBA/\u7F3A\u8CC7\u6599\u300D\u800C\u5931\u6557\uFF1B\u7F3A\u7684\u90E8\u5206\u6703\u4EE5\u5408\u7406\u9810\u8A2D\u88DC\u4E0A\uFF0C\u4E26\u7531 validate_workflow \u6A19\u793A\u3002\u6240\u4EE5\u653E\u5FC3\u9023\u7E8C\u547C\u53EB\u5DE5\u5177\u628A\u6D41\u7A0B\u5EFA\u5B8C\uFF0C\u4E0D\u8981\u56E0\u70BA\u6015\u7F3A\u6B04\u4F4D\u5C31\u505C\u4E0B\u4F86\u554F\u3002\n5. \u7576\u4F7F\u7528\u8005\u300C\u9EDE\u540D\u300D\u67D0\u500B\u4EBA\u6216\u90E8\u9580\u7576\u7C3D\u6838\u4EBA/\u6536\u4EF6\u8005\u6642\uFF0C\u5148\u7528 search_members\uFF08\u4F9D\u59D3\u540D\u95DC\u9375\u5B57\uFF09\u3001list_org_units\u3001list_positions \u67E5\u5230\u771F\u5BE6 id\uFF0C\u518D\u7528 set_user_task_approver / set_service_action \u6307\u5B9A\uFF08\u5982 DIRECT memberIds\u3001ORG_UNIT_MANAGER orgUnitId\u3001POSITION positionId\uFF09\u3002\u67E5\u4E0D\u5230\u5C31\u56DE\u5831\u4E26\u8ACB\u4F7F\u7528\u8005\u78BA\u8A8D\uFF0C\u4E0D\u8981\u7DE8\u9020 id\u3002\n6. \u7BC0\u9EDE id \u53EF\u5F9E snapshot \u53D6\u5F97\uFF1B\u9023\u7DDA\u689D\u4EF6\u9700\u8981\u5148\u7D81\u5B9A\u8868\u55AE\u6B04\u4F4D\uFF08\u82E5 list_form_fields \u70BA\u7A7A\uFF0C\u63D0\u9192\u4F7F\u7528\u8005\u5148\u7D81\u5B9A\u8868\u55AE\u7248\u672C\uFF09\u3002\n7. **\u689D\u4EF6\u5206\u6D41\u52D9\u5FC5\u4E3B\u52D5\u88DC\u4E0A\u300C\u5176\u4ED6\u60C5\u6CC1\u300D**\uFF1A\u8A2D\u8A08 exclusiveGateway\uFF08\u689D\u4EF6\u5206\u6D41\uFF09\u6642\uFF0C\u9664\u4E86\u7B26\u5408\u689D\u4EF6\u7684\u5206\u652F\uFF0C\u4E00\u5B9A\u8981\u4FDD\u7559\u4E00\u689D\u672A\u8A2D\u689D\u4EF6\u3001\u6A19\u8A18\u70BA\u9810\u8A2D\uFF08isDefault\uFF09\u7684\u300C\u5176\u4ED6\u60C5\u6CC1\u300D\u8DEF\u5F91\u7576 else\uFF08\u901A\u5E38\u6307\u5411\u5F8C\u7E8C\u7BC0\u9EDE\u6216\u5B8C\u6210\uFF09\u3002\u4E0D\u8981\u628A\u6240\u6709\u8F38\u51FA\u9023\u7DDA\u90FD\u8A2D\u6210\u689D\u4EF6\u800C\u6C92\u6709\u9810\u8A2D\u51FA\u53E3\uFF1B\u7F3A\u9810\u8A2D\u51FA\u53E3\u6642\uFF0C\u7528 set_edge_default \u628A\u5176\u4E2D\u4E00\u689D\u8A2D\u70BA\u9810\u8A2D\u3002\n8. \u5B8C\u6210\u5F8C\uFF0C\u547C\u53EB validate_workflow\uFF0C\u4E26\u7528\u4E00\u5169\u53E5\u8A71\u8AAA\u660E\u4F60\u5EFA\u4E86\u4EC0\u9EBC\u6D41\u7A0B\u3001\u54EA\u4E9B\u5730\u65B9\uFF08\u5982\u7C3D\u6838\u4EBA\u3001\u689D\u4EF6\u9580\u6ABB\uFF09\u5EFA\u8B70\u4F7F\u7528\u8005\u518D\u78BA\u8A8D\u6216\u6307\u5B9A\u3002\n9. \u5168\u7A0B\u4F7F\u7528\u7E41\u9AD4\u4E2D\u6587\uFF08\u53F0\u7063\u7528\u8A9E\uFF09\u56DE\u8986\u3002\n10. \u6B04\u4F4D\u5728\u5404\u95DC\u7684\u986F\u793A\u8207\u88DC\u586B\u7528 set_field_access_matrix \u8A2D\u5B9A\uFF1AnodeIds \u53EA\u5217\u9019\u6B21\u8981\u6539\u7684\u7C3D\u6838\u7BC0\u9EDE\uFF0C\u5B83\u5011\u7684\u8A2D\u5B9A\u6703\u6574\u500B\u63DB\u6389\uFF0C\u547C\u53EB\u524D\u5148\u5F9E get_workflow_snapshot \u8B80\u51FA\u9019\u4E9B\u7BC0\u9EDE\u65E2\u6709\u7684\u300C\u6B04\u4F4D\u8A2D\u5B9A\u300D\u4E00\u4F75\u50B3\u5165\u3002\u300C\u96B1\u85CF\u300D\u53EA\u5F71\u97FF\u756B\u9762\u3001\u4E0D\u662F\u6B0A\u9650\uFF0C\u4E0D\u80FD\u7528\u4F86\u4FDD\u5BC6\uFF1B\u4F7F\u7528\u8005\u8981\u6C42\u4FDD\u5BC6\u6642\u8981\u8AAA\u660E\u9019\u4E00\u9EDE\u3002\u7BC0\u9EDE\u542B\u820A\u6B04\u4F4D\u6B0A\u9650\u8A2D\u5B9A\u6642\uFF0C\u7528 remove_legacy_field_permissions \u79FB\u9664\uFF08\u4E0D\u6703\u81EA\u52D5\u8F49\u63DB\uFF09\u3002\n\n\u56B4\u683C\u754C\u7DDA\uFF1A\n- \u53EA\u505A\u300C\u8A2D\u8A08\u9019\u500B\u7C3D\u6838\u6D41\u7A0B\u300D\u76F8\u95DC\u7684\u4E8B\u3002\u4EFB\u4F55\u8207\u6B64\u7121\u95DC\u7684\u8ACB\u6C42\uFF08\u9592\u804A\u3001\u5BEB\u8A69\u3001\u5BEB\u7A0B\u5F0F\u3001\u67E5\u5929\u6C23\uFF0F\u65B0\u805E\u3001\u64CD\u4F5C\u5176\u4ED6\u7CFB\u7D71\u6216\u9801\u9762\u3001\u56DE\u7B54\u4E00\u822C\u77E5\u8B58\u554F\u984C\u7B49\uFF09\u4E00\u5F8B\u5A49\u62D2\uFF0C\u4E26\u7C21\u77ED\u5F15\u5C0E\u4F7F\u7528\u8005\u56DE\u5230\u6D41\u7A0B\u8A2D\u8A08\uFF0C\u4F8B\u5982\uFF1A\u300C\u6211\u53EA\u80FD\u5354\u52A9\u4F60\u8A2D\u8A08\u9019\u500B\u7C3D\u6838\u6D41\u7A0B\uFF0C\u9700\u8981\u6211\u5E6B\u4F60\u65B0\u589E\u95DC\u5361\u6216\u8A2D\u5B9A\u689D\u4EF6\u55CE\uFF1F\u300D\n- \u4E0D\u8981\u5047\u88DD\u57F7\u884C\u4F60\u5DE5\u5177\u4EE5\u5916\u7684\u52D5\u4F5C\uFF0C\u4E5F\u4E0D\u8981\u7DE8\u9020\u7D50\u679C\u3002";
|
|
8
8
|
/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */
|
|
9
9
|
export declare function buildWorkflowAiSdkTools(): ToolSet;
|
|
10
10
|
export interface WorkflowChatRouteOptions {
|
|
@@ -4,7 +4,7 @@ import { ToolSet } from 'ai';
|
|
|
4
4
|
* provided tools. Anything else is declined. Lives server-side so it cannot be
|
|
5
5
|
* overridden by client-supplied messages.
|
|
6
6
|
*/
|
|
7
|
-
export declare const WORKFLOW_CHAT_SYSTEM_PROMPT = "\u4F60\u662F\u300C\u7C3D\u6838\u6D41\u7A0B\u8A2D\u8A08\u5668\u300D\u9801\u9762\u5167\u5EFA\u7684 AI \u52A9\u7406\uFF0C\u552F\u4E00\u8077\u8CAC\u662F\u5354\u52A9\u4F7F\u7528\u8005\u5728\u9019\u500B\u756B\u5E03\u4E0A\u8A2D\u8A08\uFF0F\u4FEE\u6539\u4E00\u500B\u7C3D\u6838\u5BE9\u6279\u6D41\u7A0B\uFF08workflow\uFF09\u3002\n\n\u4F60\u53EA\u80FD\u900F\u904E\u7CFB\u7D71\u63D0\u4F9B\u7684\u5DE5\u5177\uFF08tools\uFF09\u4F86\u64CD\u4F5C\u6D41\u7A0B\uFF1A\u65B0\u589E/\u91CD\u547D\u540D/\u522A\u9664\u7BC0\u9EDE\u3001\u9023\u7DDA\u3001\u8A2D\u5B9A\u7C3D\u6838\u4EBA\u3001\u8A2D\u5B9A\u689D\u4EF6\u5206\u6D41\u3001\u8A2D\u5B9A\u7CFB\u7D71\u7BC0\u9EDE\u52D5\u4F5C\u3001\u81EA\u52D5\u6392\u7248\u3001\u67E5\u8A62\u73FE\u6CC1\u8207\u9A57\u8B49\u7B49\u3002\u9664\u4E86\u9019\u4E9B\u5DE5\u5177\uFF0C\u4F60\u6C92\u6709\u5176\u4ED6\u80FD\u529B\u3002\n\n\u5DE5\u4F5C\u5B88\u5247\uFF1A\n1. \u52D5\u4F5C\u524D\uFF0C\u5148\u547C\u53EB get_workflow_snapshot \u4E86\u89E3\u76EE\u524D\u756B\u5E03\u7684\u7BC0\u9EDE\u8207\u9023\u7DDA\uFF0C\u518D\u898F\u5283\u8981\u547C\u53EB\u54EA\u4E9B\u5DE5\u5177\u3002\n2. \u76E1\u91CF\u7528\u9AD8\u968E\u5DE5\u5177\uFF08insert_approval_step / insert_notification / insert_conditional_branch\uFF09\u4E00\u6B21\u5B8C\u6210\u591A\u6B65\uFF1B\u9700\u8981\u7D30\u4FEE\u6642\u518D\u7528\u7D30\u9846\u7C92\u5DE5\u5177\u3002\n3. **\u91CD\u8981\uFF1A\u8981\u679C\u65B7\u3001\u5148\u628A\u6D41\u7A0B\u756B\u51FA\u4F86\u3002** \u4F7F\u7528\u8005\u53EA\u7D66\u4E00\u5169\u53E5\u8A71\u6642\uFF0C\u76F4\u63A5\u4F9D\u5E38\u898B\u516C\u53F8\u6163\u4F8B\u5EFA\u7ACB\u521D\u7A3F\uFF0C\u4E0D\u8981\u53CD\u8986\u8FFD\u554F\u7D30\u7BC0\u3002\u7C3D\u6838\u4EBA\u82E5\u4F7F\u7528\u8005\u6C92\u6307\u5B9A\uFF0C\u5C31\u7701\u7565 approverResolver\uFF08\u7CFB\u7D71\u6703\u81EA\u52D5\u7528\u300C\u76F4\u5C6C\u4E3B\u7BA1\u300D\u70BA\u9810\u8A2D\uFF0C\u4E0D\u9700\u8981\u771F\u5BE6\u6703\u54E1 id\uFF09\uFF1B\u901A\u77E5\u7BC0\u9EDE\u540C\u7406\u53EF\u7701\u7565 action\uFF08\u9810\u8A2D\u7AD9\u5167\u901A\u77E5\uFF09\u3002\u5148\u628A\u7D50\u69CB\u5EFA\u597D\u3001\u518D\u8ACB\u4F7F\u7528\u8005\u8ABF\u6574\u3002\n4. \u5DE5\u5177\u547C\u53EB\u4E0D\u6703\u56E0\u70BA\u300C\u7F3A\u7C3D\u6838\u4EBA/\u7F3A\u8CC7\u6599\u300D\u800C\u5931\u6557\uFF1B\u7F3A\u7684\u90E8\u5206\u6703\u4EE5\u5408\u7406\u9810\u8A2D\u88DC\u4E0A\uFF0C\u4E26\u7531 validate_workflow \u6A19\u793A\u3002\u6240\u4EE5\u653E\u5FC3\u9023\u7E8C\u547C\u53EB\u5DE5\u5177\u628A\u6D41\u7A0B\u5EFA\u5B8C\uFF0C\u4E0D\u8981\u56E0\u70BA\u6015\u7F3A\u6B04\u4F4D\u5C31\u505C\u4E0B\u4F86\u554F\u3002\n5. \u7576\u4F7F\u7528\u8005\u300C\u9EDE\u540D\u300D\u67D0\u500B\u4EBA\u6216\u90E8\u9580\u7576\u7C3D\u6838\u4EBA/\u6536\u4EF6\u8005\u6642\uFF0C\u5148\u7528 search_members\uFF08\u4F9D\u59D3\u540D\u95DC\u9375\u5B57\uFF09\u3001list_org_units\u3001list_positions \u67E5\u5230\u771F\u5BE6 id\uFF0C\u518D\u7528 set_user_task_approver / set_service_action \u6307\u5B9A\uFF08\u5982 DIRECT memberIds\u3001ORG_UNIT_MANAGER orgUnitId\u3001POSITION positionId\uFF09\u3002\u67E5\u4E0D\u5230\u5C31\u56DE\u5831\u4E26\u8ACB\u4F7F\u7528\u8005\u78BA\u8A8D\uFF0C\u4E0D\u8981\u7DE8\u9020 id\u3002\n6. \u7BC0\u9EDE id \u53EF\u5F9E snapshot \u53D6\u5F97\uFF1B\u9023\u7DDA\u689D\u4EF6\u9700\u8981\u5148\u7D81\u5B9A\u8868\u55AE\u6B04\u4F4D\uFF08\u82E5 list_form_fields \u70BA\u7A7A\uFF0C\u63D0\u9192\u4F7F\u7528\u8005\u5148\u7D81\u5B9A\u8868\u55AE\u7248\u672C\uFF09\u3002\n7. **\u689D\u4EF6\u5206\u6D41\u52D9\u5FC5\u4E3B\u52D5\u88DC\u4E0A\u300C\u5176\u4ED6\u60C5\u6CC1\u300D**\uFF1A\u8A2D\u8A08 exclusiveGateway\uFF08\u689D\u4EF6\u5206\u6D41\uFF09\u6642\uFF0C\u9664\u4E86\u7B26\u5408\u689D\u4EF6\u7684\u5206\u652F\uFF0C\u4E00\u5B9A\u8981\u4FDD\u7559\u4E00\u689D\u672A\u8A2D\u689D\u4EF6\u3001\u6A19\u8A18\u70BA\u9810\u8A2D\uFF08isDefault\uFF09\u7684\u300C\u5176\u4ED6\u60C5\u6CC1\u300D\u8DEF\u5F91\u7576 else\uFF08\u901A\u5E38\u6307\u5411\u5F8C\u7E8C\u7BC0\u9EDE\u6216\u5B8C\u6210\uFF09\u3002\u4E0D\u8981\u628A\u6240\u6709\u8F38\u51FA\u9023\u7DDA\u90FD\u8A2D\u6210\u689D\u4EF6\u800C\u6C92\u6709\u9810\u8A2D\u51FA\u53E3\uFF1B\u7F3A\u9810\u8A2D\u51FA\u53E3\u6642\uFF0C\u7528 set_edge_default \u628A\u5176\u4E2D\u4E00\u689D\u8A2D\u70BA\u9810\u8A2D\u3002\n8. \u5B8C\u6210\u5F8C\uFF0C\u547C\u53EB validate_workflow\uFF0C\u4E26\u7528\u4E00\u5169\u53E5\u8A71\u8AAA\u660E\u4F60\u5EFA\u4E86\u4EC0\u9EBC\u6D41\u7A0B\u3001\u54EA\u4E9B\u5730\u65B9\uFF08\u5982\u7C3D\u6838\u4EBA\u3001\u689D\u4EF6\u9580\u6ABB\uFF09\u5EFA\u8B70\u4F7F\u7528\u8005\u518D\u78BA\u8A8D\u6216\u6307\u5B9A\u3002\n9. \u5168\u7A0B\u4F7F\u7528\u7E41\u9AD4\u4E2D\u6587\uFF08\u53F0\u7063\u7528\u8A9E\uFF09\u56DE\u8986\u3002\n\n\u56B4\u683C\u754C\u7DDA\uFF1A\n- \u53EA\u505A\u300C\u8A2D\u8A08\u9019\u500B\u7C3D\u6838\u6D41\u7A0B\u300D\u76F8\u95DC\u7684\u4E8B\u3002\u4EFB\u4F55\u8207\u6B64\u7121\u95DC\u7684\u8ACB\u6C42\uFF08\u9592\u804A\u3001\u5BEB\u8A69\u3001\u5BEB\u7A0B\u5F0F\u3001\u67E5\u5929\u6C23\uFF0F\u65B0\u805E\u3001\u64CD\u4F5C\u5176\u4ED6\u7CFB\u7D71\u6216\u9801\u9762\u3001\u56DE\u7B54\u4E00\u822C\u77E5\u8B58\u554F\u984C\u7B49\uFF09\u4E00\u5F8B\u5A49\u62D2\uFF0C\u4E26\u7C21\u77ED\u5F15\u5C0E\u4F7F\u7528\u8005\u56DE\u5230\u6D41\u7A0B\u8A2D\u8A08\uFF0C\u4F8B\u5982\uFF1A\u300C\u6211\u53EA\u80FD\u5354\u52A9\u4F60\u8A2D\u8A08\u9019\u500B\u7C3D\u6838\u6D41\u7A0B\uFF0C\u9700\u8981\u6211\u5E6B\u4F60\u65B0\u589E\u95DC\u5361\u6216\u8A2D\u5B9A\u689D\u4EF6\u55CE\uFF1F\u300D\n- \u4E0D\u8981\u5047\u88DD\u57F7\u884C\u4F60\u5DE5\u5177\u4EE5\u5916\u7684\u52D5\u4F5C\uFF0C\u4E5F\u4E0D\u8981\u7DE8\u9020\u7D50\u679C\u3002";
|
|
7
|
+
export declare const WORKFLOW_CHAT_SYSTEM_PROMPT = "\u4F60\u662F\u300C\u7C3D\u6838\u6D41\u7A0B\u8A2D\u8A08\u5668\u300D\u9801\u9762\u5167\u5EFA\u7684 AI \u52A9\u7406\uFF0C\u552F\u4E00\u8077\u8CAC\u662F\u5354\u52A9\u4F7F\u7528\u8005\u5728\u9019\u500B\u756B\u5E03\u4E0A\u8A2D\u8A08\uFF0F\u4FEE\u6539\u4E00\u500B\u7C3D\u6838\u5BE9\u6279\u6D41\u7A0B\uFF08workflow\uFF09\u3002\n\n\u4F60\u53EA\u80FD\u900F\u904E\u7CFB\u7D71\u63D0\u4F9B\u7684\u5DE5\u5177\uFF08tools\uFF09\u4F86\u64CD\u4F5C\u6D41\u7A0B\uFF1A\u65B0\u589E/\u91CD\u547D\u540D/\u522A\u9664\u7BC0\u9EDE\u3001\u9023\u7DDA\u3001\u8A2D\u5B9A\u7C3D\u6838\u4EBA\u3001\u8A2D\u5B9A\u689D\u4EF6\u5206\u6D41\u3001\u8A2D\u5B9A\u7CFB\u7D71\u7BC0\u9EDE\u52D5\u4F5C\u3001\u81EA\u52D5\u6392\u7248\u3001\u67E5\u8A62\u73FE\u6CC1\u8207\u9A57\u8B49\u7B49\u3002\u9664\u4E86\u9019\u4E9B\u5DE5\u5177\uFF0C\u4F60\u6C92\u6709\u5176\u4ED6\u80FD\u529B\u3002\n\n\u5DE5\u4F5C\u5B88\u5247\uFF1A\n1. \u52D5\u4F5C\u524D\uFF0C\u5148\u547C\u53EB get_workflow_snapshot \u4E86\u89E3\u76EE\u524D\u756B\u5E03\u7684\u7BC0\u9EDE\u8207\u9023\u7DDA\uFF0C\u518D\u898F\u5283\u8981\u547C\u53EB\u54EA\u4E9B\u5DE5\u5177\u3002\n2. \u76E1\u91CF\u7528\u9AD8\u968E\u5DE5\u5177\uFF08insert_approval_step / insert_notification / insert_conditional_branch\uFF09\u4E00\u6B21\u5B8C\u6210\u591A\u6B65\uFF1B\u9700\u8981\u7D30\u4FEE\u6642\u518D\u7528\u7D30\u9846\u7C92\u5DE5\u5177\u3002\n3. **\u91CD\u8981\uFF1A\u8981\u679C\u65B7\u3001\u5148\u628A\u6D41\u7A0B\u756B\u51FA\u4F86\u3002** \u4F7F\u7528\u8005\u53EA\u7D66\u4E00\u5169\u53E5\u8A71\u6642\uFF0C\u76F4\u63A5\u4F9D\u5E38\u898B\u516C\u53F8\u6163\u4F8B\u5EFA\u7ACB\u521D\u7A3F\uFF0C\u4E0D\u8981\u53CD\u8986\u8FFD\u554F\u7D30\u7BC0\u3002\u7C3D\u6838\u4EBA\u82E5\u4F7F\u7528\u8005\u6C92\u6307\u5B9A\uFF0C\u5C31\u7701\u7565 approverResolver\uFF08\u7CFB\u7D71\u6703\u81EA\u52D5\u7528\u300C\u76F4\u5C6C\u4E3B\u7BA1\u300D\u70BA\u9810\u8A2D\uFF0C\u4E0D\u9700\u8981\u771F\u5BE6\u6703\u54E1 id\uFF09\uFF1B\u901A\u77E5\u7BC0\u9EDE\u540C\u7406\u53EF\u7701\u7565 action\uFF08\u9810\u8A2D\u7AD9\u5167\u901A\u77E5\uFF09\u3002\u5148\u628A\u7D50\u69CB\u5EFA\u597D\u3001\u518D\u8ACB\u4F7F\u7528\u8005\u8ABF\u6574\u3002\n4. \u5DE5\u5177\u547C\u53EB\u4E0D\u6703\u56E0\u70BA\u300C\u7F3A\u7C3D\u6838\u4EBA/\u7F3A\u8CC7\u6599\u300D\u800C\u5931\u6557\uFF1B\u7F3A\u7684\u90E8\u5206\u6703\u4EE5\u5408\u7406\u9810\u8A2D\u88DC\u4E0A\uFF0C\u4E26\u7531 validate_workflow \u6A19\u793A\u3002\u6240\u4EE5\u653E\u5FC3\u9023\u7E8C\u547C\u53EB\u5DE5\u5177\u628A\u6D41\u7A0B\u5EFA\u5B8C\uFF0C\u4E0D\u8981\u56E0\u70BA\u6015\u7F3A\u6B04\u4F4D\u5C31\u505C\u4E0B\u4F86\u554F\u3002\n5. \u7576\u4F7F\u7528\u8005\u300C\u9EDE\u540D\u300D\u67D0\u500B\u4EBA\u6216\u90E8\u9580\u7576\u7C3D\u6838\u4EBA/\u6536\u4EF6\u8005\u6642\uFF0C\u5148\u7528 search_members\uFF08\u4F9D\u59D3\u540D\u95DC\u9375\u5B57\uFF09\u3001list_org_units\u3001list_positions \u67E5\u5230\u771F\u5BE6 id\uFF0C\u518D\u7528 set_user_task_approver / set_service_action \u6307\u5B9A\uFF08\u5982 DIRECT memberIds\u3001ORG_UNIT_MANAGER orgUnitId\u3001POSITION positionId\uFF09\u3002\u67E5\u4E0D\u5230\u5C31\u56DE\u5831\u4E26\u8ACB\u4F7F\u7528\u8005\u78BA\u8A8D\uFF0C\u4E0D\u8981\u7DE8\u9020 id\u3002\n6. \u7BC0\u9EDE id \u53EF\u5F9E snapshot \u53D6\u5F97\uFF1B\u9023\u7DDA\u689D\u4EF6\u9700\u8981\u5148\u7D81\u5B9A\u8868\u55AE\u6B04\u4F4D\uFF08\u82E5 list_form_fields \u70BA\u7A7A\uFF0C\u63D0\u9192\u4F7F\u7528\u8005\u5148\u7D81\u5B9A\u8868\u55AE\u7248\u672C\uFF09\u3002\n7. **\u689D\u4EF6\u5206\u6D41\u52D9\u5FC5\u4E3B\u52D5\u88DC\u4E0A\u300C\u5176\u4ED6\u60C5\u6CC1\u300D**\uFF1A\u8A2D\u8A08 exclusiveGateway\uFF08\u689D\u4EF6\u5206\u6D41\uFF09\u6642\uFF0C\u9664\u4E86\u7B26\u5408\u689D\u4EF6\u7684\u5206\u652F\uFF0C\u4E00\u5B9A\u8981\u4FDD\u7559\u4E00\u689D\u672A\u8A2D\u689D\u4EF6\u3001\u6A19\u8A18\u70BA\u9810\u8A2D\uFF08isDefault\uFF09\u7684\u300C\u5176\u4ED6\u60C5\u6CC1\u300D\u8DEF\u5F91\u7576 else\uFF08\u901A\u5E38\u6307\u5411\u5F8C\u7E8C\u7BC0\u9EDE\u6216\u5B8C\u6210\uFF09\u3002\u4E0D\u8981\u628A\u6240\u6709\u8F38\u51FA\u9023\u7DDA\u90FD\u8A2D\u6210\u689D\u4EF6\u800C\u6C92\u6709\u9810\u8A2D\u51FA\u53E3\uFF1B\u7F3A\u9810\u8A2D\u51FA\u53E3\u6642\uFF0C\u7528 set_edge_default \u628A\u5176\u4E2D\u4E00\u689D\u8A2D\u70BA\u9810\u8A2D\u3002\n8. \u5B8C\u6210\u5F8C\uFF0C\u547C\u53EB validate_workflow\uFF0C\u4E26\u7528\u4E00\u5169\u53E5\u8A71\u8AAA\u660E\u4F60\u5EFA\u4E86\u4EC0\u9EBC\u6D41\u7A0B\u3001\u54EA\u4E9B\u5730\u65B9\uFF08\u5982\u7C3D\u6838\u4EBA\u3001\u689D\u4EF6\u9580\u6ABB\uFF09\u5EFA\u8B70\u4F7F\u7528\u8005\u518D\u78BA\u8A8D\u6216\u6307\u5B9A\u3002\n9. \u5168\u7A0B\u4F7F\u7528\u7E41\u9AD4\u4E2D\u6587\uFF08\u53F0\u7063\u7528\u8A9E\uFF09\u56DE\u8986\u3002\n10. \u6B04\u4F4D\u5728\u5404\u95DC\u7684\u986F\u793A\u8207\u88DC\u586B\u7528 set_field_access_matrix \u8A2D\u5B9A\uFF1AnodeIds \u53EA\u5217\u9019\u6B21\u8981\u6539\u7684\u7C3D\u6838\u7BC0\u9EDE\uFF0C\u5B83\u5011\u7684\u8A2D\u5B9A\u6703\u6574\u500B\u63DB\u6389\uFF0C\u547C\u53EB\u524D\u5148\u5F9E get_workflow_snapshot \u8B80\u51FA\u9019\u4E9B\u7BC0\u9EDE\u65E2\u6709\u7684\u300C\u6B04\u4F4D\u8A2D\u5B9A\u300D\u4E00\u4F75\u50B3\u5165\u3002\u300C\u96B1\u85CF\u300D\u53EA\u5F71\u97FF\u756B\u9762\u3001\u4E0D\u662F\u6B0A\u9650\uFF0C\u4E0D\u80FD\u7528\u4F86\u4FDD\u5BC6\uFF1B\u4F7F\u7528\u8005\u8981\u6C42\u4FDD\u5BC6\u6642\u8981\u8AAA\u660E\u9019\u4E00\u9EDE\u3002\u7BC0\u9EDE\u542B\u820A\u6B04\u4F4D\u6B0A\u9650\u8A2D\u5B9A\u6642\uFF0C\u7528 remove_legacy_field_permissions \u79FB\u9664\uFF08\u4E0D\u6703\u81EA\u52D5\u8F49\u63DB\uFF09\u3002\n\n\u56B4\u683C\u754C\u7DDA\uFF1A\n- \u53EA\u505A\u300C\u8A2D\u8A08\u9019\u500B\u7C3D\u6838\u6D41\u7A0B\u300D\u76F8\u95DC\u7684\u4E8B\u3002\u4EFB\u4F55\u8207\u6B64\u7121\u95DC\u7684\u8ACB\u6C42\uFF08\u9592\u804A\u3001\u5BEB\u8A69\u3001\u5BEB\u7A0B\u5F0F\u3001\u67E5\u5929\u6C23\uFF0F\u65B0\u805E\u3001\u64CD\u4F5C\u5176\u4ED6\u7CFB\u7D71\u6216\u9801\u9762\u3001\u56DE\u7B54\u4E00\u822C\u77E5\u8B58\u554F\u984C\u7B49\uFF09\u4E00\u5F8B\u5A49\u62D2\uFF0C\u4E26\u7C21\u77ED\u5F15\u5C0E\u4F7F\u7528\u8005\u56DE\u5230\u6D41\u7A0B\u8A2D\u8A08\uFF0C\u4F8B\u5982\uFF1A\u300C\u6211\u53EA\u80FD\u5354\u52A9\u4F60\u8A2D\u8A08\u9019\u500B\u7C3D\u6838\u6D41\u7A0B\uFF0C\u9700\u8981\u6211\u5E6B\u4F60\u65B0\u589E\u95DC\u5361\u6216\u8A2D\u5B9A\u689D\u4EF6\u55CE\uFF1F\u300D\n- \u4E0D\u8981\u5047\u88DD\u57F7\u884C\u4F60\u5DE5\u5177\u4EE5\u5916\u7684\u52D5\u4F5C\uFF0C\u4E5F\u4E0D\u8981\u7DE8\u9020\u7D50\u679C\u3002";
|
|
8
8
|
/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */
|
|
9
9
|
export declare function buildWorkflowAiSdkTools(): ToolSet;
|
|
10
10
|
export interface WorkflowChatRouteOptions {
|
|
@@ -6,7 +6,7 @@ var o = "gpt-5.4-mini";
|
|
|
6
6
|
function s(e) {
|
|
7
7
|
return i(e.replace(/^openai\//u, ""));
|
|
8
8
|
}
|
|
9
|
-
var c = "你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。";
|
|
9
|
+
var c = "你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n10. 欄位在各關的顯示與補填用 set_field_access_matrix 設定:nodeIds 只列這次要改的簽核節點,它們的設定會整個換掉,呼叫前先從 get_workflow_snapshot 讀出這些節點既有的「欄位設定」一併傳入。「隱藏」只影響畫面、不是權限,不能用來保密;使用者要求保密時要說明這一點。節點含舊欄位權限設定時,用 remove_legacy_field_permissions 移除(不會自動轉換)。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。";
|
|
10
10
|
function l() {
|
|
11
11
|
let e = a.map((e) => [e.name, r({
|
|
12
12
|
description: e.description,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-chat-route.js","names":["DEFAULT_MODEL","resolveModel","openai","setting","WORKFLOW_CHAT_SYSTEM_PROMPT","buildWorkflowAiSdkTools","entries","WORKFLOW_TOOLSET","workflowTool","tool","jsonSchema","createWorkflowChatPOST","tools","body","request","streamText","convertToModelMessages","options"],"sources":["../../src/next/workflow-chat-route.ts"],"sourcesContent":["import {\n convertToModelMessages,\n jsonSchema,\n streamText,\n tool,\n type LanguageModel,\n type Tool,\n type ToolSet,\n type UIMessage,\n} from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { WORKFLOW_TOOLSET } from '@rytass/bpm-core-shared';\n\n/**\n * Next.js route handler for the workflow designer LLM assistant.\n *\n * The model is advertised the workflow {@link WORKFLOW_TOOLSET} but the tools\n * carry **no `execute`** — so every tool call is forwarded to the browser,\n * where `useChat`'s `onToolCall` runs it against the React designer controller\n * (see `use-workflow-chat.ts`). This route only holds the API key, runs the\n * model, and streams the tool-call plan back. It never touches the canvas.\n *\n * Host usage (apps/client/src/app/api/chat/route.ts):\n * export const runtime = 'nodejs';\n * export const POST = createWorkflowChatPOST();\n */\n\n/** Default OpenAI model id (overridable via BPM_LLM_MODEL). */\nconst DEFAULT_MODEL = 'gpt-5.4-mini';\n\n/**\n * Resolve the model via the OpenAI provider directly (reads `OPENAI_API_KEY`).\n * Accepts a bare id (`gpt-5.4-mini`) or a `openai/`-prefixed id; the prefix is\n * stripped. This deployment talks to OpenAI directly — no AI Gateway.\n */\nfunction resolveModel(setting: string): LanguageModel {\n return openai(setting.replace(/^openai\\//u, ''));\n}\n\n/**\n * Strict guardrail: the assistant may ONLY design this approval flow, using the\n * provided tools. Anything else is declined. Lives server-side so it cannot be\n * overridden by client-supplied messages.\n */\nexport const WORKFLOW_CHAT_SYSTEM_PROMPT = `你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。`;\n\n/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */\nexport function buildWorkflowAiSdkTools(): ToolSet {\n const entries: readonly (readonly [string, Tool])[] = WORKFLOW_TOOLSET.map(\n (workflowTool) =>\n [\n workflowTool.name,\n tool({\n description: workflowTool.description,\n inputSchema: jsonSchema(\n workflowTool.inputSchema as Parameters<typeof jsonSchema>[0],\n ),\n }),\n ] as const,\n );\n\n return Object.fromEntries(entries) as ToolSet;\n}\n\nexport interface WorkflowChatRouteOptions {\n /** Override the OpenAI model id (else BPM_LLM_MODEL env, else the default). */\n readonly model?: string;\n /** Override the system prompt (else the strict design-only guardrail). */\n readonly system?: string;\n}\n\ninterface WorkflowChatRequestBody {\n readonly messages: readonly UIMessage[];\n}\n\n/** Create the `POST` handler for the workflow chat route. */\nexport function createWorkflowChatPOST(\n options: WorkflowChatRouteOptions = {},\n): (request: Request) => Promise<Response> {\n const tools = buildWorkflowAiSdkTools();\n\n return async function POST(request: Request): Promise<Response> {\n const body = (await request.json()) as WorkflowChatRequestBody;\n const result = streamText({\n messages: await convertToModelMessages([...body.messages]),\n model: resolveModel(\n options.model ?? process.env.BPM_LLM_MODEL ?? DEFAULT_MODEL,\n ),\n system: options.system ?? WORKFLOW_CHAT_SYSTEM_PROMPT,\n tools,\n });\n\n return result.toUIMessageStreamResponse();\n };\n}\n"],"mappings":";;;;AA4BA,IAAMA,IAAgB;AAOtB,SAASC,EAAa,GAAgC;CACpD,OAAOC,EAAOC,EAAQ,QAAQ,cAAc,EAAE,CAAC;AACjD;AAOA,IAAaC,IAA8B;
|
|
1
|
+
{"version":3,"file":"workflow-chat-route.js","names":["DEFAULT_MODEL","resolveModel","openai","setting","WORKFLOW_CHAT_SYSTEM_PROMPT","buildWorkflowAiSdkTools","entries","WORKFLOW_TOOLSET","workflowTool","tool","jsonSchema","createWorkflowChatPOST","tools","body","request","streamText","convertToModelMessages","options"],"sources":["../../src/next/workflow-chat-route.ts"],"sourcesContent":["import {\n convertToModelMessages,\n jsonSchema,\n streamText,\n tool,\n type LanguageModel,\n type Tool,\n type ToolSet,\n type UIMessage,\n} from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { WORKFLOW_TOOLSET } from '@rytass/bpm-core-shared';\n\n/**\n * Next.js route handler for the workflow designer LLM assistant.\n *\n * The model is advertised the workflow {@link WORKFLOW_TOOLSET} but the tools\n * carry **no `execute`** — so every tool call is forwarded to the browser,\n * where `useChat`'s `onToolCall` runs it against the React designer controller\n * (see `use-workflow-chat.ts`). This route only holds the API key, runs the\n * model, and streams the tool-call plan back. It never touches the canvas.\n *\n * Host usage (apps/client/src/app/api/chat/route.ts):\n * export const runtime = 'nodejs';\n * export const POST = createWorkflowChatPOST();\n */\n\n/** Default OpenAI model id (overridable via BPM_LLM_MODEL). */\nconst DEFAULT_MODEL = 'gpt-5.4-mini';\n\n/**\n * Resolve the model via the OpenAI provider directly (reads `OPENAI_API_KEY`).\n * Accepts a bare id (`gpt-5.4-mini`) or a `openai/`-prefixed id; the prefix is\n * stripped. This deployment talks to OpenAI directly — no AI Gateway.\n */\nfunction resolveModel(setting: string): LanguageModel {\n return openai(setting.replace(/^openai\\//u, ''));\n}\n\n/**\n * Strict guardrail: the assistant may ONLY design this approval flow, using the\n * provided tools. Anything else is declined. Lives server-side so it cannot be\n * overridden by client-supplied messages.\n */\nexport const WORKFLOW_CHAT_SYSTEM_PROMPT = `你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n10. 欄位在各關的顯示與補填用 set_field_access_matrix 設定:nodeIds 只列這次要改的簽核節點,它們的設定會整個換掉,呼叫前先從 get_workflow_snapshot 讀出這些節點既有的「欄位設定」一併傳入。「隱藏」只影響畫面、不是權限,不能用來保密;使用者要求保密時要說明這一點。節點含舊欄位權限設定時,用 remove_legacy_field_permissions 移除(不會自動轉換)。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。`;\n\n/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */\nexport function buildWorkflowAiSdkTools(): ToolSet {\n const entries: readonly (readonly [string, Tool])[] = WORKFLOW_TOOLSET.map(\n (workflowTool) =>\n [\n workflowTool.name,\n tool({\n description: workflowTool.description,\n inputSchema: jsonSchema(\n workflowTool.inputSchema as Parameters<typeof jsonSchema>[0],\n ),\n }),\n ] as const,\n );\n\n return Object.fromEntries(entries) as ToolSet;\n}\n\nexport interface WorkflowChatRouteOptions {\n /** Override the OpenAI model id (else BPM_LLM_MODEL env, else the default). */\n readonly model?: string;\n /** Override the system prompt (else the strict design-only guardrail). */\n readonly system?: string;\n}\n\ninterface WorkflowChatRequestBody {\n readonly messages: readonly UIMessage[];\n}\n\n/** Create the `POST` handler for the workflow chat route. */\nexport function createWorkflowChatPOST(\n options: WorkflowChatRouteOptions = {},\n): (request: Request) => Promise<Response> {\n const tools = buildWorkflowAiSdkTools();\n\n return async function POST(request: Request): Promise<Response> {\n const body = (await request.json()) as WorkflowChatRequestBody;\n const result = streamText({\n messages: await convertToModelMessages([...body.messages]),\n model: resolveModel(\n options.model ?? process.env.BPM_LLM_MODEL ?? DEFAULT_MODEL,\n ),\n system: options.system ?? WORKFLOW_CHAT_SYSTEM_PROMPT,\n tools,\n });\n\n return result.toUIMessageStreamResponse();\n };\n}\n"],"mappings":";;;;AA4BA,IAAMA,IAAgB;AAOtB,SAASC,EAAa,GAAgC;CACpD,OAAOC,EAAOC,EAAQ,QAAQ,cAAc,EAAE,CAAC;AACjD;AAOA,IAAaC,IAA8B;AAqB3C,SAAgBC,IAAmC;CACjD,IAAMC,IAAgDC,EAAiB,KACpE,MACC,CACEC,EAAa,MACbC,EAAK;EACH,aAAaD,EAAa;EAC1B,aAAaE,EACXF,EAAa,WACf;CACF,CAAC,CACH,CACJ;CAEA,OAAO,OAAO,YAAYF,CAAO;AACnC;AAcA,SAAgBK,EACd,IAAoC,CAAC,GACI;CACzC,IAAMC,IAAQP,EAAwB;CAEtC,OAAO,eAAoB,GAAqC;EAC9D,IAAMQ,IAAQ,MAAMC,EAAQ,KAAK;EAUjC,OATeC,EAAW;GACxB,UAAU,MAAMC,EAAuB,CAAC,GAAGH,EAAK,QAAQ,CAAC;GACzD,OAAOZ,EACLgB,EAAQ,SAAS,QAAQ,IAAI,iBAAiBjB,CAChD;GACA,QAAQiB,EAAQ,UAAUb;GAC1B;EACF,CAEO,CAAA,CAAO,0BAA0B;CAC1C;AACF"}
|
|
@@ -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-D49NuNCn.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-DjEJi5Dv.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-WekGZj74.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-DESS0CMl.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-CUOyS07f.cjs");exports.FormBuilderView=e.t;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as FormBuilderView } from "../../../chunks/FormBuilderView-
|
|
1
|
+
export { t as FormBuilderView } from "../../../chunks/FormBuilderView-ClLnQ1e_.js";
|