@natoe/colab 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # @natoe/colab
2
+
3
+ Real-time, study-aware collaboration UI for radiology workflows. Drop-in chat
4
+ components and React hooks built on Phoenix Channels — designed to plug into a
5
+ host app that already manages auth, file uploads, and DICOM viewing.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @natoe/colab
11
+ # or
12
+ pnpm add @natoe/colab
13
+ # or
14
+ yarn add @natoe/colab
15
+ ```
16
+
17
+ Peer dependencies: `react >= 18`, `react-dom >= 18`.
18
+
19
+ ## Quick start
20
+
21
+ Wrap your app in `CollabProvider`, then drop `<CollabPopup>` (or one of the
22
+ other surfaces) wherever you want chat to appear.
23
+
24
+ ```tsx
25
+ import { CollabProvider, CollabPopup } from '@natoe/colab';
26
+
27
+ function App() {
28
+ const config = {
29
+ socketUrl: 'wss://your-backend/socket',
30
+ getToken: () => localStorage.getItem('jwt') ?? '',
31
+ userId: currentUser.id,
32
+ userRole: 'radiologist', // 'lab' | 'radiologist' | 'physician' | 'admin'
33
+ userName: currentUser.name,
34
+
35
+ // Host-app callbacks — colab calls these, never implements them.
36
+ onOpenDicom: (studyId, storageId) => openViewer(studyId, storageId),
37
+ onUploadFile: async (file, fileName) => {
38
+ const url = await uploadToS3(file, fileName);
39
+ return url;
40
+ },
41
+ onDeepLink: (path) => router.push(path),
42
+ onError: (err) => reportToSentry(err),
43
+ };
44
+
45
+ return (
46
+ <CollabProvider config={config} apiBaseUrl="https://your-backend">
47
+ {/* ...your app... */}
48
+
49
+ <CollabPopup
50
+ orderId={order.id}
51
+ patientData={{
52
+ orderId: order.id,
53
+ patientName: 'Jane Doe',
54
+ patientAge: '54',
55
+ patientSex: 'F',
56
+ studyType: 'CT Chest',
57
+ studyId: order.dicomStudyId, // PACS UID — gates the "View DICOM" button
58
+ storageId: 'PACS', // required alongside studyId
59
+ }}
60
+ participantIds={[labUserId, radiologistId, adminId]}
61
+ isOpen={chatOpen}
62
+ onClose={() => setChatOpen(false)}
63
+ />
64
+ </CollabProvider>
65
+ );
66
+ }
67
+ ```
68
+
69
+ ## What's in the box
70
+
71
+ ### Surfaces (pick one or compose)
72
+
73
+ | Component | Use when… |
74
+ | --------------- | ------------------------------------------------------------------------ |
75
+ | `CollabPopup` | Floating, draggable chat window — typical for table-row "Open chat". |
76
+ | `CollabPanel` | Full-bleed panel, e.g. side-docked next to a viewer. |
77
+ | `CollabInline` | Last-N-messages preview embedded directly in a table row. |
78
+ | `CollabInbox` | WhatsApp-style two-pane inbox: conversation list + active conversation. |
79
+
80
+ ### Provider
81
+
82
+ `CollabProvider` owns the socket connection, identity, and host callbacks.
83
+ Wrap your app once near the root.
84
+
85
+ ### Hooks (for custom UI)
86
+
87
+ - `useCollab` — read config, socket, error stream
88
+ - `useConversation` — single conversation: messages, typing, send/edit/delete
89
+ - `useConversationList` — inbox list with unread counts and last activity
90
+ - `useMessages` — paginated message history with realtime appends
91
+ - `useInlineCollab` — batch preview fetcher for table rows
92
+ - `useUnreadCount` — total unread badge for nav bars
93
+ - `useChannelSettings`, `usePinnedMessages`, `useDeepLinks`, `useAudioRecorder`
94
+
95
+ ### Composable parts
96
+
97
+ If the prebuilt surfaces don't fit, compose your own from
98
+ `ConversationList`, `MessageList`, `MessageBubble`, `MessageInput`,
99
+ `PatientHeader`, `ChannelSettings`, etc.
100
+
101
+ ## `CollabConfig` reference
102
+
103
+ | Field | Type | Notes |
104
+ | ------------- | ----------------------------------------------- | ------------------------------------------------ |
105
+ | `socketUrl` | `string` | Phoenix WebSocket endpoint. |
106
+ | `getToken` | `() => string` | Returns the current JWT. Re-read each connect. |
107
+ | `userId` | `string` | Must match your backend's user identity. |
108
+ | `userRole` | `'lab' \| 'radiologist' \| 'physician' \| 'admin'` | |
109
+ | `userName` | `string` | Displayed in messages and presence. |
110
+ | `userAvatar` | `string?` | Optional avatar URL. |
111
+ | `onOpenDicom` | `(studyId, storageId) => void` | Opens the host's DICOM viewer. Gates the button. |
112
+ | `onUploadFile`| `(file, fileName?) => Promise<string>` | Returns a public URL after upload. |
113
+ | `onDeepLink` | `(path: string) => void` | Resolves `natoe://...` links inside messages. |
114
+ | `onError` | `(err: CollabError) => void` | Centralized error sink. |
115
+
116
+ ## `PatientData` reference
117
+
118
+ Conversations are study-aware. Pass a `PatientData` object per chat surface so
119
+ the header and "View DICOM" button render with the right context.
120
+
121
+ | Field | Required | Notes |
122
+ | -------------------- | -------- | -------------------------------------------------------- |
123
+ | `orderId` | yes | Stable per-case identifier. |
124
+ | `patientName` | yes | |
125
+ | `studyId` | no | PACS study UID. Pair with `storageId` to show "View DICOM". |
126
+ | `storageId` | no | e.g. `'PACS'`. Required alongside `studyId`. |
127
+ | `patientAge`, `patientSex`, `studyType`, `bodyParts`, `referringPhysician`, `labName`, `displayOrderId` | no | Header chrome. |
128
+
129
+ ## How conversations are created
130
+
131
+ A conversation is materialized the first time someone sends a message — empty
132
+ chats don't clutter the inbox. After that first message, anyone with access to
133
+ the case can join.
134
+
135
+ Who can send the first message:
136
+
137
+ - **Lab / Admin** — any case
138
+ - **Radiologist** — only after being assigned
139
+ - **Physician** — only after the case is completed
140
+
141
+ When a new role joins (e.g. an assigned radiologist), they see the full
142
+ backlog from before they joined.
143
+
144
+ ## Message types
145
+
146
+ Text, voice (record-and-send), images, files (up to 20 MB), system events,
147
+ and deep links (`natoe://...`) that the host app resolves via `onDeepLink`.
148
+
149
+ ## Key features
150
+
151
+ - Reply to a specific message (frozen quote snapshot)
152
+ - Pin up to 3 messages per channel
153
+ - Read receipts ("Seen by …")
154
+ - Typing indicators (debounced)
155
+ - Unread counts per channel and total
156
+ - Channel settings: rename, picture, add/remove participants
157
+
158
+ ## TypeScript
159
+
160
+ Fully typed. All public types are exported from the root:
161
+
162
+ ```ts
163
+ import type {
164
+ CollabConfig,
165
+ PatientData,
166
+ Conversation,
167
+ Message,
168
+ Participant,
169
+ CollabError,
170
+ } from '@natoe/colab';
171
+ ```