Google Calendar, Three Rungs

The plan to finish Google Calendar for a company's shared calendar and a leasing agent's own calendar, and to straighten the onboarding steps around it. For Gera, in six pieces you can test one at a time.

2026-09-16 · Google integration scope v2 · S1 in progress

Where it stands

Google works for a building's calendar and nowhere else. New customers are set up as "one shared calendar for the company" by default, so the default customer path never reaches Google today.

RungWhere you see itToday
BuildingProperty Settings · onboarding Channels step (per-building companies)Google live
CompanyOrganization Settings · onboarding "company-microsoft" step (centralized companies)Microsoft only
Person/welcome/calendar · Profile → My calendarMicrosoft only

The company and person calendars are stored in one shared token vault that already has a provider field with google as a legal value. What keeps them Microsoft-only is small: one refresh function that only knows how to call Microsoft, three gates that refuse a Google row, and two "coming shortly" buttons. There is no second storage system to build.

Decisions the plan keeps

The slices, in order

Each is its own pull request off main, each works on its own, and each ends with something you can see.

S1 · The vault learns to refresh a Google token

~250 lines · no visible change · unblocks S2 and S3

The one refresh function branches on the row's provider: Google rows refresh through Google, keep their long-lived refresh token, and a revoked Google grant marks the row "reconnect needed" with plain-English copy. The three gates that refuse a Google company or person row start admitting it, and the tour code reports "google" for those rungs instead of assuming Outlook. Tests: the existing "a Google company row never reaches Microsoft" pin flips to "reaches Google"; a new pin for the person rung, which has none today.

You see: nothing yet. This is the plumbing every later slice stands on.

S2 · A company connects its Google calendar

~300 lines · onboarding + Organization Settings

The company connect route sends a Google-domain mailbox to Google's consent for the shared calendar, and the shared callback stores a company row. The onboarding step's Google button stops saying "coming shortly". Organization Settings shows the Google calendar as connected, and the collapsed row in Property Settings names it.

You see: in the centralized test company, "Continue with Google" opens Google's consent for calendar access; afterwards Organization Settings shows the calendar connected, and Clara books tours against it from the bench.

S3 · A person connects their own Google calendar

~250 lines · /welcome/calendar + Profile

The Google button on the first-run calendar screen and a Google option on "My calendar" go through the same shared callback and store a person row. Clara's "which agent is free" logic reads it like a Microsoft one.

You see: a new teammate's first sign-in offers Google for real; Profile → My calendar shows it connected.

S4 · The Microsoft-named step gets a neutral name

~150 lines, mechanical · old address redirects

/onboarding/company-microsoft becomes /onboarding/company-channels. Every reference moves (about twenty, plus a generated route manifest and one browser test); the old address keeps working by redirect so no bookmark or email link breaks.

You see: the address bar says company-channels.

S5 · The wizard runs in one straight line

~300 lines · sequence, back button, Skip vs Finish

One Channels step that shows the company shape or the per-building shape instead of skipping to a separate page. The personal calendar becomes a wizard step after Channels rather than a surprise after "Finish". Every step has Back. Teammates ends with one primary action: "Skip for now" when nothing is typed, "Send invites" when something is, never both.

You see: AppFolio → Sync → Review → Channels → Your calendar → Teammates → Dashboard, with Back on every screen.

S6 · Teammates: the standard dropdown, and name optional

~120 lines · teammates step only

The role picker moves to the arsenal select. The name field becomes optional: a suggestion is derived from the email ("jane.doe" becomes "Jane Doe") and stays editable, since guesses from addresses are wrong often enough that people need to fix them.

You see: invite a teammate with just an email.

What to test after each

AfterWhoDoExpect
S2propflow.test in the centralized test companyOnboarding company step → Continue with Google → pick test.client@ → allow calendarBack in PropFlow with the calendar connected; Organization Settings shows it; a bench tour lands on that Google calendar
S3a new teammate inviteFirst sign-in → /welcome/calendar → Continue with GoogleProfile → My calendar shows the Google calendar
S4anyoneOpen the old company-microsoft addressLands on company-channels
S5a fresh test companyWalk the whole wizardStraight line, Back everywhere, one clear ending
S6same walkInvite by email onlyName suggested, editable

Open with Fede, not blocking

Cleanup owed

PropFlow Docs