@masterteam/task-schedule
v0.0.86
Published
Backend-aware task-schedule package that renders **`@masterteam/gantt`** (`mt-gantt`). It is a thin wrapper: `@masterteam/gantt` owns all Gantt behavior (rendering, scheduling, WBS, baselines, critical path, resource view, columns, RTL/LTR, export, tas
Readme
@masterteam/task-schedule
Backend-aware task-schedule package that renders @masterteam/gantt
(mt-gantt). It is a thin wrapper: @masterteam/gantt owns all Gantt behavior
(rendering, scheduling, WBS, baselines, critical path, resource view, columns,
RTL/LTR, export, taskbar editing, row drag/drop, context menu); this package owns
backend fetch/mutation mapping, the schedule modes, the custom add/edit dialog,
the import drawer, and persisting edits.
Syncfusion Gantt has been fully removed. See
docs/task-schedule/masterteam-gantt-migration-architecture.mdanddocs/task-schedule/schedule-persistence-contract.md.
Local backend showcase
Run the standard showcase against a local Pplus5.BE instance and open
/task-schedule/21/690/edit. Authentication is handled by the
showcase's existing interceptor; no token input is required. See the
Schedule Runtime Frontend Guide
for the authoritative route, proxy, and authentication contract.
Public API
TaskSchedulecomponent (mt-task-schedule)TaskScheduleHeadercomponent (mt-task-schedule-header)TaskScheduleImportDialogcomponent (mt-task-schedule-import-dialog)TaskScheduleFetchService/TaskScheduleFetchDataAdapterTaskScheduleActionService/TaskScheduleActionDataAdapterTaskScheduleRequestErrorpreserves backenderrors.code/detailsTaskScheduleReadRequest,TaskScheduleProcessSubmitRequest, and their canonical module/operation/field typescreateTaskScheduleGanttConfig,taskScheduleNodeToActionRow,taskScheduleEditIntentFromRequest(gantt adapter helpers)TaskScheduleModelType,TaskScheduleContext,TaskScheduleResourceAssignment,TASK_SCHEDULE_DEFAULT_PDF_FONT
Inputs
context: object with requiredlevelIdandlevelDataId.modelType:'default' | 'edit' | 'baseline' | 'criticalPath' | 'resources' | 'unscheduled' | 'phaseGate' | 'custom'(aliases accepted)langCode:'en' | 'ar'— flips gantt direction + labelsdateFormat(defaultdd/MM/yyyy),height(default760px)allowTaskEditing— enables task CRUD, inline edits, taskbar/row/link gestures in Schedule mode. Keep it separate fromallowEditMode, which may be granted to baseline/import-only users. This input is fail-closed: omitted/undefined means read-only, and an authorized host must passtrue.pdfFonts:{ regular?; arabic?; size? }— Arabic font embedded for direct Arabic PDF export (falls back toTASK_SCHEDULE_DEFAULT_PDF_FONT)allowImport,allowSetBaseline,hasTasks,baselinePending- MPP controls:
mppRequestTimeoutMs,mppPreviewMaxRows(rendered rows only),mppPreviewMaxResponseBytes, and optionalmppKeepRawPayload
Outputs
toolbarAction,actionBegin,actionCompleted,baselineSet,actionError,loaded,loadErrorscheduleEvent— the@masterteam/gantttyped event firehose (GanttEvent)taskChanged— cancelableGanttTaskChangeEvent(UI deletes persist optimistically and reconcile on failure or pending approval)
Desktop Schedule mode
Schedule mode uses the native Gantt's edit.mode: 'both': double-click/typing
edits supported cells inline, Enter commits and moves to the same column on the
next row, while Add/Edit and link-management actions open the complete backend
form. The toolbar includes search, zoom-to-fit, fit columns, the column chooser,
fullscreen, and export. Connector drag, an inline predecessor column, and
searchable Add/Remove Dependency context actions cover nearby and distant links
in large schedules.
Only the first load of a new level scope shows a skeleton. Taskbar, progress,
dependency, row-drop, inline, and delete actions remain optimistic; successful
writes trigger a debounced background /schedule/read so backend-calculated
fields update without destroying the Gantt or visibly refreshing it. A compact
status rail reports saving/reconciliation/pending/failure. Schedule requests
carry noMessage: true, so the host's global toast interceptor stays silent.
Writes are serialized, stale in-flight reads are discarded, expansion is
preserved, and data-only reconciles reuse the Gantt config for 1,000-row plans.
Initial-load and reconcile failures expose an inline Retry action even for
read-only users; retry keeps the Gantt mounted and preserves existing plan state.
Undo/redo is a SERVER round-trip, not a local snapshot replay (the gantt's own
history stays off via undoLimit: 0; Ctrl+Z and the toolbar buttons drive the
wrapper's stack through the gantt's undoDelegate). Every gesture that lands
on the server pushes one entry on success — inline cell edits of every column
(custom and manual included), taskbar drags, indent/outdent/row drops, block
moves, summary-progress distributions, creates and deletes — and the executor
reverses it through the same wire builders the forward write used. Undoing a
CREATE deletes the row behind a confirmation (it was never published); undoing
a DELETE re-creates the captured subtree parent-first and re-links surviving
successors. History is draft-scoped: publish, discard, scope switches and the
draft-creating id-space swap clear it, while ordinary reconcile reads only
prune rows that vanished.
Custom add/edit popup
Full-record editing goes through the existing TaskScheduleDialog (ClientForm),
not the Gantt built-in dialog. The wrapper listens to the cancelable
editRequest, opens the correct mode/parent, and enables silentMessages so
transport errors appear inside the form rather than as toasts. Fast supported
fields edit inline; backend-calculated fields and calculated custom properties
remain read-only.
Fetch adapter (TaskScheduleFetchService)
load(modelType, context)importTasks(context, file)exportTasks(context)loadBaselineSnapshot(context, version?)
All mixed modes, including phase-gate mode, read through
POST /api/levels/{levelId}/{levelDataId}/schedule/read. Each load fetches the
effective calendar in parallel once from
GET /api/levels/{levelId}/{levelDataId}/schedule/working-hours. The adapter
maps the documented DTO to TaskScheduleDataResult.workingHours, and the Gantt
uses its working weekdays, daily minute range, and holiday dates for taskbar and
dependency date math. It does not infer a regional work week or read calendar
settings from browser storage. The adapter maps
catalog.properties[].normalizedKey to the corresponding storage key in
record.values, including the backend calculation metadata
(plannedProgress, rollup/float/variance/early/late values, source/coverage
values, and the three editability flags).
Resource allocation reads use the backend's authoritative
resourceAssignments: [{resourceId, unit}] shape (Query exposes the same array
under values.resources). Baseline/current snapshots follow the matching
resourceAssignments / currentResourceAssignments fields. Legacy
resources: string[] remains an explicit compatibility input and normalizes to
100% assignments; allocation units remain visible in the resource column and
survive every silent reconcile. Timeline labels resolve the live Gantt
assignments against the configured resource roster, so an inline reassignment
or clear updates names and units immediately instead of waiting for that
reconcile or exposing resource ids.
MPP preview parsing retains the complete normalized task tree for Replace or
Append while rendering at most mppPreviewMaxRows. Search, select-all, and the
apply payload operate on the complete model, so a 1,000+ row import is not
truncated by the UI window. A non-empty response above
mppPreviewMaxResponseBytes fails inline and is never treated as already
applied; only an empty response or an explicit backend applied/imported marker
uses the direct-apply path.
Action adapter (TaskScheduleActionService)
updateTask(context, taskId, payload)deleteTask(context, taskId, taskType?)updateBulkTasks(context, payload)updateOrder(context, payload, taskType?)updateProgress(context, taskId, payload)applyImportedTasks(context, payload)setBaseline(context, payload?)
Single-item resource fields are written through process-submit as
{resourceId, unit} arrays. Planned/actual schedule dates are date-only in the
UI but serialize as wall-date-preserving midnight UTC ISO values ending in Z,
which matches the backend process parser; explicit null clears remain null.
Usage
import { Component, signal } from "@angular/core";
import { TaskSchedule, TaskScheduleModelType } from "@masterteam/task-schedule";
@Component({
standalone: true,
imports: [TaskSchedule],
selector: "app-task-schedule-host",
template: `
<mt-task-schedule
[context]="{
levelId: 12,
levelDataId: 18,
modelType: modelType(),
langCode: 'en',
allowTaskEditing: modelType() === 'edit',
}"
/>
`,
})
export class TaskScheduleHostComponent {
readonly modelType = signal<TaskScheduleModelType>("default");
}Backend routes used (unchanged by the migration):
- Schedule reads:
POST /api/levels/{levelId}/{levelDataId}/schedule/read - Effective working calendar:
GET /api/levels/{levelId}/{levelDataId}/schedule/working-hours - Baseline snapshot:
GET /api/levels/{levelId}/{levelDataId}/schedule/baselines/latest - Custom view columns:
GET /api/schedulemanager/{levelId}/schedule/{customViewId} - Mutations:
POST /api/process-submit(single-item create/update/delete/progress/dependency/taskbar changes),PUT .../schedule/bulk-update(atomic mixed-type row parent+order changes),PUT .../schedule/reorder(standalone reorder consumers) - Baseline snapshots:
POST .../schedule/baselines - Import/export:
GET/POST .../schedule/mpp
Backend-owned fields (isSummary, duration, planned progress, and baseline
fields) are removed from normal writes. Summary progress and dates are guarded
by isProgressEditable, arePlannedDatesEditable, and
areActualDatesEditable.
Duration is backend-owned but still EDITABLE, and the two are easy to
confuse. Its value is never sent - writableTaskScheduleFields strips it - but
typing one is how a planner says "make this N days": the gantt re-derives the
finish from start + duration and the request carries that finish, from which
the backend recalculates the duration in WORKING days. So a duration cell is
read-only exactly where the planned pair is, and nowhere else: on summary rows,
and on rows whose arePlannedDatesEditable is false (both would otherwise send
a submit with an empty field list). That rule lives in one place,
core/task-schedule-cell-edit.ts, next to its tests. Do not add duration to
a "calculated, never editable" list: it has an inverse, unlike Total Float. The wrapper preserves backend summary values in the
Gantt and silently reconciles the unified read snapshot after mutations so the
central calculation pass remains authoritative without a refresh screen. See
the persistence contract doc for the full table.
Peer dependency
@masterteam/gantt (workspace). No Syncfusion dependency remains.
