Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
209 lines
9.4 KiB
Markdown
209 lines
9.4 KiB
Markdown
# PRD: Scheduling Context Facade (Agent Schedule + Time Entry)
|
|
|
|
- Slug: `scheduling-context-facade`
|
|
- Date: `2026-02-05`
|
|
- Status: Draft
|
|
|
|
## Summary
|
|
|
|
Restore the Agent Schedule Drawer and Add Time Entry features that were stubbed during the Jan 2026 modularization refactor. Use a React Context facade in `@alga-psa/ui` to avoid cross-package imports, with real implementations provided from the composition layer.
|
|
|
|
## Problem
|
|
|
|
During the `c43321e4c` refactor ("continue modularization of actions and components", Jan 20 2026), scheduling features were removed from consumer packages (tickets, clients, projects) and consolidated into `@alga-psa/scheduling`. The old imports were replaced with stubs:
|
|
|
|
1. **Agent Schedule Drawer** — `AgentScheduleDrawer.tsx` in tickets shows "Agent schedule view is now owned by Scheduling." instead of a calendar. Triggered from:
|
|
- Ticket detail → click agent name
|
|
- Interaction detail → click user name
|
|
|
|
2. **Add Time Entry** — Three handlers show `toast('Time entry is managed in Scheduling.')` instead of opening the time entry dialog. Triggered from:
|
|
- Ticket detail → "Add Time Entry" button
|
|
- Interaction detail → "Add Time Entry" button
|
|
- Project task → "Add Time Entry" button
|
|
|
|
## Goals
|
|
|
|
1. **Restore Agent Schedule Drawer** — clicking an agent/user name opens a single-agent calendar in a drawer
|
|
2. **Restore Add Time Entry** — clicking "Add Time Entry" opens the TimeEntryDialog with correct work-item context
|
|
3. **No cross-package dependencies** — use a React Context facade pattern; consumer packages (tickets, clients, projects) must NOT import from `@alga-psa/scheduling`
|
|
4. **Graceful fallback** — if the context provider is absent, stubs degrade to the current alert/toast behavior
|
|
|
|
## Non-Goals
|
|
|
|
- Full-page schedule view in drawers (sidebar, multi-technician comparison, appointment requests)
|
|
- Modifying the existing `ScheduleCalendar` component (it stays as-is for the scheduling page)
|
|
- Timer feature changes (timer state is passed through, not modified)
|
|
- Editing the TimeEntryDialog component itself
|
|
|
|
## Architecture
|
|
|
|
### Pattern: React Context Facade
|
|
|
|
```
|
|
@alga-psa/ui — defines SchedulingContext (interface + hook + no-op default)
|
|
@alga-psa/scheduling — provides AgentScheduleView + timeEntryLauncher (real implementations)
|
|
@alga-psa/msp-composition — creates MspSchedulingProvider (imports from scheduling, provides context)
|
|
server pages — wraps relevant pages with MspSchedulingProvider
|
|
consumer components — call useSchedulingCallbacks() to get renderAgentSchedule / launchTimeEntry
|
|
```
|
|
|
|
**Why this pattern:**
|
|
- All consumer packages already depend on `@alga-psa/ui` — no new deps needed
|
|
- Follows the established DI pattern (like `renderContactDetails`, `renderClientDetails`)
|
|
- The composition layer (`msp-composition`) already exists for tickets
|
|
- Context avoids deep prop threading through 3-4 layers of components
|
|
|
|
### Context Interface
|
|
|
|
```ts
|
|
interface SchedulingCallbacks {
|
|
renderAgentSchedule: (agentId: string) => React.ReactNode;
|
|
launchTimeEntry: (params: {
|
|
openDrawer: OpenDrawerFn;
|
|
closeDrawer: () => void;
|
|
context: TimeEntryWorkItemContext;
|
|
onComplete?: () => void;
|
|
}) => Promise<void>;
|
|
}
|
|
```
|
|
|
|
## Users and Primary Flows
|
|
|
|
### Flow 1: View Agent Schedule (from Ticket)
|
|
1. User opens a ticket detail page
|
|
2. Clicks on an agent name in the ticket properties
|
|
3. Drawer opens with the agent's weekly calendar
|
|
4. Events are color-coded by type (ticket, project task, interaction, etc.)
|
|
5. User can click events to view details via EntryPopup
|
|
|
|
### Flow 2: View Agent Schedule (from Interaction)
|
|
1. User opens an interaction detail
|
|
2. Clicks on the assigned user name
|
|
3. Same drawer experience as Flow 1
|
|
|
|
### Flow 3: Add Time Entry (from Ticket)
|
|
1. User clicks "Add Time Entry" on a ticket
|
|
2. System fetches current time period and creates/fetches time sheet
|
|
3. TimeEntryDialog opens in drawer mode with ticket context pre-filled
|
|
4. User enters time, selects service, saves
|
|
5. Timer resets (if running)
|
|
|
|
### Flow 4: Add Time Entry (from Interaction)
|
|
1. User clicks "Add Time Entry" on an interaction
|
|
2. Same dialog flow; pre-filled with interaction type, client, and start/end times from interaction
|
|
|
|
### Flow 5: Add Time Entry (from Project Task)
|
|
1. User clicks "Add Time Entry" on a project task form
|
|
2. Same dialog flow; pre-filled with project/phase/task names and service if configured
|
|
|
|
## UX / UI Notes
|
|
|
|
### Agent Schedule Drawer
|
|
- Default view: **week** (fits drawer width better than month)
|
|
- Shows: day/week/month view toggle, navigable date range
|
|
- Events: color-coded by work item type, clickable for details
|
|
- Auto-scrolls to 8 AM on open
|
|
- Respects permissions: `user_schedule:read` (own), `user_schedule:read:all` (others)
|
|
|
|
### Time Entry Dialog
|
|
- Opens via `openDrawer()` in `inDrawer: true` mode
|
|
- Pre-fills work item info, date, default times
|
|
- Handles: service selection, duration, notes, billability
|
|
- Shows toast on save success/failure
|
|
- Calls `onComplete` callback for timer reset
|
|
|
|
## Data / API / Integrations
|
|
|
|
### Existing Actions (in @alga-psa/scheduling)
|
|
|
|
| Action | Location | Purpose |
|
|
|--------|----------|---------|
|
|
| `getScheduleEntries(start, end, techIds)` | scheduleActions.ts | Fetch events for calendar |
|
|
| `getCurrentUser()` | users/actions | Get current user for time entry |
|
|
| `getCurrentTimePeriod()` | timePeriodsActions.ts | Get active billing period |
|
|
| `fetchOrCreateTimeSheet(userId, periodId)` | timeSheetActions.ts | Get/create time sheet |
|
|
| `saveTimeEntry(entry)` | timeEntryCrudActions.ts | Persist time entry |
|
|
|
|
### Existing Components (in @alga-psa/scheduling)
|
|
|
|
| Component | Purpose |
|
|
|-----------|---------|
|
|
| `DynamicBigCalendar` | Dynamic import of React Big Calendar |
|
|
| `EntryPopup` | Event creation/editing popup |
|
|
| `CalendarStyleProvider` | Calendar CSS-in-JS styling |
|
|
| `TimeEntryDialog` | Full time entry form (dialog or drawer mode) |
|
|
| `TimeEntryProvider` | Context for time entry state management |
|
|
|
|
### New Type (in @alga-psa/types)
|
|
|
|
```ts
|
|
interface TimeEntryWorkItemContext {
|
|
workItemId: string;
|
|
workItemType: WorkItemType;
|
|
workItemName: string;
|
|
ticketNumber?: string;
|
|
interactionType?: string;
|
|
clientName?: string | null;
|
|
startTime?: Date;
|
|
endTime?: Date;
|
|
projectName?: string;
|
|
phaseName?: string;
|
|
taskName?: string;
|
|
serviceId?: string | null;
|
|
serviceName?: string | null;
|
|
elapsedTime?: number;
|
|
timeDescription?: string;
|
|
}
|
|
```
|
|
|
|
## Security / Permissions
|
|
|
|
- Agent schedule: respects `user_schedule:read` (own only) and `user_schedule:read:all` (view others)
|
|
- Time entry: requires active time period; shows toast/dialog if none exists
|
|
- Private schedule entries: shown as "Busy" for non-owning users
|
|
|
|
## Files to Create
|
|
|
|
| File | Package | Purpose |
|
|
|------|---------|---------|
|
|
| `packages/ui/src/context/SchedulingContext.tsx` | ui | Context definition, hook, no-op default |
|
|
| `packages/scheduling/src/components/schedule/AgentScheduleView.tsx` | scheduling | Single-agent calendar drawer |
|
|
| `packages/scheduling/src/lib/timeEntryLauncher.tsx` | scheduling | Time entry orchestration helper |
|
|
| `packages/msp-composition/src/scheduling/MspSchedulingProvider.tsx` | msp-composition | Real provider wrapping children |
|
|
|
|
## Files to Modify
|
|
|
|
| File | Change |
|
|
|------|--------|
|
|
| `packages/types/src/interfaces/scheduling.interfaces.ts` | Add `TimeEntryWorkItemContext` |
|
|
| `packages/ui/src/context/index.ts` | Re-export SchedulingContext |
|
|
| `packages/msp-composition/package.json` | Add `@alga-psa/scheduling` dep |
|
|
| `server/src/app/msp/tickets/[id]/page.tsx` | Wrap with MspSchedulingProvider |
|
|
| `server/src/app/msp/contacts/[id]/page.tsx` | Wrap with MspSchedulingProvider |
|
|
| `server/src/app/msp/contacts/[id]/activity/page.tsx` | Wrap with MspSchedulingProvider |
|
|
| `server/src/app/msp/projects/[id]/page.tsx` | Wrap with MspSchedulingProvider |
|
|
| `packages/tickets/.../AgentScheduleDrawer.tsx` | Stub → useSchedulingCallbacks() |
|
|
| `packages/tickets/.../TicketDetails.tsx` | Time entry stub → useSchedulingCallbacks() |
|
|
| `packages/clients/.../InteractionDetails.tsx` | Time entry stub → useSchedulingCallbacks() |
|
|
| `packages/projects/.../TaskForm.tsx` | Time entry stub → useSchedulingCallbacks() |
|
|
|
|
## Reference Implementations
|
|
|
|
- **Old AgentScheduleDrawer**: `git show c6e3612d2 -- packages/tickets/src/components/ticket/AgentScheduleDrawer.tsx`
|
|
- **ScheduleCalendar**: `packages/scheduling/src/components/schedule/ScheduleCalendar.tsx`
|
|
- **Old time entry handlers**: `git log --all -p -S "TimeEntryDialog" -- packages/tickets/src/components/ticket/TicketDetails.tsx`
|
|
- **TimeEntryDialog**: `packages/scheduling/src/components/time-management/time-entry/time-sheet/TimeEntryDialog.tsx`
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] Clicking agent name from ticket opens agent schedule calendar in drawer
|
|
- [ ] Clicking user name from interaction opens agent schedule calendar in drawer
|
|
- [ ] Calendar shows color-coded events for the agent, default week view
|
|
- [ ] Clicking "Add Time Entry" from ticket opens TimeEntryDialog with ticket context
|
|
- [ ] Clicking "Add Time Entry" from interaction opens TimeEntryDialog with interaction context
|
|
- [ ] Clicking "Add Time Entry" from project task opens TimeEntryDialog with task context
|
|
- [ ] Time entry save persists correctly and calls onComplete callback
|
|
- [ ] Missing time period shows appropriate error feedback
|
|
- [ ] Without MspSchedulingProvider, stubs degrade gracefully (alert/toast)
|
|
- [ ] No new cross-package dependencies added to tickets, clients, or projects
|