226 lines
30 KiB
Markdown
226 lines
30 KiB
Markdown
# PROMPT.md – nick-tracker (AutoScheduler GTD System)
|
||
|
||
## Objective
|
||
|
||
Build **AutoScheduler**, a self-hosted web application implementing the Getting Things Done (GTD) methodology with automatic calendar scheduling. The system ingests tasks from multiple sources (manual capture, email via IMAP/Microsoft Graph, read-only ConnectWise Manage sync), processes them through GTD workflows (Inbox → Next Actions/Projects/Waiting For/Someday-Maybe/Tickler), and automatically schedules actionable tasks into available calendar slots from CalDAV-compatible sources (Nextcloud, Google Calendar, Outlook). The scheduling engine respects user-defined working hours, context constraints (@desk, @phone, @errand, @homelab, @anywhere), deadlines, and manual priorities, batching similar tasks when possible and automatically rescheduling displaced tasks when calendar conflicts arise. A React SPA with interactive week-view calendar supports drag-and-drop manual overrides and task locking. Weekly Review interface auto-schedules and presents comprehensive system review. Real-time notifications via WebSocket, email, and optional webhook inform users of schedule changes and follow-up reminders.
|
||
|
||
**Core Value Proposition:** Unified GTD task management across work (ConnectWise) and personal domains (Homelab, Daily Routines, House, Professional Development) with intelligent automatic scheduling that respects context, time constraints, and priorities, eliminating manual time-blocking while maintaining GTD methodology integrity.
|
||
|
||
---
|
||
|
||
## Application Type
|
||
|
||
**web** – Self-hosted web application with React SPA frontend and NestJS backend API, accessed via browser. Containerized deployment via Docker Compose for user-managed infrastructure. No native desktop or mobile apps required; responsive web UI serves all devices.
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
### Core Components
|
||
|
||
1. **Frontend (React SPA)**
|
||
- Interactive week-view calendar with drag-and-drop task scheduling
|
||
- GTD workflow interfaces: Inbox processing, Next Actions, Projects, Weekly Review
|
||
- Real-time updates via WebSocket for automatic rescheduling notifications
|
||
- Task capture quick-add form and external REST API endpoint
|
||
- Settings UI for calendar connections, email/ConnectWise integration, working hours
|
||
|
||
2. **Backend (NestJS API)**
|
||
- REST API for CRUD operations on tasks, projects, inbox items, connections
|
||
- WebSocket gateway for real-time notifications and schedule updates
|
||
- Scheduled jobs (cron) for: email polling, ConnectWise sync, calendar sync, Tickler activation, Weekly Review scheduling
|
||
- Task scheduling engine with constraint satisfaction and conflict resolution
|
||
- Integration services: CalDAV client, Microsoft Graph client, Google Calendar API, ConnectWise Manage API, IMAP/SMTP
|
||
|
||
3. **Database (PostgreSQL)**
|
||
- Relational schema: Users, InboxItems, Tasks, Projects, CalendarEvents, ReschedulingEvents, Notifications
|
||
- Foreign keys enforce referential integrity; indexes optimize scheduler queries
|
||
- JSON columns for flexible metadata (workingHours, notificationPreferences, sourceMetadata)
|
||
|
||
4. **Message Queue (Redis + Bull)**
|
||
- Asynchronous job processing for long-running syncs (ConnectWise, email, calendar)
|
||
- Rate limiting and retry logic for external API calls
|
||
- Queue-based scheduling engine to prevent concurrent conflicts
|
||
|
||
5. **Container Orchestration (Docker Compose)**
|
||
- Services: backend (NestJS), database (PostgreSQL), cache (Redis), frontend (nginx serving static build)
|
||
- Volumes for PostgreSQL persistence, file uploads (reference materials)
|
||
- Health checks, restart policies, environment-based configuration
|
||
|
||
### Interfaces
|
||
|
||
- **REST API:** Full CRUD for all entities, connection management, manual scheduling overrides, Weekly Review operations
|
||
- **WebSocket:** Real-time push notifications for rescheduling events, Waiting For follow-ups, Tickler activations
|
||
- **External Integrations:** CalDAV (Nextcloud/generic), Microsoft Graph (Outlook/Google Calendar via OAuth), ConnectWise Manage REST API, IMAP/SMTP
|
||
|
||
### Deployment
|
||
|
||
**self_hosted** – Docker Compose stack deployed to user's infrastructure (home server, VPS). Users manage environment variables (API keys, DB credentials), backups, and reverse proxy (Traefik/nginx) for HTTPS. Optional OIDC authentication for enterprise users; local account authentication default.
|
||
|
||
---
|
||
|
||
## Tech Stack
|
||
|
||
- **Language:** TypeScript (strict mode, ES2022 target)
|
||
- **Runtime:** Node.js 20 LTS
|
||
- **Backend Framework:** NestJS 10 (modules, dependency injection, guards, interceptors)
|
||
- **Frontend Framework:** React 18 (functional components, hooks) + Vite 5 (build tool)
|
||
- **Database:** PostgreSQL 16 (relational, JSONB support)
|
||
- **ORM:** TypeORM 0.3 (Active Record pattern, migrations, query builder)
|
||
- **State Management:** Zustand (lightweight, minimal boilerplate)
|
||
- **Data Fetching:** TanStack Query (React Query v5) for server state caching
|
||
- **Routing:** React Router v6 (nested routes, loaders)
|
||
- **Calendar UI:** FullCalendar (drag-drop, resource timeline, week view)
|
||
- **Drag-and-Drop:** react-beautiful-dnd (accessible drag-drop for task lists)
|
||
- **WebSocket:** socket.io (NestJS WS adapter, auto-reconnect)
|
||
- **Job Queue:** Bull (Redis-backed task queues, cron scheduling)
|
||
- **Cache:** Redis 7 (Bull queue storage, session cache)
|
||
- **Authentication:** Passport.js (JWT strategy, optional OIDC)
|
||
- **Validation:** class-validator, class-transformer (backend DTOs), Zod (frontend forms)
|
||
- **Styling:** TailwindCSS 3 + Radix UI (accessible component primitives)
|
||
- **Date Handling:** date-fns (timezone-aware, immutable)
|
||
- **Calendar Protocols:** ical.js (iCal parsing), node-caldav (CalDAV client)
|
||
- **External APIs:** @microsoft/microsoft-graph-client (Outlook/Graph), axios (ConnectWise REST), imap (email polling)
|
||
- **Testing:** Jest (backend unit/integration), Vitest (frontend unit), Supertest (API e2e)
|
||
- **Containerization:** Docker, Docker Compose v2
|
||
- **Monorepo:** pnpm workspaces (shared types package)
|
||
- **Build Tools:** tsup (backend bundling), Vite (frontend HMR and build)
|
||
|
||
---
|
||
|
||
## Phases & Completion Criteria
|
||
|
||
### Phase 1: Foundation
|
||
|
||
**Goal:** Establish project structure, core infrastructure, authentication, and basic database schema. Prove Docker Compose stack runs and REST API accepts requests.
|
||
|
||
#### Completion Criteria
|
||
|
||
- [ ] **Monorepo initialized** with pnpm workspaces: `packages/backend`, `packages/frontend`, `packages/shared-types`. Root `package.json` has workspace scripts: `pnpm dev`, `pnpm build`, `pnpm test`. Verify: `pnpm install && pnpm -r list` shows all three workspaces.
|
||
- [ ] **Backend NestJS app** scaffolded with modules: `AuthModule`, `UsersModule`, `TasksModule`, `ProjectsModule`, `InboxModule`. Verify: `pnpm --filter backend build` succeeds, `dist/` contains compiled JS.
|
||
- [ ] **Frontend React app** created with Vite, TailwindCSS configured, Radix UI installed. Basic route structure: `/login`, `/inbox`, `/calendar`, `/projects`, `/settings`. Verify: `pnpm --filter frontend dev` starts dev server on port 5173, homepage renders.
|
||
- [ ] **PostgreSQL database** schema created via TypeORM migrations: `User`, `InboxItem`, `Task`, `Project`, `CalendarConnection`, `CalendarEvent`, `ConnectWiseConnection`, `EmailConnection`, `ReferenceMaterial`, `ReschedulingEvent`, `Notification` entities with all fields and relationships from spec. Verify: `pnpm --filter backend migration:run` succeeds, `psql -d autoscheduler -c "\dt"` lists 11 tables.
|
||
- [ ] **Docker Compose** stack configured: `backend`, `postgres`, `redis`, `frontend` services. Backend exposes port 3000, frontend port 80, PostgreSQL port 5432 (internal only). Environment variables loaded from `.env` file. Verify: `docker compose up -d && docker compose ps` shows all services healthy, `curl http://localhost:3000/health` returns `{"status":"ok"}`.
|
||
- [ ] **Authentication implemented:** JWT-based auth with Passport.js. `/api/v1/auth/register`, `/api/v1/auth/login`, `/api/v1/auth/refresh` endpoints functional. Password hashing with bcrypt (12 rounds). JWT guard protects all routes except auth endpoints. Verify: `curl -X POST http://localhost:3000/api/v1/auth/register -d '{"email":"test@example.com","password":"SecurePass123!","name":"Test User","timezone":"America/New_York"}' -H "Content-Type: application/json"` returns 201 with JWT, subsequent `curl -H "Authorization: Bearer <token>" http://localhost:3000/api/v1/users/me` returns user object.
|
||
- [ ] **Frontend authentication flow:** Login form posts to `/api/v1/auth/login`, stores JWT in memory (Zustand), redirects to `/inbox`. Protected routes require auth token, redirect to `/login` if missing. Axios interceptor adds `Authorization` header. Verify: Manual test in browser: register user, log in, see redirect to inbox, refresh page maintains session.
|
||
- [ ] **Basic error handling:** Global exception filter in NestJS logs errors, returns standardized JSON error responses (status, message, timestamp). Frontend axios interceptor catches 401, clears token, redirects to login. Verify: `curl http://localhost:3000/api/v1/nonexistent` returns 404 JSON, `curl -H "Authorization: Bearer invalid" http://localhost:3000/api/v1/users/me` returns 401 JSON.
|
||
- [ ] **Logging configured:** NestJS Logger configured with timestamps, log levels (dev: debug, prod: info). Winston logger optional enhancement but basic Logger functional. Verify: Backend console shows structured logs on startup and per request.
|
||
- [ ] **Type safety enforced:** `shared-types` package exports DTOs (CreateTaskDto, TaskResponseDto, etc.) used by both backend validators and frontend Zod schemas. Verify: Change a field type in `shared-types`, run `pnpm build`, see TypeScript errors in both frontend and backend if mismatched.
|
||
- [ ] **Health checks:** `/health` endpoint returns database connection status, Redis connection status. Docker Compose health checks configured for backend (GET /health every 30s). Verify: `curl http://localhost:3000/health` returns `{"status":"ok","database":"connected","redis":"connected"}`, `docker compose ps` shows backend as `healthy`.
|
||
|
||
**Phase 1 Verification Command:**
|
||
|
||
```bash
|
||
pnpm install && pnpm build && docker compose up -d && sleep 10 && \
|
||
curl -f http://localhost:3000/health && \
|
||
curl -X POST http://localhost:3000/api/v1/auth/register \
|
||
-d '{"email":"verify@test.com","password":"Test123!","name":"Verify User","timezone":"UTC"}' \
|
||
-H "Content-Type: application/json" | grep -q "token" && \
|
||
echo "✓ Phase 1 Complete"
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 2: Core Features
|
||
|
||
**Goal:** Implement GTD workflows (Inbox capture and processing, Next Actions, Projects, Someday/Maybe, Waiting For, Tickler), basic scheduling engine, and interactive calendar week view with drag-and-drop.
|
||
|
||
#### Completion Criteria
|
||
|
||
- [ ] **Inbox capture endpoints:** `POST /api/v1/inbox` creates unprocessed InboxItem with source=MANUAL. Fields: content (text), source (enum), sourceMetadata (JSON). Returns created item with ID. Verify: `curl -X POST -H "Authorization: Bearer <token>" http://localhost:3000/api/v1/inbox -d '{"content":"Test inbox item"}' -H "Content-Type: application/json"` returns 201 with `{"id":"uuid","content":"Test inbox item","processed":false}`.
|
||
- [ ] **Inbox list and processing:** `GET /api/v1/inbox` returns all unprocessed items for authenticated user. `POST /api/v1/inbox/:id/process` accepts action payload (e.g., `{"action":"task","context":"@desk","domain":"work","title":"Do thing"}`) and converts inbox item to Task, Project, or marks as Trash, setting `processed=true`. Verify: Create inbox item, process to task, GET inbox returns empty array, `GET /api/v1/tasks` includes new task.
|
||
- [ ] **Task CRUD:** Full CRUD endpoints for Tasks with validation: `POST /api/v1/tasks` (required: title, domain; optional: context, priority, dueDate, estimatedDuration, projectId), `GET /api/v1/tasks` (with filters: status, context, domain), `PATCH /api/v1/tasks/:id` (partial updates), `DELETE /api/v1/tasks/:id` (soft delete or hard delete based on policy). Task status enum: NEXT_ACTION, WAITING_FOR, SOMEDAY_MAYBE, TICKLER, COMPLETED. Context enum: DESK, PHONE, ERRAND, HOMELAB, ANYWHERE. Domain enum: WORK, HOMELAB, DAILY_ROUTINES, HOUSE, PROFESSIONAL_DEVELOPMENT. Verify: Create task with all fields, GET task by ID returns full object, PATCH updates priority, DELETE removes task.
|
||
- [ ] **Task filtering views:** Separate endpoints or query params for GTD views: `GET /api/v1/tasks?status=NEXT_ACTION` (Next Actions), `GET /api/v1/tasks?status=WAITING_FOR` (Waiting For items with follow-up dates), `GET /api/v1/tasks?status=SOMEDAY_MAYBE` (Someday/Maybe list), `GET /api/v1/tasks?status=TICKLER` (future Tickler items). Verify: Create 5 tasks with different statuses, each GET returns only matching tasks.
|
||
- [ ] **Project CRUD:** Full CRUD for Projects: `POST /api/v1/projects` (required: name, domain; optional: description, desiredOutcome, connectwiseProjectId), `GET /api/v1/projects` (filter by status: active, on-hold, completed, domain), `PATCH /api/v1/projects/:id`, `DELETE /api/v1/projects/:id`. Verify: Create project, assign task to project via `PATCH /api/v1/tasks/:id` with `projectId`, `GET /api/v1/projects/:id/tasks` returns associated tasks.
|
||
- [ ] **Reference material attachments:** `POST /api/v1/projects/:id/reference` accepts multipart file upload or URL/text reference. Creates ReferenceMaterial entity linked to project. File uploads stored in Docker volume, path saved in DB. `GET /api/v1/projects/:id` includes references array. Verify: Upload PDF to project, GET project returns reference with file path, file accessible at `/uploads/:filename` route.
|
||
- [ ] **Waiting For follow-up dates:** Tasks with status=WAITING_FOR accept `followUpDate` timestamp. Scheduled job (cron) runs daily, identifies Waiting For items where `followUpDate <= NOW()`, creates Notification for user. Verify: Create Waiting For task with followUpDate tomorrow, manually trigger cron (`POST /api/v1/debug/trigger-cron`), next day notification appears in `GET /api/v1/notifications`.
|
||
- [ ] **Tickler activation:** Tasks with status=TICKLER have `ticklerDate`. Daily cron job checks for `ticklerDate <= NOW()`, converts to InboxItem with source=TICKLER, status=PROCESSED, creates notification. Verify: Create Tickler task for tomorrow, run cron job manually, InboxItem appears with content from task title, notification created.
|
||
- [ ] **User preferences:** `GET /api/v1/user/preferences` returns user's workingHours (JSON: `{"monday":{"start":"09:00","end":"17:00"},...}`), timezone, notificationPreferences (JSON: `{"email":true,"webhook":false,"webhookUrl":""}`). `PATCH /api/v1/user/preferences` updates preferences. Verify: Update working hours, GET preferences returns new hours.
|
||
- [ ] **Basic scheduling engine:** Service (`SchedulingService`) finds available time slots in user's calendar based on working hours and existing CalendarEvents. `POST /api/v1/schedule/regenerate` triggers scheduling: fetches all NEXT_ACTION tasks without `scheduledStart`, assigns `scheduledStart`/`scheduledEnd` within available slots, respecting `estimatedDuration` (default 30 min) and constraints (work contexts only during work hours). Persists scheduled times to Task table. Returns scheduled task count. Verify: Create 3 Next Action tasks (2 @desk, 1 @phone), set working hours 9-5, run regenerate, GET tasks shows scheduledStart/scheduledEnd within 9-5.
|
||
- [ ] **Context-based batching:** Scheduling engine groups consecutive tasks with same context when possible (e.g., 3 @phone tasks scheduled 10:00, 10:30, 11:00). Verify: Create 5 @phone tasks, regenerate schedule, GET schedule shows consecutive phone blocks.
|
||
- [ ] **Priority and deadline respect:** Scheduling engine sorts tasks by manual priority (1=highest) and dueDate before assigning slots. Higher priority tasks scheduled earlier in available slots. Verify: Create 3 tasks with priorities 1, 2, 3 and no due dates, regenerate, task with priority=1 gets earliest slot; create task with dueDate=today, regenerate, it schedules before lower-priority tasks with later due dates.
|
||
- [ ] **Frontend inbox UI:** React component renders unprocessed inbox items as list. Quick-add form at top posts to `/api/v1/inbox`. Each item has "Process" button opening modal with GTD decision tree (Is it actionable? → Yes: Is it multi-step? → Project vs. Next Action; No: Reference/Trash). Modal form submits to `/api/v1/inbox/:id/process`. Optimistic UI updates with React Query mutations. Verify: Manual browser test: add inbox item, process to Next Action with @desk context, see item disappear from inbox.
|
||
- [ ] **Frontend calendar week view:** FullCalendar component configured with timeGridWeek view, displays Tasks with `scheduledStart`/`scheduledEnd` as events. Color-coded by context (custom CSS or FullCalendar event color prop). Verify: Create 5 scheduled tasks, navigate to `/calendar`, see tasks rendered in week grid with correct times and colors.
|
||
- [ ] **Drag-and-drop manual scheduling:** FullCalendar `eventDrop` callback fires on drag, PATCH `/api/v1/tasks/:id` with new `scheduledStart`/`scheduledEnd`, sets `isLocked=true` to prevent auto-rescheduling. `eventResize` callback handles duration changes. Verify: Drag task in calendar, reload page, task remains at new time, `isLocked=true` in DB.
|
||
- [ ] **Task lock/unlock:** `POST /api/v1/tasks/:id/lock` sets `isLocked=true`, `POST /api/v1/tasks/:id/unlock` sets `isLocked=false`. Scheduling engine skips locked tasks. Lock icon displayed in calendar for locked tasks. Verify: Lock task, run schedule regenerate, task time unchanged; unlock, regenerate, task can move.
|
||
- [ ] **Next Actions list UI:** React component at `/next-actions` fetches `GET /api/v1/tasks?status=NEXT_ACTION`, renders grouped by context. Filter dropdown by context. Click task opens detail modal with edit form. Verify: Create 10 Next Actions across 3 contexts, filter by @phone, see only phone tasks.
|
||
- [ ] **Projects list UI:** React component at `/projects` fetches `GET /api/v1/projects`, renders cards with name, domain, active task count. Click card navigates to `/projects/:id` detail view showing tasks, references, edit form. Verify: Create 3 projects with tasks, navigate to project detail, see tasks listed, add reference material, see it appear.
|
||
- [ ] **Waiting For UI:** React component at `/waiting-for` fetches `GET /api/v1/tasks?status=WAITING_FOR`, displays with follow-up dates. Overdue follow-ups highlighted red. Verify: Create Waiting For task with past follow-up date, see red highlight.
|
||
- [ ] **Someday/Maybe UI:** React component at `/someday-maybe` fetches `GET /api/v1/tasks?status=SOMEDAY_MAYBE`, allows activation (status change to NEXT_ACTION). Verify: Create Someday task, click "Activate" button, task disappears from Someday list, appears in Next Actions.
|
||
|
||
**Phase 2 Verification Command:**
|
||
|
||
```bash
|
||
TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
|
||
-d '{"email":"verify@test.com","password":"Test123!"}' \
|
||
-H "Content-Type: application/json" | jq -r .token) && \
|
||
curl -f -X POST -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/v1/inbox \
|
||
-d '{"content":"Phase 2 verify"}' -H "Content-Type: application/json" && \
|
||
TASK_ID=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/v1/tasks \
|
||
-d '{"title":"Test task","domain":"WORK","context":"DESK"}' \
|
||
-H "Content-Type: application/json" | jq -r .id) && \
|
||
curl -f -X POST -H "Authorization: Bearer $TOKEN" \
|
||
http://localhost:3000/api/v1/schedule/regenerate && \
|
||
curl -s -H "Authorization: Bearer $TOKEN" \
|
||
http://localhost:3000/api/v1/tasks/$TASK_ID | jq .scheduledStart | grep -q "T" && \
|
||
echo "✓ Phase 2 Complete"
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 3: Integration
|
||
|
||
**Goal:** Implement external integrations (CalDAV, Microsoft Graph, ConnectWise Manage, IMAP email capture), real-time WebSocket notifications for rescheduling, conflict detection and automatic rescheduling, Weekly Review scheduling and interface.
|
||
|
||
#### Completion Criteria
|
||
|
||
- [ ] **CalDAV connection CRUD:** `POST /api/v1/connections/calendar` creates CalendarConnection with provider=CALDAV, calendarUrl, credentials (username/password encrypted at rest). `GET /api/v1/connections/calendar` lists user's connections. `DELETE /api/v1/connections/calendar/:id` removes connection. Verify: Create CalDAV connection with Nextcloud URL, GET returns connection with masked credentials.
|
||
- [ ] **CalDAV sync job:** `CalendarSyncService` using `node-caldav` library queries CalDAV server for events in date range (next 30 days). Creates/updates CalendarEvent entities with externalId, startTime, endTime. Cron job runs every 15 minutes. Manual trigger: `POST /api/v1/connections/calendar/:id/sync`. Verify: Configure CalDAV connection to test Nextcloud instance, create event in Nextcloud, trigger sync, `GET /api/v1/calendar/events?start=<today>&end=<+7days>` returns event.
|
||
- [ ] **Microsoft Graph calendar connection:** `POST /api/v1/connections/calendar` with provider=MICROSOFT_GRAPH initiates OAuth flow, stores refresh token. `CalendarSyncService` uses `@microsoft/microsoft-graph-client` to fetch events from Outlook. Verify: Mock Microsoft Graph client in tests to return sample events, trigger sync, events persisted.
|
||
- [ ] **Google Calendar API connection:** `POST /api/v1/connections/calendar` with provider=GOOGLE initiates OAuth flow (or accepts service account JSON). `CalendarSyncService` uses Google Calendar API via axios/googleapis. Verify: Mock Google API responses, trigger sync, events persisted with externalId.
|
||
- [ ] **ConnectWise Manage connection:** `POST /api/v1/connections/connectwise` accepts companyId, publicKey, privateKey, apiUrl, memberId. `ConnectWiseService` using axios queries `/service/tickets`, `/project/tickets`, `/project/projects` with conditions `owner/id={memberId}`. Creates InboxItems with source=CONNECTWISE, sourceMetadata includes ticketId, priority, SLA. Cron job runs hourly. Verify: Mock ConnectWise API responses (3 tickets), trigger sync, inbox has 3 items with sourceMetadata.
|
||
- [ ] **ConnectWise zero-ticket projects:** ConnectWise sync queries projects assigned to user with `/project/projects/:id/tickets` count=0. Creates InboxItem with content="Plan project: {projectName}" and sourceMetadata.connectwiseProjectId. Verify: Mock API returns project with zero tickets, sync creates planning task inbox item.
|
||
- [ ] **ConnectWise priority display:** Tasks sourced from ConnectWise store `connectwisePriority` and `connectwiseSLA` fields (strings). Displayed in UI for reference but do not affect scheduling priority (user manually sets `priority` integer). Verify: Process ConnectWise inbox item to task, task has `connectwisePriority="High"`, `priority=null`, user updates `priority=1`, scheduling uses user priority.
|
||
- [ ] **IMAP email connection:** `POST /api/v1/connections/email` with provider=IMAP accepts imapHost, imapPort, credentials, inboxFolder. `EmailSyncService` using `imap` library connects, fetches UNSEEN messages, creates InboxItem with content=email subject+body, sourceMetadata includes from, date. Marks email as SEEN. Cron job runs every 5 minutes. Verify: Mock IMAP responses, trigger sync, inbox items created from emails.
|
||
- [ ] **Microsoft Graph email connection:** `POST /api/v1/connections/email` with provider=MICROSOFT_GRAPH uses OAuth token to fetch messages from inbox folder via Graph API. Same InboxItem creation logic. Verify: Mock Graph API responses, sync creates inbox items.
|
||
- [ ] **Conflict detection on calendar sync:** After calendar sync, `ConflictDetectionService` compares new CalendarEvents against scheduled Tasks. If CalendarEvent overlaps Task's `scheduledStart`/`scheduledEnd` and task is not locked, marks task for rescheduling. Verify: Create scheduled task 10:00-11:00, sync calendar event 10:30-11:30, task marked for rescheduling.
|
||
- [ ] **Automatic rescheduling on conflict:** `ReschedulingService` finds next available slot for displaced tasks (respecting working hours, context, priority), updates `scheduledStart`/`scheduledEnd`, creates ReschedulingEvent record with reason, original/new times. Verify: Trigger conflict, rescheduling runs, task moves to 11:30-12:30, ReschedulingEvent created.
|
||
- [ ] **WebSocket gateway setup:** NestJS WebSocket gateway (`@WebSocketGateway()`) with JWT authentication guard. Clients connect to `ws://localhost:3000`. On connection, server stores userId-to-socketId mapping. Verify: Frontend connects via socket.io-client, connection established, `getaddrinfo` command shows WebSocket connection in logs.
|
||
- [ ] **Real-time rescheduling notifications:** When ReschedulingService reschedules task, emits WebSocket event `task:rescheduled` with payload `{taskId, originalStart, newStart, reason}` to user's socket. Frontend socket.io client listens, shows toast notification, refetches calendar via React Query invalidation. Verify: Open browser with WebSocket devtools, trigger reschedule via API, see WebSocket message received, toast appears.
|
||
- [ ] **Notification persistence:** All rescheduling events create Notification entity with type=RESCHEDULING, message, relatedEntityId=taskId. `GET /api/v1/notifications` returns unread notifications. `PATCH /api/v1/notifications/:id/read` marks as read. Frontend shows notification bell icon with count. Verify: Reschedule task, GET notifications returns 1 unread, click mark read, count decreases.
|
||
- [ ] **Email notification for rescheduling:** If user's `notificationPreferences.email=true`, `NotificationService` sends email via SMTP (nodemailer) with rescheduling details. Email template includes task title, old/new times, reason. Verify: Mock SMTP server (MailHog), trigger reschedule, check MailHog for email.
|
||
- [ ] **Webhook notification:** If `notificationPreferences.webhook=true` and `webhookUrl` configured, `NotificationService` posts JSON payload to webhook URL on rescheduling. Verify: Mock webhook endpoint (webhook.site), trigger reschedule, see POST request received with correct payload.
|
||
- [ ] **Weekly Review auto-scheduling:** On user creation or preferences update, `WeeklyReviewService` creates recurring Task with status=WEEKLY_REVIEW, `scheduledStart` at user-configured day/time (e.g., Friday 4 PM), duration 60 min, `isLocked=true`. Cron job checks weekly, ensures review task exists for upcoming week. Verify: Set weekly review time in preferences, trigger cron, GET tasks shows recurring review task at configured time.
|
||
- [ ] **Weekly Review interface:** `GET /api/v1/weekly-review` endpoint returns aggregated data: active projects count, projects without next actions (flagged), total Next Actions grouped by project, unprocessed inbox count, Waiting For items with overdue follow-ups, Someday/Maybe count. Frontend `/weekly-review` page displays checklist UI. Verify: Create 2 projects (1 with tasks, 1 without), 5 inbox items, GET weekly-review returns `{"activeProjects":2,"projectsWithoutNextActions":1,"inboxCount":5,...}`.
|
||
- [ ] **Weekly Review completion:** `POST /api/v1/weekly-review/complete` updates User.lastWeeklyReview timestamp. Verify: Complete review, GET user preferences shows lastWeeklyReview updated.
|
||
- [ ] **Rate limiting on external API calls:** Bull queue configured for calendar, ConnectWise, email sync jobs with concurrency=1, rate limiter (1 request per 2 seconds to respect API limits). Verify: Queue 10 sync jobs, monitor logs, jobs execute sequentially with delay.
|
||
- [ ] **Error handling for external APIs:** Sync services wrap API calls in try-catch, log errors, create system Notification for user if sync fails (e.g., "ConnectWise sync failed: invalid credentials"). Failed jobs retry 3 times with exponential backoff (Bull retry strategy). Verify: Mock ConnectWise API to return 401, trigger sync, see error notification created, job retries logged.
|
||
- [ ] **Encryption for credentials:** Database credentials (IMAP passwords, API keys) encrypted with AES-256 using secret from environment variable before persisting. Decrypted on retrieval. Use `crypto` module or `@nestjs/config` with encryption utility. Verify: Inspect database, credentials columns show encrypted strings; GET connection via API returns functional connection (decrypt succeeds).
|
||
|
||
**Phase 3 Verification Command:**
|
||
|
||
```bash
|
||
TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
|
||
-d '{"email":"verify@test.com","password":"Test123!"}' \
|
||
-H "Content-Type: application/json" | jq -r .token) && \
|
||
curl -f -X POST -H "Authorization: Bearer $TOKEN" \
|
||
http://localhost:3000/api/v1/connections/calendar \
|
||
-d '{"provider":"CALDAV","calendarUrl":"http://mock","credentials":{"username":"test","password":"pass"}}' \
|
||
-H "Content-Type: application/json" && \
|
||
curl -f -X GET -H "Authorization: Bearer $TOKEN" \
|
||
http://localhost:3000/api/v1/weekly-review | jq .inboxCount | grep -E '^[0-9]+$' && \
|
||
echo "✓ Phase 3 Complete"
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 4: Polish
|
||
|
||
**Goal:** Comprehensive documentation, deployment hardening, performance optimization, accessibility audit, error boundary improvements, and final testing. Prepare for production self-hosted deployment.
|
||
|
||
#### Completion Criteria
|
||
|
||
- [ ] **README.md complete:** Root README includes: project overview, architecture diagram (Mermaid or ASCII), prerequisites (Docker, Docker Compose, Node.js for dev), quick-start instructions (clone, `cp .env.example .env`, `docker compose up`), environment variable documentation (all required vars listed with descriptions), default ports, access URLs. Verify: Fresh clone on new machine, follow README, app starts successfully.
|
||
- [ ] **Docker Compose production configuration:** `docker-compose.prod.yml` with: PostgreSQL persistent volume, Redis persistent volume, backend health checks, restart policies (`unless-stopped`), resource limits (CPU/memory), nginx serving frontend static files with gzip, security headers (Helmet). Verify: `docker compose -f docker-compose.prod.yml up -d`, all services start with resource constraints visible in `docker stats`.
|
||
- [ ] **Environment variable validation:** Backend startup validates required env vars (DATABASE_URL, REDIS_URL, JWT_SECRET, ENCRYPTION_KEY) using `@nestjs/config` with Joi schema. Missing vars log error and exit process. Verify: Remove DATABASE_URL from `.env`, start backend, see error "Missing required env var: DATABASE_URL" and exit code 1.
|
||
- [ ] **Database migrations documentation:** `packages/backend/README.md` documents migration workflow: `pnpm migration:generate -n MigrationName`, `pnpm migration:run`, `pnpm migration:revert`. Includes note on production migrations (run before deploying new backend version). Verify: Generate dummy migration, run it, verify in DB, re |