Calendar and client visibility

The Contractor Codex has a built-in calendar surface for project deadlines, work sessions, and (optionally) client-facing availability windows. It can also sync two-ways with your Google Calendar.

Two surfaces

  • Admin calendar at /admin/calendar — your full project calendar with every event (project deadlines, work sessions, quote followups, invoice reminders, manual entries, imported Google events).
  • Client calendar at /dashboard/calendar — what your clients see. Off by default; opt-in per event.

Connecting Google Calendar

From Settings → Calendar:

  1. Click Connect Google Calendar.
  2. Sign in with Google and grant calendar access.
  3. Pick which of your Google calendars to sync with (default: your primary).

The connection stores an OAuth refresh token (encrypted at rest) and the calendar ID. Each admin in your org connects independently — no shared calendar accounts.

What syncs

Outbound — the portal pushes to Google:

  • Work sessions you log (start/stop time, project name, duration)
  • Project deadlines (as all-day events on the due date)
  • Signed contracts (as a one-time milestone event)
  • Quote acceptances (as a one-time milestone)
  • Invoice due dates

Inbound — Google pushes to the portal:

  • Manual events you create in Google Calendar that mention a project (the portal does best-effort parsing; not perfect)

You can disable any sync direction per-event-type in Settings → Calendar.

Picking a different sync calendar

If you don't want work sessions cluttering your main calendar, create a separate Google calendar (e.g., "Contractor Codex") and pick it in the settings page. Future syncs go to that calendar; existing events stay where they are.

Client-facing calendar

This is opt-in and off by default. When on, clients see a Calendar tab in their portal at /dashboard/calendar listing events you've marked client-visible.

Two locks

The portal uses two-step gating to make sure admin-internal events never leak to clients:

Lock 1: Per-event flag (clientVisible, default false)

Every calendar event has a clientVisible checkbox in the event editor. Off by default — existing events default to invisible. You opt events in one at a time.

Lock 2: Org-wide gate (customer_calendar_visible, default false)

In Settings → Calendar, the Client-facing calendar card has a pill toggle. Off by default — even when individual events are flagged client-visible, the entire client Calendar tab is hidden until you flip this toggle.

Defense in depth: even if the UI logic regressed, the customer-side Prisma query filters by clientVisible: true at the data layer.

What clients see when it's off

The Calendar tab in their portal shows a single muted card:

Calendar viewing isn't available

[Your Business Name] has the calendar turned off. If you need to schedule a call, reach out to them directly and they can switch it on or send you a time.

No DB query runs in that branch.

What clients see when it's on

The Calendar tab shows their visible events split into Upcoming and Past. Each event shows:

  • Title, time, optional description, optional location
  • A small icon if it's all-day

Privacy scope at the query layer: clients only see events that are:

  • Marked client-visible AND
  • Generic (no customer/project link), OR tied to this customer, OR tied to one of this customer's projects.

Another customer's scoped events never reach this client's query.

Typical use cases

  • Availability windows — Mark slots in your calendar as "Open for calls" and flip them client-visible. Clients see when you're available and can email to book.
  • Project milestones — Mark deliverable dates client-visible so clients know when things land.
  • Kickoff meetings — Tied to a specific project, marked visible — only that client's portal shows them.

Admin-only events (work sessions, internal follow-ups, imported personal calendar items) stay private because their clientVisible defaults to false and the project-internal events are auto-created without the flag set.

Configuring per-event visibility

When you create or edit an event from /admin/calendar, the modal has a Visible to clients checkbox below "All day". Tick it to opt the event in.

The event's body description and location both show to clients — keep them client-appropriate. The portal doesn't sanitize the text.

Disconnecting Google Calendar

To stop syncing, go to Settings → Calendar and click Disconnect. Existing events stay in both places — the portal won't push new updates or pull new events.