react-roster-shift-board/ is a React 19 / TypeScript 5 / Vite 7 single-page application that ships inside the AL extension. At build time it produces a single bundle and CSS file directly into BC-Human-Resource-AL/scripts/assets/. At runtime it loads inside a BC iframe via the standard Control Add-in mechanism.
Two screens, one app
| Screen | Status | Purpose |
|---|
| Roster Scheduler (active) | Mounted in App.tsx | The Visual Roster Board — drag-and-drop assignment of employees to shifts on a Syncfusion Schedule calendar |
| Project Gantt (built but not mounted) | Fully implemented under components/project-gantt-chart/ | A Syncfusion Gantt chart for BC project tasks and planning lines. Wired up to data services but not currently exposed via App.tsx |
The Gantt screen is not rendered in the current build. The code is retained for partner-specific deployments that re-enable it.
What it does for the user
Open the Visual Roster Board page in BC (page=70003175). The page loads an iframe; the React app inside it:
- Fetches the list of employees and the list of shift configurations marked
Add to Roster = true.
- Fetches roster entries for the visible date window (default: 3 months centred on today).
- Renders a two-pane layout — a searchable employee sidebar on the left, a Syncfusion
ScheduleComponent grouped by shift on the right.
- Lets the user drag an employee onto a shift slot. The app calls back into BC to insert an
Employee Roster HAC row.
- Re-renders the updated assignment in place.
There is no separate “save” button — every drag-drop is committed immediately.
How it talks to BC
Strictly through the Control Add-in extensibility bridge:
| Direction | Mechanism |
|---|
| React → BC (outbound) | Microsoft.Dynamics.NAV.InvokeExtensibilityMethod(eventName, parameter) invokes a trigger on the AL page hosting the control add-in |
| BC → React (inbound) | The AL trigger calls CurrPage.Control.ProcedureName(JsonArray), which maps to a named window.<name> callback in the React app |
No HTTP, no fetch, no OData from the React side. This keeps audit, permissions and tenant-isolation uniform with BC’s normal access control.
Stack
| Layer | Tooling |
|---|
| Build | Vite 7 with entryFileNames: 'js/base.js', sourcemaps on, minify off |
| Language | TypeScript 5 |
| UI runtime | React 19 + Redux Toolkit |
| UI components | Syncfusion EJ2 — Schedule, Gantt, Grid, Dialog, Splitter, Toast, DateRangePicker, and more |
| Icons | FontAwesome free |
| BC bridge | The Microsoft-provided window.Microsoft.Dynamics.NAV global, typed via src/Microsoft.d.ts |
License note for AppSource
Syncfusion EJ2 is commercial. The license key lives in react-roster-shift-board/syncfusion-license.txt. registerLicense(key) from @syncfusion/ej2-base must be called at startup before any Syncfusion component renders.
Confirm this call is present in main.tsx or App.tsx before final AppSource submission, or the production build will display Syncfusion’s evaluation banner.
Project layout
react-roster-shift-board/
├── index.html Vite entry
├── package.json Dependencies
├── vite.config.ts Build output → ../BC-Human-Resource-AL/scripts/assets
├── tsconfig.json
├── syncfusion-license.txt Commercial license key
└── src/
├── main.tsx Registers window.InitControls; bootstraps Provider + App
├── App.tsx Currently mounts <AutoLoad/> and <RosterScheduler/>
├── bc-env.ts isBC detection
├── Microsoft.d.ts Ambient declaration for window.Microsoft.Dynamics.NAV
├── components/ Roster scheduler + Gantt + dialogs
├── services/dataService.ts Sole data layer
├── store/ Redux: roster + Gantt + resources slices
├── types/ Domain types (IResource, IShiftCode, IRosterEntry, …)
└── utils/ Utility helpers
Build and deploy
cd react-roster-shift-board
npm ci
npm run build # writes to ../BC-Human-Resource-AL/scripts/assets/{js/base.js, app.css}
Then build the AL extension as normal. The bundle is part of the .app artifact — no separate frontend hosting needed.