Both adapters uploaded with fetch(), which has no upload-progress event — so the
new picker's progress bar could only ever have been indeterminate. putWithProgress
does the presigned PUT over XHR and reports true byte progress; when a server
sends no content-length the fraction is withheld rather than guessed, and the bar
falls back to indeterminate.
Picks up the attachment picker, the Gmail-style Compose button, and the mail
reply row fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The /vendor/photo-gallery-sdk/node_modules ignore rule had no effect
because the nanoid files inside it were already tracked, so `npm install`
kept surfacing them as modified. Drops them from the index; they stay on
disk and npm install regenerates them (photo-gallery-sdk is a file: dep).
Lockfile churn is from that same install: onnxruntime-web and friends
un-hoisted under @huggingface/transformers, and @types/react/csstype
moved dev -> devOptional.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Offline-notifications popup was covered by Smart Gallery's toolbar. Raising
its z-index could not have worked: .dash-topbar is `position: sticky; z-index: 20`,
which opens a stacking context, so .tm-menu-pop's z-index: 30 only ranked inside
the topbar. Against module content the whole topbar competed at 20 — and the
vendored photo-gallery SDK stacks up to 1400 (its own README documents this).
Adds AnchoredPopover, which portals to <body> — out of the topbar's stacking
context entirely — positions from the trigger's viewport rect, clamps to the
window edges, and closes on scroll/resize/Escape/outside-click. Applied to the
notification bell AND the user menu, which had the identical trap and would have
been reported next.
Portalling rather than z-index escalation is the fix that survives a module
raising its own z-index later.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Needed by the Seen fix: the adapter reads e.userId to tell your own read receipt
from the other party's.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Receipts now prefer userId over the IIOS actor UUID, so the SDK can actually
tell your own read receipt from the other party's.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up the code-chip contrast fix (inline code was invisible on your own
bubbles) and stripMarkup(). Also applies stripMarkup to the notification toast,
which had the same problem — a formatted message toasted as "~crazy~".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up message formatting (bold/italic/strike/code/links) and the composer
textarea with native OS spellcheck + autocorrect.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase D of channel-roster import. CrmMessagingAdapter.addMembers() proxies the
new crm.messenger.participant.addMany door action, enabling the SDK's '#' import
affordance in the channel/group settings panel. Pulls @insignia/iios-messaging-ui@0.1.8.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Integrates the Leads and Lead Verification screens with the live be-crm
backend (crm.lead.* / crm.leadVerification.* / crm.media.*) behind a
mode-agnostic data layer that still falls back to the local mock when the
Shell isn't configured.
Data layer (new)
- src/lib/leads-api.ts — crm.lead.search/get/stats/create/update/
updateStatus/assign/sendForVerification + media (presign upload/download)
- src/lib/verify-api.ts — crm.leadVerification.search/get/stats/verify/
markUnverified/assign/reassign/moveToPending
Backend → FE wiring
- Assignee / creator names: read the resolved DTO fields and, as a safety
net, resolve member ids → real names against crm.team.member.search
(be-crm currently echoes the raw principalId as the name).
- Created By: read the createdBy object the backend returns (was blank).
- Site photos: upload via crm.media.presignUpload → PUT, persist through
crm.lead.attachment.add (per photo, after create), render in the detail
popup via crm.media.presignDownload.
- Duplicate guard: surface the backend's real error (detail / duplicate_lead)
instead of the SDK's opaque "data command failed: 400".
UX
- Leads detail: Property Photos gallery + edit-mode photo add/remove.
- Verification: "Change Assignee" now opens a searchable, scrollable popup
instead of a long inline dropdown.
- Removed the static "Storm Zone" tag from lead cards/detail; storm banner
only renders when the lead carries storm data.
Reference / follow-ups
- leads.http, leads.postman_collection.json — be-crm data-door requests.
- LEADS_BACKEND_CHANGES.md — required be-crm changes (name resolution,
assignee carry-over on sendForVerification) verified against :4010.
Co-Authored-By: Claude Opus 4.8 (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>