feat(messenger): CRM consumes @insignia/iios-messaging-ui instead of embedding

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>
This commit is contained in:
2026-07-21 19:37:47 +05:30
parent 036ed84255
commit e70904a219
7 changed files with 271 additions and 4 deletions
@@ -0,0 +1,72 @@
"use client";
// The CRM messenger, now rendered by the shared @insignia/iios-messaging-ui SDK instead of a
// bespoke in-CRM implementation. The CRM only supplies an adapter (transport) + theming; all the
// UI + messaging logic lives in the SDK. Live path = the be-crm data door (CrmMessagingAdapter);
// demo path = the SDK's own MockAdapter.
import { useMemo } from "react";
import { useAppShell, useAuth } from "@abe-kap/appshell-sdk/react";
import { MessagingProvider, Messenger as SdkMessenger, type MessagingAdapter } from "@insignia/iios-messaging-ui";
import { MockAdapter } from "@insignia/iios-messaging-ui/adapters/mock";
import "@insignia/iios-messaging-ui/styles.css";
import { isShellConfigured } from "@/lib/appshell";
import { CrmMessagingAdapter, type DataDoor } from "@/lib/crm-messaging-adapter";
import { PageHead } from "./ui";
const SHELL = isShellConfigured();
export function MessengerSdk() {
return (
<div className="view">
<PageHead
eyebrow="Communication"
title="Messenger"
subtitle="Chat with your team and clients — rendered by the messaging SDK"
icon="send"
/>
{!SHELL && (
<div
style={{
margin: "0 0 14px",
padding: "8px 14px",
borderRadius: 10,
background: "var(--panel-2)",
color: "var(--muted)",
fontSize: 13,
border: "1px solid var(--border)",
}}
>
Demo mode running on the SDK&apos;s mock adapter.
</div>
)}
<div className="miu-host">{SHELL ? <LiveHost /> : <DemoHost />}</div>
</div>
);
}
// SHELL is a build-time constant, so exactly one of these mounts for the life of the app
// (Rules-of-Hooks safe — the other branch never renders).
function DemoHost() {
const adapter = useMemo<MessagingAdapter>(() => new MockAdapter(), []);
return (
<MessagingProvider adapter={adapter}>
<SdkMessenger />
</MessagingProvider>
);
}
function LiveHost() {
const { sdk } = useAppShell();
const { user } = useAuth();
const adapter = useMemo<MessagingAdapter | null>(
() => (user?.id ? new CrmMessagingAdapter(sdk as unknown as DataDoor, user.id) : null),
[sdk, user?.id],
);
if (!adapter) return <div className="miu-empty">Loading</div>;
return (
<MessagingProvider adapter={adapter}>
<SdkMessenger />
</MessagingProvider>
);
}