Commit Graph

75 Commits

Author SHA1 Message Date
maaz519 7982c243c0 feat: attachments in Messenger — upload/preview images + files
- media-api: useUploadAttachment (presign → direct PUT to IIOS storage),
  useDownloadUrl (short-lived signed URL), isImage, 25MB cap
- socket + REST send now carry attachment {contentRef, mimeType, sizeBytes}
- composer: 📎 attach button, staged chip, send with attachment (text optional)
- MessageBubble renders AttachmentView (inline image or file chip), aligned
  to the message side; empty bubble suppressed for attachment-only messages

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:17:45 +05:30
maaz519 1490fa3460 fix(messenger): reply focuses composer + quoted message jumps to original
- Clicking Reply now focuses the composer input (was requiring a manual click).
- A quoted message is clickable → scrolls to the original and flashes it briefly
  (was inert). Message elements register refs by id for the jump.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:03:44 +05:30
maaz519 36f43d7d2a refactor(inbox): fold mail INTO the Inbox (one unified surface, no Mail tab)
The Inbox is the single communication surface — mentions, needs-reply, system
alerts, support updates AND the mail behind them, all in one list. Removed the
separate Mail tab.

- Inbox is now two-pane: the item list (crm.inbox.*) on the left; clicking an item
  tied to a thread opens its conversation (MailReader) on the right to read + reply.
  Non-threaded items (e.g. system alerts) show their detail. Item actions
  (Done/Snooze/Archive) work for every item.
- Compose new mail (in-app or email) from the Inbox header.
- mail.tsx trimmed to reusable MailReader + NewMailModal (no standalone tab);
  sidebar + dashboard reverted to no Mail entry.
- The existing work-item Inbox stays the notifier; this makes it the reader too.
  HTML bodies still render in a sandboxed iframe. tsc + next build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 16:43:48 +05:30
maaz519 46aa7a767c feat(mail): dedicated Mail reader in the CRM
A Mail surface in the Communication group, distinct from Messenger (chat) and the
work-item Inbox: read app-to-app + email messages (subject + rendered body), reply,
and compose (in-app to a person, or external to an email).

- mail-api.ts: crm.mail.list/history/reply + compose (crm.mail.internal/send), reusing
  the messenger directory for the people picker. Live via the AppShell SDK; mock in demo mode.
- mail.tsx: thread list + reader + reply + New-mail composer. HTML bodies render inside a
  SANDBOXED iframe (no scripts) — safe against untrusted email HTML.
- Wired into the sidebar (Communication) + dashboard switch.

The existing work-item Inbox stays as the notifier (IIOS's projector already flags new
mail there); this is where you read it. tsc + next build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 16:38:16 +05:30
maaz519 4b50d682e6 Merge pull request 'feat(messenger): reactions, replies, typing, seen ticks + UX fixes' (#29) from feat/messenger-ux into goutamnextflow
Reviewed-on: Goutam/lynkeduppro-crm#29
2026-07-16 11:24:03 +00:00
maaz519 adb5a6bb7b feat(messenger): reactions, replies, typing, seen ticks + UX fixes
Wire the IIOS kernel client's existing realtime affordances into the
Messenger UI and fix three display issues.

- Bubble contrast: incoming bubbles used --panel-2, which equals --bg in
  the dark theme (both #060608) → invisible. Use --panel + a border.
- DM title: mapped all participants (incl. self → unknown-id fallback),
  producing "User d10888, Maaz Ahmed". Now shows the counterpart only.
- Live sidebar preview: lastMessage/time update on any inbound socket
  message, reconciled with a debounced conversation-list refetch.
- Typing indicator: throttled typing() send + auto-expiring "typing…" line.
- Read receipts: markRead() on open/new message; "Sent"/"Seen" under the
  last outgoing message. Receipt event carries no threadId, so it is a
  global stream filtered to my own messages by actor id.
- Reactions: emoji picker on hover, chips with counts, live via annotation.
- Reply/quote: parentInteractionId round-trips; quoted parent renders above
  the reply and in a composer quote bar.

Presence (online + last-seen) is intentionally not included — the kernel
has no presence receive-event yet; that needs an IIOS change + client release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:52:29 +05:30
maaz519 2056592d51 Merge pull request 'feat: Messenger + Inbox on the AppShell data door + live IIOS socket' (#28) from feat/messenger-inbox into goutamnextflow
Reviewed-on: Goutam/lynkeduppro-crm#28
2026-07-15 21:23:37 +00:00
maaz519 49b04e9710 feat: Messenger + Inbox on the AppShell data door + live IIOS socket
New "Communication" area in the dashboard, built on the existing appshell-sdk wiring
(useQuery/sdk.command) like Team, with the IIOS MessageSocket for live streaming.

- messenger-api.ts / inbox-api.ts: mock (demo) + live (crm.messenger.* / crm.inbox.*)
  behind one interface, switched by isShellConfigured().
- messenger-socket.tsx: MessengerSocketProvider — one IIOS MessageSocket per panel
  (openThread history + live on("message") + send). REST 4s poll is the automatic
  fallback when the socket isn't connected.
- messenger.tsx: conversation list ⇄ thread + composer + new-chat people picker
  (DM 1 person / group 2+); inbox.tsx: filterable feed with Done/Snooze/Archive.
- sidebar: Communication group (Messenger + Inbox, always-visible); dashboard: panel switch.
- .npmrc: add the @insignia Gitea registry for @insignia/iios-kernel-client.

Works on mock immediately; goes live once NEXT_PUBLIC_SUPABASE_URL + the BFF + be-crm are set.
tsc clean; next build passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:45:47 +05:30
tanweer919 e7c042fb1a Merge pull request 'refactor(email): move invite email to be-crm (AppShell)' (#27) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-14 10:09:03 +00:00
tanweer919 9eb234935d refactor(email): remove frontend invite email; be-crm sends it now
Per AppShell, email delivery lives in the domain API. Delete the frontend
/api/email/invite route + invite-email template, and the team-management email
wiring. be-crm now emails invitees on create/resend. Copy link stays as a
fallback (invite token still returned in the pending-invite list).
2026-07-14 15:38:53 +05:30
tanweer919 fa538355ae Merge pull request 'fix(login): readable email-OTP send error' (#26) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 15:14:24 +00:00
tanweer919 1a2043eaae fix(login): readable email-OTP send error instead of raw '{}'
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.
2026-07-13 20:44:05 +05:30
tanweer919 70831e189d Merge pull request 'fix(team): real member names + emails' (#25) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 15:03:08 +00:00
tanweer919 eeded95940 fix(team): show member real name + email (no more principal UUIDs)
Use displayName + email now returned by be-crm member search (from CRM
registration). Falls back to the ACE for the current user, then job title —
never the raw principal id as the email.
2026-07-13 20:33:01 +05:30
tanweer919 0643b8c9b2 Merge pull request 'fix(invite): route register vs sign-in + prefill email (public lookup)' (#24) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 14:55:33 +00:00
tanweer919 be4bd5f41d fix(invite): look up the invitation and route correctly (register vs sign in)
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).
2026-07-13 20:25:25 +05:30
tanweer919 75417cb4f1 Merge pull request 'feat(email): invitation emails via Twilio (Vercel route)' (#23) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 14:18:31 +00:00
tanweer919 cc71516278 feat(email): send team invitations from a Vercel serverless route (Twilio)
- POST /api/email/invite: sends the invite email via the Twilio Emails API using
  server-only TWILIO_ACCOUNT_SID/TWILIO_AUTH_TOKEN (Vercel env), from
  EMAIL_FROM_ADDRESS (default support@lynkedup.dev). Fixed invite template; gated
  on a session cookie so it isn't an open relay.
- Team Management emails the invite on create and resend (token now returned by
  both), with the Copy-link as a fallback. Mock mode skips email.
2026-07-13 19:48:19 +05:30
tanweer919 6606a55207 Merge pull request 'feat(rbac): invitation-based membership + nav gating; no role at registration' (#22) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 10:56:17 +00:00
tanweer919 103f3ae3a1 feat(rbac): no role at registration; invitation-based membership; nav gating
- 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).
2026-07-13 16:25:53 +05:30
tanweer919 661e291fae Merge pull request 'fix(legal): opt-in page spacing' (#21) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 09:30:18 +00:00
tanweer919 68bf137d11 fix(legal): explicit spacing after inline elements on SMS opt-in page 2026-07-13 14:59:59 +05:30
tanweer919 2fa7ffc327 Merge pull request 'fix(legal): spacing on opt-in/terms' (#20) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 09:25:40 +00:00
tanweer919 5f67c8fa85 fix(legal): correct spacing around company name on opt-in and terms pages 2026-07-13 14:55:33 +05:30
tanweer919 4a53cb6353 Merge pull request 'feat(legal): SMS opt-in + Privacy + Terms for Twilio A2P 10DLC' (#19) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 09:21:07 +00:00
tanweer919 5511ef4110 feat(legal): SMS opt-in form + Privacy Policy + Terms for Twilio A2P 10DLC
Public, unauthenticated pages required for A2P 10DLC campaign verification:
- /sms-opt-in: web opt-in form (phone field, un-pre-checked consent checkbox,
  message description, frequency, msg&data-rates + HELP/STOP disclaimers, links
  to Terms & Privacy, 'Yes, sign me up!' submit).
- /privacy: Privacy Policy with the mandatory SMS clauses (no third-party/affiliate
  sharing of mobile numbers or SMS consent, message frequency, rates disclosure).
- /terms: Terms of Service with an SMS program-terms section.
Business details centralized in legal-config.ts (replace SMS_SENDER with the
live Twilio number once approved).
2026-07-13 14:50:49 +05:30
tanweer919 870525d6c7 Merge pull request 'fix(login): back nav, single OTP channel, register back buttons' (#18) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 09:02:35 +00:00
tanweer919 127e0f8912 fix(login): back-button nav, single OTP channel, register back buttons
- 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.
2026-07-13 14:32:18 +05:30
tanweer919 925170296e Merge pull request 'feat(otp): mock phone OTP fallback while Twilio is down' (#17) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 08:31:29 +00:00
tanweer919 27a5aa939e feat(otp): mock phone OTP fallback while Twilio SMS is down
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.
2026-07-13 13:59:04 +05:30
tanweer919 0a4468cc54 Merge pull request 'feat: real phone OTP verification (Twilio) in register + onboarding' (#16) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-12 22:59:22 +00:00
tanweer919 da7f7a7891 feat(register/onboard): real phone OTP verification via Twilio (SDK 0.2.6)
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.
2026-07-13 04:29:02 +05:30
tanweer919 3c6190d16a Merge pull request 'fix(onboarding): reliably prefill verified email (SDK 0.2.5)' (#15) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-12 22:36:29 +00:00
tanweer919 6ba00bfbf9 fix(onboarding): reliably prefill the verified email (SDK 0.2.5)
getUserEmail() relied on getSession(), which returns null here because the
Supabase session is intentionally never persisted to storage — so the
onboarding email field stayed empty and Continue was blocked. SDK 0.2.5
captures the email in memory from the token-exchange response instead.
Bump @abe-kap/appshell-sdk to ^0.2.5.
2026-07-13 04:06:13 +05:30
tanweer919 5e2fa574bd Merge pull request 'fix(onboarding): gate direct access behind OAuth handoff' (#14) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-12 22:32:35 +00:00
tanweer919 d79da8cd6a fix(onboarding): gate direct access behind the OAuth handoff
/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.
2026-07-13 04:01:01 +05:30
tanweer919 10141806dc Merge pull request 'fix(onboarding): prefill verified email; SDK 0.2.4' (#13) from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-12 22:26:05 +00:00
tanweer919 08ef85869f fix(onboarding): prefill verified email in a disabled field; bump SDK 0.2.4
- 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).
2026-07-13 03:55:05 +05:30
tanweer919 619ec4c9b1 Merge pull request 'feat(auth): Google onboarding flow (option 3)' (#12) from feat/login-methods into goutamnextflow 2026-07-12 22:06:03 +00:00
tanweer919 948abf75fd feat(auth): first-time Google onboarding (option 3)
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.
2026-07-13 03:35:55 +05:30
tanweer919 66cd8953ba Merge pull request 'feat(auth): gate Google sign-in on CRM profile' (#11) from feat/login-methods into goutamnextflow 2026-07-12 21:52:11 +00:00
tanweer919 34cb44cfa8 feat(auth): gate Google sign-in on an existing CRM profile
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.)
2026-07-13 03:22:04 +05:30
tanweer919 11931cbf6f Merge pull request 'polish: real role in user chip' (#10) from feat/login-methods into goutamnextflow 2026-07-12 21:33:24 +00:00
tanweer919 f38fa80bde polish(dashboard): show real ACE role in user chip instead of static 'Property Owner' 2026-07-13 03:03:17 +05:30
tanweer919 43f9a3eb83 Merge pull request 'feat(dashboard): real user + sign-out; remove demo text' (#9) from feat/login-methods into goutamnextflow 2026-07-12 21:27:58 +00:00
tanweer919 86af77c92b feat(dashboard): real user in sidebar + sign-out; strip demo/mock hints
- 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.
2026-07-13 02:57:51 +05:30
tanweer919 f5ff7bf6ea Merge pull request 'fix(auth): session persists on reload (SDK 0.2.2)' (#8) from feat/login-methods into goutamnextflow 2026-07-12 21:11:46 +00:00
tanweer919 27e7bdf8f5 fix(auth): bump appshell-sdk 0.2.2 (ready-after-restore) — session persists on reload 2026-07-13 02:41:39 +05:30
tanweer919 3e79b3bf31 Merge pull request 'fix(auth): session persists on reload (gate redirect on boot-ready)' (#7) from feat/login-methods into goutamnextflow 2026-07-12 21:05:20 +00:00
tanweer919 64be4b9b3f fix(auth): don't redirect from dashboard until SDK boot completes
AuthGate redirected to /portal/login whenever status !== authenticated, which
during boot (before restore() resolves) dropped a valid session on reload. Gate
the redirect on useAppShell().ready so we only bounce once auth has resolved.
2026-07-13 02:35:13 +05:30