@rytass/bpm-core-react
v0.4.1
Published
BPM approval workflow React components and views for the Rytass BPM stack. Ships AuthProvider, NotificationDrawer + bell widget, host-facing hooks (useBPMMember / useBPMLogout / useBPMRoutes), and full page-body views for inbox / instances / templates / f
Maintainers
Readme
@rytass/bpm-core-react
React provider / hook / view components for the Rytass BPM approval workflow stack.
This package composes @mezzanine-ui/react primitives with the BPM domain logic in @rytass/bpm-core-client and @rytass/bpm-core-shared so a consumer can wire up the full BPM admin UI by re-exporting page modules from their Next.js App Router.
Status
0.4.0 (breaking) — drops the bundled navigation shell. BPM views no longer wrap themselves in an <AppLayout> / Mezzanine <Navigation>; the host owns the layout chrome and mounts BPM views inside its own sidebar / top bar. New host-facing widgets ship in the root barrel: useBPMMember, useBPMLogout, <BPMNotificationBellButton />. See CHANGELOG.md for the migration walkthrough, and docs/integration-guide.md for a host integration recipe.
Install
pnpm add @rytass/bpm-core-react @rytass/bpm-core-client @rytass/bpm-core-shared @mezzanine-ui/react @mezzanine-ui/iconsPeer requirements: React 18+, Mezzanine UI 1.1+. Next.js is required only when consuming the pages/* subpath; framework-agnostic consumers can use views/* directly with their own router adapter.
Next.js + pnpm setup
If your host uses Next.js (15+) with pnpm strict mode, add the package to transpilePackages in next.config.js. Without this, Next's Turbopack cannot resolve transitive peer-dep imports such as @rytass/bpm-core-client/workflow from inside pnpm-isolated node_modules/.pnpm/... paths.
/** @type {import('next').NextConfig} */
module.exports = {
reactStrictMode: true,
// Include the sibling packages too — BPM views import from
// `@rytass/bpm-core-client/workflow`, `/organization`, `/template`,
// `/form` and re-export shared types from `@rytass/bpm-core-shared`.
// pnpm strict mode + Turbopack rejects the transitive resolution
// without each entry listed explicitly.
transpilePackages: [
'@rytass/bpm-core-react',
'@rytass/bpm-core-client',
'@rytass/bpm-core-shared',
],
};Usage
Host integration shape
BPM ships page bodies, providers, and widgets — never a full layout.
The host wires its own <Layout> / <Navigation> (or its own design
system equivalent), then drops BPM widgets and views into the slots:
// app/layout.tsx
import { BPMNextProviders } from '@rytass/bpm-core-react/next';
import { MyHostLayout } from './host-layout';
export default function RootLayout({ children }) {
return (
<html lang="zh-TW"><body>
<BPMNextProviders>
<MyHostLayout>{children}</MyHostLayout>
</BPMNextProviders>
</body></html>
);
}// app/host-layout.tsx
'use client';
import {
BPMNotificationBellButton,
useBPMLogout,
useBPMMember,
useBPMRoutes,
useRouterAdapter,
} from '@rytass/bpm-core-react';
export function MyHostLayout({ children }) {
const router = useRouterAdapter();
const routes = useBPMRoutes();
const member = useBPMMember();
const logout = useBPMLogout();
return (
<div className="grid grid-cols-[240px_1fr]">
<aside>
<h1>My Console</h1>
<ul>
<li><a href={routes.dashboard()}>工作台</a></li>
<li><a href={routes.inbox()}>我的待簽</a></li>
{/* …host's own nav items… */}
</ul>
<div className="flex items-center gap-2">
<span>{member?.name}</span>
<BPMNotificationBellButton />
<button onClick={() => logout()}>登出</button>
</div>
</aside>
<main>{children}</main>
</div>
);
}The page shims under pages/* are unchanged — they remain one-line
{ default, metadata } re-exports and are mounted under the host
layout exactly like any other Next.js page:
// app/inbox/page.tsx
export { default, metadata } from '@rytass/bpm-core-react/pages/inbox';For an end-to-end reference layout (with the original 4-group BPM nav
structure, admin-only filtering, and member display), see
apps/client/src/app/_components/host-layout.tsx in the BPMCore repo.
Framework-agnostic view
'use client';
import { useRouter, usePathname } from 'next/navigation';
import {
AuthProvider,
RouterAdapterProvider,
} from '@rytass/bpm-core-react';
import { LoginView } from '@rytass/bpm-core-react/views/login';
function MyLoginPage() {
const next = useRouter();
const pathname = usePathname();
return (
<RouterAdapterProvider value={{ pathname, push: next.push, replace: next.replace }}>
<AuthProvider>
<LoginView brandTitle="My BPM" />
</AuthProvider>
</RouterAdapterProvider>
);
}For SPA / Remix / Tanstack Router hosts, supply a RouterAdapter that bridges your router primitives.
Mounting BPM under a non-root URL prefix
If your host already owns the / namespace (Shuttle, an existing
admin console, etc.), wrap the BPM tree in <BPMRoutesProvider> and
override every internal cross-link to a prefixed path. The full
BPMRoutes contract has 19 entries — overriding only some leaves
the rest pointing at the unprefixed defaults, which usually 404s on
your host. Use a small factory to keep the override exhaustive:
import {
BPMRoutesProvider,
createDefaultBPMRoutes,
type BPMRoutes,
} from '@rytass/bpm-core-react/next';
function createPrefixedRoutes(prefix: string): BPMRoutes {
const trim = prefix.replace(/\/$/, '');
return {
...createDefaultBPMRoutes(), // safety net for any future routes
dashboard: () => `${trim}`,
inbox: () => `${trim}/inbox`,
sent: () => `${trim}/sent`,
cc: () => `${trim}/cc`,
search: () => `${trim}/search`,
delegations: () => `${trim}/delegations`,
notifications: () => `${trim}/notifications`,
caseDetail: (id) => `${trim}/instances/${id}`,
caseNew: (templateId) => templateId
? `${trim}/instances/new?templateId=${encodeURIComponent(templateId)}`
: `${trim}/instances/new`,
templates: () => `${trim}/templates`,
templateDesigner: (id) => `${trim}/templates/${id}/designer`,
templateVersions: (id) => `${trim}/templates/${id}/versions`,
templateCategories: () => `${trim}/templates/categories`,
forms: () => `${trim}/forms`,
formBuilder: (id) => `${trim}/forms/${id}/builder`,
notificationSettings: () => `${trim}/settings/notifications`,
adminOrgs: () => `${trim}/admin/orgs`,
adminUsers: () => `${trim}/admin/users`,
adminDelegations: () => `${trim}/admin/delegations`,
};
}
// In your layout:
<BPMRoutesProvider value={createPrefixedRoutes('/operations/approval')}>
<BPMNextProviders>{children}</BPMNextProviders>
</BPMRoutesProvider>createDefaultBPMRoutes() is the source of truth for the route shape;
spreading it first means new BPMCore versions that add routes never
silently regress your host.
License
MIT
