gotrue can throw an error whose message is an unhelpful JSON blob (e.g. 500
'Error sending magic link email' when Supabase SMTP isn't configured). Show a
readable, actionable message instead of rendering the raw payload.
The invite link is token-only, so the landing page couldn't tell the invited
email or whether an account existed — it dumped everyone on an empty register
screen. Now it calls a public lookup (/api/invite/lookup → be-crm) and:
- first-time invitee → register with the email prefilled + locked;
- email already registered → sign in with the email prefilled;
- signed-in + registered → accept immediately; signed-in + no profile → onboarding.
Login now redeems the pending invite after sign-in (password/OTP/OAuth), so an
existing user's invite is accepted on login (not only when already logged in).
- Registration no longer asks for a role/persona (removed the role select +
allottee block); crm.account.register sends no persona.
- New users have no permissions → the sidebar now shows only Dashboard + Profile
for them, gated on crm.account.me (membership + permissions). Members see the
areas their permissions allow.
- Add /portal/invite?token=… : accepts the invite for a signed-in registered
user, or routes an unregistered invitee through register/onboarding, which
redeems the stashed token on completion (granting the invited role).
- Team Management: 'Copy link' on pending invites builds the invite link from
the invitation token (no email delivery yet).
- Login: identify → password/otp now push() history (was replace()), so Back
returns to the email screen instead of leaving to /register.
- Login: in Shell mode the OTP screen no longer shows an email/SMS toggle —
the channel is fixed by how you signed in, so email sign-in never shows a
stray SMS option.
- Login: hide phone (SMS) sign-in while OTP is mocked — a faked login code
can't mint a session (AuthGate would bounce), so it can't work without SMS.
Email OTP + password + Google stay real and working.
- Register: add Back buttons (email screen → sign in; profile screen → email).
- Share MOCK_OTP/DEMO_OTP via @/lib/otp.
The Twilio sender isn't delivering, so gate the Verify step behind
NEXT_PUBLIC_MOCK_OTP (default true): 'Send code' skips Supabase/Twilio and a
fixed demo code (123456) verifies, with clear demo-mode copy. The real
addPhone/verifyPhone path is untouched — flip the flag to "false" to restore
live SMS verification.
Replaces the mock verify step (which accepted any 6 digits) with a real SMS
OTP: addPhone() → Supabase/Twilio texts a code → verifyPhone() confirms it.
- Registration creates the auth account when leaving the Account step, so the
phone can be attached + verified against a live Supabase session; finish()
no longer double-registers.
- Onboarding gains a real phone-verify step (Profile → Verify → Address).
- Email is trusted without an OTP in both flows (Supabase auto-confirms on
signup; Google verifies for onboarding), matching project config.
- Bump @abe-kap/appshell-sdk to ^0.2.6.
/portal/onboarding is only a step in the Google sign-in flow. Redirect to
login unless the login page's onboard_email handoff hint is present (and the
session is authenticated), instead of showing an empty form on direct/typed
URL access. Hold rendering until the check passes to avoid a form flash, and
clear the hint once onboarding completes so it can't be reused.
- Onboarding reads the OAuth email (session-storage hint + live Supabase
session via getUserEmail) so Continue is no longer disabled.
- Replace the empty 'Signed in as' banner with a disabled, prefilled
email field (email is fixed — Google-verified).
- Stash the verified email at OAuth callback time for onboarding prefill.
- Bump @abe-kap/appshell-sdk to ^0.2.4 (getUserEmail via getSession).
After a Google sign-in with no CRM profile, route to /portal/onboarding — the
registration flow in mode='onboard': email skipped (Google-verified, prefilled
via getUserEmail), sets a password (updateUser) so email+password login also
works, skips the OTP-verify step, and persists the profile via crm.account.register.
Existing-profile users go straight to the dashboard. Bumps SDK ^0.2.3.
Google is sign-IN only: after OAuth, check crm.account.registrationStatus; if the
account has no CRM profile, sign back out and show an error on the login screen
instead of admitting a profile-less user. (Onboarding to collect the profile comes later.)
- Sidebar footer now shows the ACE identity (was static 'James Carter') with a
Sign-out menu; topbar user gets a Sign-out dropdown too. Both call useAuth().logout.
- Remove all visible 'Demo:' hints from login + register and the 'Demo mode' subtitle.
The OAuth-return effect ran on mount before AppShell finished booting, so
completeOAuthLogin no-oped (sdk.auth undefined → null) and fell back to identify,
never retrying. Gate it on useAppShell().ready so it runs once auth is up, and
strip ?code= from the URL on success.
- SocialButtons: only Google (Microsoft/Apple hidden).
- Sign-up: remove all social (email/password only).
- Login: keep Google + email/password (single step) + email/SMS OTP; add a direct
'Sign in with a phone number' entry (SMS OTP), and rename the OTP link to
'Sign in with a one-time code'. All Supabase-supported; no mandatory 2FA.
Lift StepAddress/AddressBlock state up so finish() can send the complete
sign-up payload (name, phone, persona, allottee id/names, registered+mailing
address, consent) to be-crm crm.account.register after auth. Config-gated;
non-fatal if the domain persist fails (auth account already created).
- team-management: new useTeamData() data layer serves live be-crm data door
(crm.team.member.search/role.list/invitation.list + set-roles/perm/invite
commands) or the mock fallback, gated on isShellConfigured(). A member can
hold MANY roles: role chips + multi-select picker in Edit/Invite modals.
- login OTP: OtpVerify wired to real Supabase email + SMS OTP (passwordless)
via appshell sendEmailOtp/verifyEmailOtp + sendPhoneOtp/verifyPhoneOtp.
- bump @abe-kap/appshell-sdk ^0.2.0 (phone OTP).
- dashboard sidebar now uses the --sidebar token so it turns white in
light mode instead of staying black; active nav item stays readable
- registration: drop the Property / Job ID step (job ID + find-by-address),
flow is now Account -> Verify -> Address with steps renumbered
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New "Your role" options matching the portal roles
- Customer/Owner = self; Employee shows an Employee ID field;
Contractor/Sub-Con/Vendor show their ID + property owner name
- Validation adapts per role
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Flag emojis don't render on Windows, so derive an ISO code from the
emoji's regional indicators and load a flagcdn image in front of the
country-code selector (Account + Verify steps).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Support Center overview rebuilt as a bento grid: large live-chat hero
tile, support team tile, mini channel tiles and a recent-tickets strip.
- Restore full workspace sidebar (Workspace/Team/Profile groups) with a
premium golden active state; unbuilt modules show a Coming Soon view.
- Use Western names across the UI (James Carter; Emma Wilson, Liam Foster,
Sophie Turner, Noah Mitchell) incl. tickets, threads, portal demo user.
- Remove the Allottee card from Profile; add a clean Account & identity
card and live-editable contacts.
- Relabel "Plot" -> "House number" everywhere; drop the allotment chip in
favour of the property category.
- Premium visual pass: brand gradients, depth, glow accents, pill tabs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>