docs: design vynte scheduler replacement
This commit is contained in:
@@ -108,6 +108,7 @@ packages/**/.yarn/ci-cache/
|
||||
# AI - session/user specific files (keep CLAUDE.md and .cursor/skills tracked)
|
||||
.claude/plans/
|
||||
.claude/settings.local.json
|
||||
.superpowers/
|
||||
|
||||
# Spec drafts (rough work, not ready for review)
|
||||
specs/**/_drafts/
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
# Vynte Scheduler Replacement Design
|
||||
|
||||
## Goal
|
||||
|
||||
Build a small internal scheduling app that replaces the current Cal.diy interface for Vynte. The app keeps the parts of Cal that are hard and valuable: Authentik login, calendar provider connections, busy-time calculation, conferencing integrations, and booking creation. It removes the broad public scheduling product surface and focuses on one team that schedules meetings with each other.
|
||||
|
||||
## Product Decisions
|
||||
|
||||
- Every user must log in through Authentik.
|
||||
- Everyone belongs to one shared Vynte team automatically.
|
||||
- There are no anonymous or public booking links.
|
||||
- Teammates only see each other's free/busy blocks.
|
||||
- Meeting titles, descriptions, attendees, and locations are private unless the viewer is invited to that meeting.
|
||||
- Scheduling starts by selecting attendees, including the organizer.
|
||||
- The app shows mutual availability for the selected attendees.
|
||||
- Selecting a mutual time creates the meeting immediately and sends normal calendar invitations.
|
||||
- Each organizer can choose any duration, with 15, 30, 45, and 60 minute presets plus custom duration.
|
||||
- Locations are optional.
|
||||
- Automatic conferencing is optional.
|
||||
- Organizers always attend meetings they create.
|
||||
- External calendar sync is required: read busy times and write confirmed meetings.
|
||||
- All calendar providers already supported by Cal.diy remain available.
|
||||
- Each person has one weekly schedule plus date-specific overrides.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Public booking pages.
|
||||
- Multiple teams, organizations, managed users, or external guests.
|
||||
- Multiple named schedules per person.
|
||||
- Payment, routing forms, round-robin event types, workflows, app marketplace UI, embed surfaces, or public profile pages.
|
||||
- Rebuilding calendar provider adapters from scratch.
|
||||
- Replacing Authentik with another auth system.
|
||||
|
||||
## Architecture
|
||||
|
||||
Create a new slim Next.js app inside the monorepo at `apps/scheduler`. It shares the existing database and backend packages rather than forking the scheduling engine.
|
||||
|
||||
The current Cal web app stays in place during development. The new app can run locally and behind a separate internal route until it is verified. After that, the deployment can switch the internal scheduler domain to the new app.
|
||||
|
||||
The new app owns a small set of scheduler-specific routes and API handlers:
|
||||
|
||||
- `/calendar`: team calendar and meeting creation.
|
||||
- `/availability`: weekly schedule and date overrides.
|
||||
- `/connections`: calendar and conferencing providers.
|
||||
- `/api/scheduler/team`: current user and team members.
|
||||
- `/api/scheduler/availability`: free/busy and mutual availability queries.
|
||||
- `/api/scheduler/schedule`: weekly schedule and override reads/writes.
|
||||
- `/api/scheduler/connections`: calendar and conferencing connection metadata.
|
||||
- `/api/scheduler/meetings`: meeting creation and meeting list reads.
|
||||
|
||||
The first version must not add scheduler-specific database tables. It uses the existing Cal database models: `User`, `Credential`, `SelectedCalendar`, `Schedule`, `Availability`, `Booking`, `BookingReference`, `Attendee`, and calendar/conferencing app credential tables.
|
||||
|
||||
## Reused Cal Components
|
||||
|
||||
Reuse these areas:
|
||||
|
||||
- Authentik/NextAuth configuration and sign-in provisioning.
|
||||
- Prisma client and schema.
|
||||
- Calendar provider adapters and `CalendarManager`.
|
||||
- Selected calendar repository.
|
||||
- Schedule repository and availability primitives.
|
||||
- Busy-time and user-availability calculation.
|
||||
- Booking creation service.
|
||||
- Conferencing app integrations.
|
||||
|
||||
Do not reuse the current Cal page modules. Small primitive components from shared UI packages are allowed only when they do not pull in Cal's broader page routing or product settings flows.
|
||||
|
||||
## Primary Workflow
|
||||
|
||||
The primary screen is a calendar-first weekly view.
|
||||
|
||||
The left rail has only the app name, three navigation items, and the current user/timezone. The center is the team calendar canvas. The right panel is a meeting composer.
|
||||
|
||||
Scheduling flow:
|
||||
|
||||
1. User opens Calendar.
|
||||
2. User selects teammates in the composer.
|
||||
3. Organizer is always included.
|
||||
4. The app fetches free/busy blocks for selected people.
|
||||
5. The calendar shows the organizer's own event titles where allowed.
|
||||
6. Teammates' unavailable blocks render as private busy blocks.
|
||||
7. Mutual free slots are outlined on the calendar.
|
||||
8. User selects a mutual slot.
|
||||
9. User enters title, duration, optional location, and optional conferencing.
|
||||
10. User clicks Create meeting.
|
||||
11. The app creates the booking and writes calendar events/invites through the existing Cal booking flow.
|
||||
|
||||
## Availability Workflow
|
||||
|
||||
The availability screen has one weekly schedule and date overrides.
|
||||
|
||||
Weekly schedule requirements:
|
||||
|
||||
- Show timezone.
|
||||
- Show all seven days.
|
||||
- Each day can be enabled or unavailable.
|
||||
- Enabled days have one or more time ranges.
|
||||
- Default seed can be Monday through Friday, 9:00 AM to 5:00 PM in the user's timezone.
|
||||
|
||||
Date override requirements:
|
||||
|
||||
- Add unavailable all day.
|
||||
- Add custom hours for a specific date.
|
||||
- Remove override.
|
||||
- Overrides take precedence over weekly hours.
|
||||
|
||||
The app maps this to the existing `Schedule` and `Availability` models. If a user has multiple legacy schedules, the scheduler app chooses or creates one canonical scheduler schedule and avoids exposing multiple schedules in the UI.
|
||||
|
||||
## Connections Workflow
|
||||
|
||||
The connections screen consolidates calendar and conferencing setup.
|
||||
|
||||
Calendar section:
|
||||
|
||||
- Show connected calendars.
|
||||
- Show every provider supported by the existing Cal installation.
|
||||
- Connect additional providers by linking into the existing Cal provider OAuth/credential routes.
|
||||
- Make clear that teammates only see busy blocks.
|
||||
|
||||
Conferencing section:
|
||||
|
||||
- Show connected conferencing providers.
|
||||
- Connect providers by linking into the existing Cal provider OAuth/credential routes.
|
||||
- Conferencing is optional per meeting.
|
||||
|
||||
The first version routes provider connection actions into existing Cal provider OAuth handlers. It does not implement new OAuth handlers.
|
||||
|
||||
## Privacy Rules
|
||||
|
||||
Free/busy data must be normalized before returning to the client.
|
||||
|
||||
For current user:
|
||||
|
||||
- Show own meeting title when the user is an attendee or organizer.
|
||||
- Show own busy blocks from external calendars.
|
||||
|
||||
For teammates:
|
||||
|
||||
- Return only start, end, source type, and participant id.
|
||||
- Do not return event title, description, attendees, location, conferencing link, calendar name, or provider event id.
|
||||
|
||||
For created meetings:
|
||||
|
||||
- A user can see full details only if they are an attendee or organizer.
|
||||
- Non-attendee teammates only see a busy block.
|
||||
|
||||
## Data Flow
|
||||
|
||||
Calendar page load:
|
||||
|
||||
1. Authenticate with existing Authentik session.
|
||||
2. Ensure the user exists in the Cal database.
|
||||
3. Ensure the user is part of the single Vynte team.
|
||||
4. Fetch team members.
|
||||
5. Fetch the user's upcoming meetings.
|
||||
6. Fetch selected attendees' busy blocks for the visible week.
|
||||
7. Compute mutual availability from weekly schedules, overrides, and busy blocks.
|
||||
8. Render the weekly calendar.
|
||||
|
||||
Meeting creation:
|
||||
|
||||
1. Validate Authentik session.
|
||||
2. Validate selected attendees are Vynte team members.
|
||||
3. Force organizer into the attendee list.
|
||||
4. Validate selected time is within mutual availability at request time.
|
||||
5. Validate duration and location/conferencing choices.
|
||||
6. Call Cal's existing booking creation service.
|
||||
7. Write calendar events/invites through Cal's existing provider integrations.
|
||||
8. Return the created meeting summary.
|
||||
|
||||
Availability update:
|
||||
|
||||
1. Validate Authentik session.
|
||||
2. Normalize weekly ranges and overrides.
|
||||
3. Upsert the canonical scheduler schedule.
|
||||
4. Replace relevant availability rows in one transaction.
|
||||
5. Return the normalized schedule.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If Authentik session is missing, redirect to login.
|
||||
- If the user is not provisioned yet, run the existing sign-in provisioning path.
|
||||
- If no calendar is connected, show the calendar screen with an explicit connection prompt.
|
||||
- If provider busy-time fetch fails, show that provider as degraded and continue with known internal meetings when possible.
|
||||
- If meeting creation fails after the selected slot becomes unavailable, return a conflict error and refresh mutual availability.
|
||||
- If calendar write fails during booking creation, return the existing Cal booking error to the client and do not add a scheduler-specific retry state.
|
||||
- If the canonical scheduler schedule is missing, create it from defaults on first visit.
|
||||
|
||||
## Performance Requirements
|
||||
|
||||
- Avoid loading Cal's full web app bundles.
|
||||
- Keep the new app's first screen focused on team, schedule, and booking data only.
|
||||
- Fetch team members, current user's meetings, and selected attendees' busy blocks in parallel.
|
||||
- Cache provider metadata and team member list per request or short server-side TTL.
|
||||
- Query free/busy for the visible date range only.
|
||||
- Debounce attendee selection changes before refetching mutual availability.
|
||||
- Return privacy-filtered compact event payloads.
|
||||
|
||||
## UI Design
|
||||
|
||||
Use a quiet operational interface:
|
||||
|
||||
- Light gray page background.
|
||||
- White app surfaces with thin neutral borders.
|
||||
- Muted green accent for selected navigation, mutual availability, and primary actions.
|
||||
- Small, dense typography suitable for repeated daily use.
|
||||
- Three primary navigation items: Calendar, Availability, Connections.
|
||||
- No landing page, marketing hero, public profile, or settings maze.
|
||||
|
||||
Primary calendar screen:
|
||||
|
||||
- Left rail: app name, navigation, current user/timezone.
|
||||
- Center: week calendar with own events, teammate busy blocks, and outlined mutual free slots.
|
||||
- Right rail: attendee picker, title, duration presets/custom duration, optional location, optional conferencing, create button.
|
||||
|
||||
Availability screen:
|
||||
|
||||
- Single weekly-hours panel.
|
||||
- Date overrides panel.
|
||||
- Save button.
|
||||
|
||||
Connections screen:
|
||||
|
||||
- Calendar providers panel.
|
||||
- Conferencing providers panel.
|
||||
- Privacy note.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
Unit tests:
|
||||
|
||||
- Mutual availability calculation with overlapping busy blocks.
|
||||
- Privacy filtering for teammate busy blocks.
|
||||
- Canonical schedule selection and creation.
|
||||
- Weekly schedule plus date override normalization.
|
||||
- Meeting request validation.
|
||||
|
||||
Integration tests:
|
||||
|
||||
- Authenticated user can fetch team members.
|
||||
- User with no schedule receives a default canonical schedule.
|
||||
- Availability update writes expected `Schedule` and `Availability` rows.
|
||||
- Meeting creation rejects unavailable slots.
|
||||
- Meeting creation includes organizer automatically.
|
||||
- Meeting creation calls existing booking/calendar write services with expected attendees.
|
||||
|
||||
Browser tests:
|
||||
|
||||
- Authentik-authenticated user can open Calendar.
|
||||
- Selecting attendees updates visible mutual availability.
|
||||
- Creating a meeting shows it on the calendar.
|
||||
- Availability edits persist after reload.
|
||||
- Connections page lists available provider categories without exposing secrets.
|
||||
|
||||
Manual verification:
|
||||
|
||||
- Use the in-app Browser against the local app.
|
||||
- Verify desktop calendar layout.
|
||||
- Verify mobile/tablet fallback stacks navigation, calendar, and composer without overlap.
|
||||
- Verify live Authentik login path before deployment.
|
||||
|
||||
## Deployment Shape
|
||||
|
||||
During development, run the new scheduler app locally. For production, add a separate `scheduler` service to the existing Portainer stack. After verification, Nginx Proxy Manager routes the internal scheduler hostname to the new service.
|
||||
|
||||
The new service must use:
|
||||
|
||||
- Existing PostgreSQL database.
|
||||
- Existing Redis service.
|
||||
- Existing Authentik provider.
|
||||
- Existing internal routing through Nginx Proxy Manager.
|
||||
|
||||
Do not cut over the live internal Cal route until the replacement app has passed browser verification and meeting creation tests.
|
||||
Reference in New Issue
Block a user