Back to site

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:

  • Events from your synced Google calendar, imported as-is. They land on your admin calendar with their title, time, and description carried over. The portal doesn't match them to a project or customer, so they stay unlinked admin entries.

The connection works in both directions whenever it's active. Per-direction and per-event-type controls aren't in Settings → Calendar yet.

Quick-add from where you work

A project card's Calendar section and each contract row on a client's Contracts tab have an Add to calendar form (title and date). It creates an all-day event linked to that project or contract, so it shows on the project, the org calendar, and, when Google is connected, your Google 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.

Clients can view the times you've shared, but they can't book themselves in. There's no self-booking. To set up a meeting they reach out to you, and you create the event.

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.