Opens from the drawer's "More Details", replacing the placeholder toast.
The board swaps for a full-width lead record; "← Pipeline" goes back.
- Detail page: breadcrumb, hero (avatar, stage/type/storm/age pills,
Call + Email), stage progress bar + 7-step stepper, and an overview
grid of Contact / Property / Job Details / Insurance / Assignment.
- Move Stage dropdown so a lead can be re-staged from the detail page,
not only by dragging on the kanban board. Stage changes now route
through one setStage() used by both paths, so "reached" advancement
and the toast behave identically.
- Detail is keyed by lead id rather than a snapshot, so a stage move
updates the pills, progress, stepper and page accent live.
- pipeline-data: PLead gains phones, propertyType, source, tradeType,
urgency, insurance (null for Retail jobs) and assignment. All derived
deterministically from the existing rows — no Date.now, so SSR and
client render identically.
- Dropdown is a bottom sheet under 720px and an anchored menu above it;
dropped overflow:hidden on .pld-hero, which was clipping it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolve conflicts in dashboard.tsx and dashboard.css:
- Keep goutamnextflow's SDK inbox/messenger, settings, notifications,
realtime provider and smart gallery (the old messenger/inbox files
were deleted on that branch)
- Graft the feat/leads additions (Leads, Verify views + their CSS) on top
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Notifications phase 2: a /push-sw.js service worker shows a system notification
on push and, on click, focuses an open CRM tab (or opens one) and deep-links to
the thread. usePushNotifications registers the SW, requests permission, subscribes
with IIOS's VAPID key, and stores the subscription via the be-crm door. The topbar
bell becomes a real enable/disable control (hidden when push is unsupported/demo).
Dashboard listens for the SW's notif-click message + a ?thread= deep link.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Shared RealtimeProvider opens ONE dashboard-wide IIOS message socket reused by
the messenger tab and the new NotificationCenter. CrmMessagingAdapter gains
setFocus (drives presence/push suppression) and subscribeActivity (joins all
of the caller's threads, fans out incoming messages). NotificationCenter shows
a clickable toast on new messages when you're not on the messenger tab and
deep-links to the conversation. Pulls @insignia/iios-messaging-ui@0.1.7.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Topbar search box → debounced crm.search → dropdown of hits (chat/mail, highlighted
snippet). Clicking a hit switches to the right tab and focuses the exact thread:
mail → Inbox, chat → Messenger (via the SDK's new focusThreadId, 0.1.6). Snippet HTML
is escaped except the <em> highlight. Demo mode searches an in-memory set.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the SMTP 'coming soon' stub with a real card: host/port/SSL/user/password/
from-address/from-name → crm.settings.smtp.configure, masked status from
crm.settings.smtp.status. Mirrors the Twilio SMS card; demo mode stores hints locally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CrmMessagingAdapter implements addMember/removeMember/renameConversation over
crm.messenger.participant.add/remove + group.rename, powering the SDK's new
group/channel settings panel. listMembers maps customer role too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
goutamnextflow adopts @insignia/iios-messaging-ui (drops the bespoke
messenger/inbox/mail) and adds Org Settings → Integrations. Resolve against
the Smart Gallery work:
- dashboard.tsx: route messenger→MessengerSdk, inbox→InboxSdk, add settings→
Settings (goutamnextflow) AND keep gallery→SmartGallery (ours). Drop the
now-deleted Messenger/Inbox imports.
- next.config.ts / package.json: union transpilePackages + deps (keep
@photo-gallery/sdk + ML deps AND @insignia/iios-messaging-ui).
- dashboard.css: keep both appended blocks (.gal* gallery + .settings-* org
settings).
Gallery integrates with zero errors. Vendored @photo-gallery/sdk re-synced
(0-diff). Verified: tsc clean + `next build` compiles the whole merged app
(gallery AI routes + dashboard). The new @insignia/iios-messaging-ui private
dep is stubbed locally (see .claude/dev-scripts); it resolves on CI/Vercel.
CrmMessagingAdapter now implements upload() (presign-upload → PUT → presign-download
→ Attachment with contentRef), so the composer's attach button appears; send()
forwards the attachment to the socket; and incoming messages (history + realtime)
resolve a presigned display URL. Pull SDK 0.1.4.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- crm.messenger.directory limit 200 -> 100 (be-crm caps at 100, was 400 Bad
Request) in both the inbox + messaging adapters.
- CrmInboxAdapter.downloadAttachment via crm.media.presignDownload, so mail
attachments are downloadable. Pull SDK 0.1.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The SDK is now published to git.lynkedup.cloud (@insignia scope); switch from the
local file: tarball to ^0.1.0 so prod/CI installs resolve it. Install auth uses
GITEA_TOKEN (read:package), same as @insignia/iios-kernel-client.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CrmMessagingAdapter.directory() now returns the org people list (Person[]) for the
new 'New message' picker; internal name-resolution uses directoryMap(). The DM/group
create path rides the existing crm.messenger.conversation.open door (dedupe + group).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delete the now-dead pre-SDK stack (messenger/inbox/mail components + their
*-api libs + messenger-socket) — both tabs render the @insignia/iios-messaging-ui
SDK. Remove the redundant <PageHead> from the messenger/inbox SDK wrappers (the
Topbar already shows the title); the SDK itself renders no header.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New Settings tab renders an Integrations section; the SMS (Twilio) card lets a
tenant admin connect their own Twilio account (Account SID / write-only Auth Token
/ E.164 from-number) via crm.settings.sms.configure, and shows the masked status
(from-number + SID last-4) from crm.settings.sms.status. Demo mode stores hints
locally. Email (SMTP) shown as the next provider on the same registry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add Lead source dropdown options (Door Knock, Referral, Storm Chase,
Mailer/Postcard, Sign Call, Insurance Agent Referral, Repeat Customer,
Social Media, Other)
- Quick form: Referral shows a referral-note textarea; Door Knock shows a
canvasser search (by name or email) with picker + chip
- Full form is now a Next/Back wizard ending in Create Lead
- Reps gain email for canvasser search
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
uploadAttachment presigns via crm.media.presignUpload + PUTs bytes to IIOS
storage; mailReply/composeInternal/composeExternal forward attachment refs
to crm.mail.reply/internal/send (server already supported them).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the bespoke in-CRM inbox with the SDK's <Inbox>, driven by a new
CrmInboxAdapter over the be-crm data door (crm.inbox.* + crm.mail.*).
Demo mode falls back to the SDK MockInboxAdapter. Maps --miu-* tokens for
.miu-inbox to the CRM theme.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CrmMessagingAdapter now implements the full SDK contract:
- browseChannels/createChannel/leaveChannel over the be-crm channel door
(crm.messenger.channel.*); joinChannel is a governed public self-join over
the socket (openThread) — the BFF has no join verb, OPA enforces it
- listMembers over crm.messenger.members → drives @mention autocomplete
- send forwards mentions[] on the socket path (→ IIOS MENTION inbox items)
So the CRM messenger (rendered by the SDK) now gets sectioned Channels/DMs,
browse+join+create, and @mention autocomplete/highlight — no embedded code.
(SDK re-packed to the local tarball; publish for deploy.) tsc + next build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CrmMessagingAdapter is now hybrid: BFF for the conversation list, thread
creation, and directory (server-side tenancy); the IIOS MessageSocket
(delegated crm.messenger.realtime token) for everything live — history+join,
send, typing, read receipts, and reaction annotations. Falls back to the 4s
poll only when no socket is available.
- messenger-sdk useRealtimeSocket() opens the MessageSocket from the BFF token
and passes it to the adapter (adapter rebuilds live once connected)
- socket path maps senderId (userId space) → actorId so 'mine'/seen work
consistently, killing the actor-vs-user ambiguity in the poll fallback
- reaction annotations tracked per message and re-emitted as full sets
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The messenger tab now renders the shared SDK's <Messenger>, not the bespoke
in-CRM implementation. The CRM only supplies transport + theme:
- CrmMessagingAdapter implements the SDK's MessagingAdapter over the be-crm
data door (crm.messenger.conversation.list/open, history, send, directory);
live updates via a 4s poll for now (socket realtime is a follow-up that
reuses messenger-socket.tsx). Tenancy/auth stay server-side.
- messenger-sdk.tsx wires MessagingProvider + adapter (live = CrmMessagingAdapter,
demo = the SDK MockAdapter), themed by mapping --miu-* tokens to the CRM
design system.
- dashboard renders <MessengerSdk/>; the old messenger.tsx is left in place
for rollback and will be deleted once this is proven live.
NOTE: the SDK is installed from a local tarball (file:) for verification since
it isn't published yet. For deploy: publish @insignia/iios-messaging-ui to the
registry and change the dep to a version range. transpilePackages covers ESM.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Leads: card board with status stats, search + status tabs, a rich
lead-detail popup (contact, property, job, insurance, assignment,
storm banner) and a New Lead intake form (Quick / Full Form) with
multi-phone/email rows, site-photo dropzone and urgency picker.
Lead Verification: clickable stat tiles, status/source/assignee
filters, a full verification table, a detail popup with an activity
timeline, and a per-row actions menu (verify / reassign / pending…).
Both wired into the dashboard view switcher and styled with the
existing orange brand + glass-card system (dark & light themes).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Browsers report an empty file.type for files with no OS-registered MIME (esp.
.md), which then went up as application/octet-stream and got rejected. Infer
the type from the extension for the text types IIOS allows (md/markdown/html/
htm/txt/csv) so those attachments upload.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The appshell SDK useQuery only re-runs when the action changes, not the
variables — so switching mail items (same crm.mail.history action, new
threadId) never refetched and the reader kept the first thread's content
(only the subject, a direct prop, updated). Key MailReader by threadId to
remount it on switch (same pattern Messenger already uses for ThreadView).
Also force a refetch of crm.inbox.list when the filter changes, which hit
the same stale-variables bug.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- inbox merges crm.mail.list threads (kind MAIL) into crm.inbox.list items so
sent/received mail actually appears in the one surface; mail shows in the
Open view (it has no inbox work-item state) and opens in MailReader on click.
Mail rows are read/reply only — no Done/Snooze/Archive (crm.inbox.transition
doesn't apply to a mail thread).
- compose Attachments block was wrapped in <Field> (a <label>), so the label
hijacked the Attach button's click via label→file-input association and the
picker misfired. Use a plain div with the same field styling.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A transformed/overflow panel ancestor was trapping the modal overlay's
position:fixed, so the modal rendered offset inside the messenger/inbox panel
and clipped (Group settings sat inside the thread pane; the compose modal's
top was cut at the panel edge). Portal the overlay up to .dash-root — above
those panels but still inside the scoped design-system CSS — so it centers on
the viewport and the 90vh cap works. Falls back to inline render if no
.dash-root is present.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The modal body is a flex child with overflow-y:auto but had no min-height:0,
so it refused to shrink below content height and pushed the footer past the
90vh cap (visible on the tall Group settings modal — the Done button was
clipped). Add flex:1 1 auto + min-height:0 so the body scrolls and the
footer stays pinned.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- messenger-api: useGroupSettings(threadId) — members query + rename/add/
remove commands + isAdmin (from the member roles); live + mock
- messenger: a settings (gear) button on group thread headers opens a
GroupSettingsModal — editable name (admin), member list with role pills,
admin-gated remove, and add-from-directory search
- controls are admin-gated in the UI; IIOS/OPA re-enforces server-side
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- mail-api: MailMessage carries attachment; reply + sendInternal +
sendExternal accept uploaded attachments; mock updated
- mail.tsx: MailAttachmentView (inline image or file chip via signed URL),
StagedChip; attach button in the reply footer and the New Message
composer (multi-file, up to 10); text optional when a file is attached
- messenger: file-chip icon uses paperclip (was an unknown name)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
- 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>
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>
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>
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>
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>
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).