@natoe/colab 0.1.28 → 0.1.30
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/dist/index.d.mts +77 -30
- package/dist/index.d.ts +77 -30
- package/dist/index.js +58 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -119,25 +119,40 @@ interface PatientData {
|
|
|
119
119
|
bodyParts?: string[];
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
|
-
*
|
|
122
|
+
* What a thread is about — and, crucially, what it is SCOPED to.
|
|
123
123
|
*
|
|
124
|
-
* - `case`
|
|
125
|
-
*
|
|
126
|
-
* - `
|
|
124
|
+
* - `case` — the discussion about one clinical order. Scoped to an order,
|
|
125
|
+
* so `orderId` is always set. Lab, radiologist, physician, admin.
|
|
126
|
+
* - `help_lab` — an imaging centre's help channel with Natoe support. Scoped to
|
|
127
|
+
* the LAB, not to any case: it has NO `orderId`, it outlives
|
|
128
|
+
* every order, and there is exactly one per imaging centre.
|
|
127
129
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
130
|
+
* `help_lab` was briefly modelled as per-case (one help thread per order). It is
|
|
131
|
+
* now per-lab, so support gets one continuous thread with each imaging centre
|
|
132
|
+
* rather than N short-lived ones scattered across cases. A case can still be
|
|
133
|
+
* referenced from inside the channel; it just no longer scopes it.
|
|
134
|
+
*
|
|
135
|
+
* Isolation — who can see a help channel — is enforced by the backend's
|
|
136
|
+
* participant seeding, never by anything in this package. Treat this as a label
|
|
137
|
+
* for rendering and for addressing the right thread, not as a security boundary.
|
|
132
138
|
*
|
|
133
139
|
* Optional everywhere, defaulting to `case`, so callers written before help
|
|
134
|
-
*
|
|
140
|
+
* channels existed keep their exact previous behaviour.
|
|
135
141
|
*/
|
|
136
|
-
type ConversationKind = 'case' | 'help_lab'
|
|
142
|
+
type ConversationKind = 'case' | 'help_lab';
|
|
137
143
|
declare function isHelpKind(kind?: ConversationKind | string): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* True when a thread is scoped to an order and therefore has patient context to
|
|
146
|
+
* render. Prefer this over testing `orderId` directly: a help channel has no
|
|
147
|
+
* order, so every patient-shaped affordance must be skipped for it.
|
|
148
|
+
*/
|
|
149
|
+
declare function isCaseKind(kind?: ConversationKind | string): boolean;
|
|
138
150
|
interface Conversation {
|
|
139
151
|
id: string;
|
|
140
|
-
|
|
152
|
+
/** Absent on a help channel — that thread belongs to a lab, not an order. */
|
|
153
|
+
orderId?: string;
|
|
154
|
+
/** Set only on a help channel: the imaging centre that owns it. */
|
|
155
|
+
labUserId?: string;
|
|
141
156
|
name: string;
|
|
142
157
|
kind?: ConversationKind;
|
|
143
158
|
picture?: string;
|
|
@@ -146,7 +161,8 @@ interface Conversation {
|
|
|
146
161
|
unreadCount: number;
|
|
147
162
|
createdAt: string;
|
|
148
163
|
updatedAt: string;
|
|
149
|
-
/** Denormalized patient data snapshot — lets UI render without extra fetch
|
|
164
|
+
/** Denormalized patient data snapshot — lets UI render without extra fetch.
|
|
165
|
+
* Always absent on a help channel: there is no patient. */
|
|
150
166
|
patientSnapshot?: PatientData;
|
|
151
167
|
/** Currently pinned messages (max 3) */
|
|
152
168
|
pinnedMessages?: Message[];
|
|
@@ -157,10 +173,12 @@ interface Conversation {
|
|
|
157
173
|
*/
|
|
158
174
|
interface ConversationListItem$1 {
|
|
159
175
|
id: string;
|
|
160
|
-
orderId
|
|
176
|
+
/** Absent on a help channel — see `Conversation.orderId`. */
|
|
177
|
+
orderId?: string;
|
|
178
|
+
labUserId?: string;
|
|
161
179
|
name: string;
|
|
162
|
-
/** Lets the inbox
|
|
163
|
-
*
|
|
180
|
+
/** Lets the inbox render the lab's help channel distinctly from its case
|
|
181
|
+
* threads — they appear as separate rows. */
|
|
164
182
|
kind?: ConversationKind;
|
|
165
183
|
picture?: string;
|
|
166
184
|
unreadCount: number;
|
|
@@ -285,10 +303,15 @@ interface InviteUserPayload {
|
|
|
285
303
|
}
|
|
286
304
|
|
|
287
305
|
interface CollabPanelProps {
|
|
288
|
-
/** Order
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
306
|
+
/** Order this thread belongs to. Required for a `case` thread; omitted for a
|
|
307
|
+
* help channel, which belongs to a lab rather than an order. */
|
|
308
|
+
orderId?: string;
|
|
309
|
+
/** A thread the caller already resolved — how a help channel is opened.
|
|
310
|
+
* Skips the order-keyed preview lookup entirely. */
|
|
311
|
+
conversation?: Conversation | null;
|
|
312
|
+
/** Patient/study data displayed in the header. Absent on a help channel:
|
|
313
|
+
* there is no patient, and the header renders its support variant instead. */
|
|
314
|
+
patientData?: PatientData;
|
|
292
315
|
/** Participant user IDs — only used when creating a brand-new conversation */
|
|
293
316
|
participantIds?: string[];
|
|
294
317
|
/** Which thread on the case. Defaults to the shared `case` thread. */
|
|
@@ -343,7 +366,7 @@ interface CollabPanelProps {
|
|
|
343
366
|
* Full collaboration panel: patient header + pinned bar + message thread + input.
|
|
344
367
|
* Supports reply, pin/unpin, and (optionally) seen-by indicators.
|
|
345
368
|
*/
|
|
346
|
-
declare function CollabPanel({ orderId, patientData, participantIds, kind, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, showSettings: controlledShowSettings, onSettingsChange, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
|
|
369
|
+
declare function CollabPanel({ orderId, conversation: providedConversation, patientData, participantIds, kind, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, showSettings: controlledShowSettings, onSettingsChange, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
|
|
347
370
|
|
|
348
371
|
interface CollabPopupProps {
|
|
349
372
|
/** Order ID for this conversation */
|
|
@@ -635,19 +658,36 @@ interface CollabProviderProps {
|
|
|
635
658
|
declare function CollabProvider({ config, apiBaseUrl, children }: CollabProviderProps): react_jsx_runtime.JSX.Element | null;
|
|
636
659
|
|
|
637
660
|
interface UseConversationOptions {
|
|
638
|
-
|
|
639
|
-
|
|
661
|
+
/**
|
|
662
|
+
* The order this thread belongs to. Required for a `case` thread, which is
|
|
663
|
+
* created lazily on first message and resolved by an order-keyed preview.
|
|
664
|
+
* Omitted for a help channel — that belongs to a lab, not an order.
|
|
665
|
+
*/
|
|
666
|
+
orderId?: string;
|
|
667
|
+
/**
|
|
668
|
+
* A thread the caller has ALREADY resolved, opened directly instead of being
|
|
669
|
+
* looked up by order.
|
|
670
|
+
*
|
|
671
|
+
* This is how a help channel opens. The backend's `GET /help_channel` does
|
|
672
|
+
* the get-or-create and hands back the conversation, so there is nothing to
|
|
673
|
+
* look up and nothing to lazily create — passing it here skips the preview
|
|
674
|
+
* request entirely. The preview endpoint is order-keyed and could not serve
|
|
675
|
+
* an order-less thread in any case.
|
|
676
|
+
*/
|
|
677
|
+
conversation?: Conversation | null;
|
|
678
|
+
/** Patient/study context. Absent on a help channel — there is no patient. */
|
|
679
|
+
patientData?: PatientData;
|
|
640
680
|
/** Participant user IDs used only when creating a brand-new conversation */
|
|
641
681
|
participantIds?: string[];
|
|
642
682
|
/** Load full history on mount (default true) */
|
|
643
683
|
loadHistory?: boolean;
|
|
644
684
|
/**
|
|
645
|
-
*
|
|
646
|
-
*
|
|
685
|
+
* What this thread is. Defaults to the shared `case` thread, so every
|
|
686
|
+
* existing caller is unaffected.
|
|
647
687
|
*
|
|
648
|
-
* For
|
|
649
|
-
* support, and `participantIds` is ignored server-side — a help
|
|
650
|
-
*
|
|
688
|
+
* For `help_lab` the backend seats only the imaging centre plus Natoe
|
|
689
|
+
* support, and `participantIds` is ignored server-side — a help channel must
|
|
690
|
+
* never be widened by the client.
|
|
651
691
|
*/
|
|
652
692
|
kind?: ConversationKind;
|
|
653
693
|
}
|
|
@@ -684,7 +724,7 @@ interface UseConversationReturn {
|
|
|
684
724
|
* when the user sends the first message. If a conversation already exists for
|
|
685
725
|
* the given orderId, it's loaded and joined automatically.
|
|
686
726
|
*/
|
|
687
|
-
declare function useConversation({ orderId, patientData, participantIds, loadHistory, kind, }: UseConversationOptions): UseConversationReturn;
|
|
727
|
+
declare function useConversation({ orderId, conversation: providedConversation, patientData, participantIds, loadHistory, kind, }: UseConversationOptions): UseConversationReturn;
|
|
688
728
|
|
|
689
729
|
interface UseConversationListOptions {
|
|
690
730
|
/**
|
|
@@ -950,6 +990,13 @@ interface PatientHeaderProps {
|
|
|
950
990
|
hideName?: boolean;
|
|
951
991
|
/** Override the displayed name — use the stored conversation.name to avoid reconstructing from patientData components */
|
|
952
992
|
displayName?: string;
|
|
993
|
+
/**
|
|
994
|
+
* Render the support variant: a help channel has no patient, no study and no
|
|
995
|
+
* case, so the demographics line and every case-level action are suppressed
|
|
996
|
+
* and the title is the channel name. Without this the header would show a
|
|
997
|
+
* row of empty fields and a dead "View DICOM" button.
|
|
998
|
+
*/
|
|
999
|
+
isSupportChannel?: boolean;
|
|
953
1000
|
className?: string;
|
|
954
1001
|
}
|
|
955
1002
|
/**
|
|
@@ -967,7 +1014,7 @@ interface PatientHeaderProps {
|
|
|
967
1014
|
* When `hideName=true` (CollabPopup wraps the conversation in its own
|
|
968
1015
|
* window title), only the meta + actions row renders.
|
|
969
1016
|
*/
|
|
970
|
-
declare function PatientHeader({ patientData, participants, onOpenDicom, onOpenCase, onOpenSettings, onBack, hideName, displayName, className, }: PatientHeaderProps): react_jsx_runtime.JSX.Element;
|
|
1017
|
+
declare function PatientHeader({ patientData, participants, onOpenDicom, onOpenCase, onOpenSettings, onBack, hideName, displayName, isSupportChannel, className, }: PatientHeaderProps): react_jsx_runtime.JSX.Element;
|
|
971
1018
|
|
|
972
1019
|
interface MessageListHandle {
|
|
973
1020
|
/** Scroll to a specific message by ID (used by pin jump-to) */
|
|
@@ -1230,4 +1277,4 @@ declare const SUPPORTED_IMAGE_TYPES: string[];
|
|
|
1230
1277
|
/** Max file size in bytes (20MB) */
|
|
1231
1278
|
declare const MAX_FILE_SIZE: number;
|
|
1232
1279
|
|
|
1233
|
-
export { AUDIO_MIME_TYPE, type ChannelEvent, ChannelSettings, type ChannelUpdatePayload, type CollabConfig, type CollabError, CollabInbox, CollabInline, CollabPanel, CollabPopup, CollabProvider, CollabSocket, type Conversation, type ConversationKind, ConversationList, ConversationListItem, type ConversationListItem$1 as ConversationListItemData, type ConversationPreview, type CreateConversationResponse, DEEP_LINK_PREFIX, EVENTS, type InviteUserPayload, MAX_FILE_SIZE, MAX_PINNED_MESSAGES, MESSAGES_PAGE_SIZE, MESSAGE_TYPES, type Message, MessageActionsMenu, MessageBubble, MessageInput, MessageList, type MessageListHandle, type MessageSnapshot, type MessageType, type Participant, ParticipantsList, type PatientData, PatientHeader, PinnedMessagesBar, type PreviewBatchResponse, ReplyPreview, ReplyQuoteBlock, SUPPORTED_IMAGE_TYPES, SeenByIndicator, type SendMessagePayload, THEME_DEFAULTS, THEME_VAR, TYPING_DEBOUNCE_MS, type Theme, type TypingEvent, type UserRole, applyThemeOverrides, isHelpKind, useAudioRecorder, useChannelSettings, useCollab, useConversation, useConversationList, useDeepLinks, useInlineCollab, useMessages, usePinnedMessages, useUnreadCount };
|
|
1280
|
+
export { AUDIO_MIME_TYPE, type ChannelEvent, ChannelSettings, type ChannelUpdatePayload, type CollabConfig, type CollabError, CollabInbox, CollabInline, CollabPanel, CollabPopup, CollabProvider, CollabSocket, type Conversation, type ConversationKind, ConversationList, ConversationListItem, type ConversationListItem$1 as ConversationListItemData, type ConversationPreview, type CreateConversationResponse, DEEP_LINK_PREFIX, EVENTS, type InviteUserPayload, MAX_FILE_SIZE, MAX_PINNED_MESSAGES, MESSAGES_PAGE_SIZE, MESSAGE_TYPES, type Message, MessageActionsMenu, MessageBubble, MessageInput, MessageList, type MessageListHandle, type MessageSnapshot, type MessageType, type Participant, ParticipantsList, type PatientData, PatientHeader, PinnedMessagesBar, type PreviewBatchResponse, ReplyPreview, ReplyQuoteBlock, SUPPORTED_IMAGE_TYPES, SeenByIndicator, type SendMessagePayload, THEME_DEFAULTS, THEME_VAR, TYPING_DEBOUNCE_MS, type Theme, type TypingEvent, type UserRole, applyThemeOverrides, isCaseKind, isHelpKind, useAudioRecorder, useChannelSettings, useCollab, useConversation, useConversationList, useDeepLinks, useInlineCollab, useMessages, usePinnedMessages, useUnreadCount };
|
package/dist/index.d.ts
CHANGED
|
@@ -119,25 +119,40 @@ interface PatientData {
|
|
|
119
119
|
bodyParts?: string[];
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
|
-
*
|
|
122
|
+
* What a thread is about — and, crucially, what it is SCOPED to.
|
|
123
123
|
*
|
|
124
|
-
* - `case`
|
|
125
|
-
*
|
|
126
|
-
* - `
|
|
124
|
+
* - `case` — the discussion about one clinical order. Scoped to an order,
|
|
125
|
+
* so `orderId` is always set. Lab, radiologist, physician, admin.
|
|
126
|
+
* - `help_lab` — an imaging centre's help channel with Natoe support. Scoped to
|
|
127
|
+
* the LAB, not to any case: it has NO `orderId`, it outlives
|
|
128
|
+
* every order, and there is exactly one per imaging centre.
|
|
127
129
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
130
|
+
* `help_lab` was briefly modelled as per-case (one help thread per order). It is
|
|
131
|
+
* now per-lab, so support gets one continuous thread with each imaging centre
|
|
132
|
+
* rather than N short-lived ones scattered across cases. A case can still be
|
|
133
|
+
* referenced from inside the channel; it just no longer scopes it.
|
|
134
|
+
*
|
|
135
|
+
* Isolation — who can see a help channel — is enforced by the backend's
|
|
136
|
+
* participant seeding, never by anything in this package. Treat this as a label
|
|
137
|
+
* for rendering and for addressing the right thread, not as a security boundary.
|
|
132
138
|
*
|
|
133
139
|
* Optional everywhere, defaulting to `case`, so callers written before help
|
|
134
|
-
*
|
|
140
|
+
* channels existed keep their exact previous behaviour.
|
|
135
141
|
*/
|
|
136
|
-
type ConversationKind = 'case' | 'help_lab'
|
|
142
|
+
type ConversationKind = 'case' | 'help_lab';
|
|
137
143
|
declare function isHelpKind(kind?: ConversationKind | string): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* True when a thread is scoped to an order and therefore has patient context to
|
|
146
|
+
* render. Prefer this over testing `orderId` directly: a help channel has no
|
|
147
|
+
* order, so every patient-shaped affordance must be skipped for it.
|
|
148
|
+
*/
|
|
149
|
+
declare function isCaseKind(kind?: ConversationKind | string): boolean;
|
|
138
150
|
interface Conversation {
|
|
139
151
|
id: string;
|
|
140
|
-
|
|
152
|
+
/** Absent on a help channel — that thread belongs to a lab, not an order. */
|
|
153
|
+
orderId?: string;
|
|
154
|
+
/** Set only on a help channel: the imaging centre that owns it. */
|
|
155
|
+
labUserId?: string;
|
|
141
156
|
name: string;
|
|
142
157
|
kind?: ConversationKind;
|
|
143
158
|
picture?: string;
|
|
@@ -146,7 +161,8 @@ interface Conversation {
|
|
|
146
161
|
unreadCount: number;
|
|
147
162
|
createdAt: string;
|
|
148
163
|
updatedAt: string;
|
|
149
|
-
/** Denormalized patient data snapshot — lets UI render without extra fetch
|
|
164
|
+
/** Denormalized patient data snapshot — lets UI render without extra fetch.
|
|
165
|
+
* Always absent on a help channel: there is no patient. */
|
|
150
166
|
patientSnapshot?: PatientData;
|
|
151
167
|
/** Currently pinned messages (max 3) */
|
|
152
168
|
pinnedMessages?: Message[];
|
|
@@ -157,10 +173,12 @@ interface Conversation {
|
|
|
157
173
|
*/
|
|
158
174
|
interface ConversationListItem$1 {
|
|
159
175
|
id: string;
|
|
160
|
-
orderId
|
|
176
|
+
/** Absent on a help channel — see `Conversation.orderId`. */
|
|
177
|
+
orderId?: string;
|
|
178
|
+
labUserId?: string;
|
|
161
179
|
name: string;
|
|
162
|
-
/** Lets the inbox
|
|
163
|
-
*
|
|
180
|
+
/** Lets the inbox render the lab's help channel distinctly from its case
|
|
181
|
+
* threads — they appear as separate rows. */
|
|
164
182
|
kind?: ConversationKind;
|
|
165
183
|
picture?: string;
|
|
166
184
|
unreadCount: number;
|
|
@@ -285,10 +303,15 @@ interface InviteUserPayload {
|
|
|
285
303
|
}
|
|
286
304
|
|
|
287
305
|
interface CollabPanelProps {
|
|
288
|
-
/** Order
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
306
|
+
/** Order this thread belongs to. Required for a `case` thread; omitted for a
|
|
307
|
+
* help channel, which belongs to a lab rather than an order. */
|
|
308
|
+
orderId?: string;
|
|
309
|
+
/** A thread the caller already resolved — how a help channel is opened.
|
|
310
|
+
* Skips the order-keyed preview lookup entirely. */
|
|
311
|
+
conversation?: Conversation | null;
|
|
312
|
+
/** Patient/study data displayed in the header. Absent on a help channel:
|
|
313
|
+
* there is no patient, and the header renders its support variant instead. */
|
|
314
|
+
patientData?: PatientData;
|
|
292
315
|
/** Participant user IDs — only used when creating a brand-new conversation */
|
|
293
316
|
participantIds?: string[];
|
|
294
317
|
/** Which thread on the case. Defaults to the shared `case` thread. */
|
|
@@ -343,7 +366,7 @@ interface CollabPanelProps {
|
|
|
343
366
|
* Full collaboration panel: patient header + pinned bar + message thread + input.
|
|
344
367
|
* Supports reply, pin/unpin, and (optionally) seen-by indicators.
|
|
345
368
|
*/
|
|
346
|
-
declare function CollabPanel({ orderId, patientData, participantIds, kind, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, showSettings: controlledShowSettings, onSettingsChange, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
|
|
369
|
+
declare function CollabPanel({ orderId, conversation: providedConversation, patientData, participantIds, kind, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, showSettings: controlledShowSettings, onSettingsChange, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
|
|
347
370
|
|
|
348
371
|
interface CollabPopupProps {
|
|
349
372
|
/** Order ID for this conversation */
|
|
@@ -635,19 +658,36 @@ interface CollabProviderProps {
|
|
|
635
658
|
declare function CollabProvider({ config, apiBaseUrl, children }: CollabProviderProps): react_jsx_runtime.JSX.Element | null;
|
|
636
659
|
|
|
637
660
|
interface UseConversationOptions {
|
|
638
|
-
|
|
639
|
-
|
|
661
|
+
/**
|
|
662
|
+
* The order this thread belongs to. Required for a `case` thread, which is
|
|
663
|
+
* created lazily on first message and resolved by an order-keyed preview.
|
|
664
|
+
* Omitted for a help channel — that belongs to a lab, not an order.
|
|
665
|
+
*/
|
|
666
|
+
orderId?: string;
|
|
667
|
+
/**
|
|
668
|
+
* A thread the caller has ALREADY resolved, opened directly instead of being
|
|
669
|
+
* looked up by order.
|
|
670
|
+
*
|
|
671
|
+
* This is how a help channel opens. The backend's `GET /help_channel` does
|
|
672
|
+
* the get-or-create and hands back the conversation, so there is nothing to
|
|
673
|
+
* look up and nothing to lazily create — passing it here skips the preview
|
|
674
|
+
* request entirely. The preview endpoint is order-keyed and could not serve
|
|
675
|
+
* an order-less thread in any case.
|
|
676
|
+
*/
|
|
677
|
+
conversation?: Conversation | null;
|
|
678
|
+
/** Patient/study context. Absent on a help channel — there is no patient. */
|
|
679
|
+
patientData?: PatientData;
|
|
640
680
|
/** Participant user IDs used only when creating a brand-new conversation */
|
|
641
681
|
participantIds?: string[];
|
|
642
682
|
/** Load full history on mount (default true) */
|
|
643
683
|
loadHistory?: boolean;
|
|
644
684
|
/**
|
|
645
|
-
*
|
|
646
|
-
*
|
|
685
|
+
* What this thread is. Defaults to the shared `case` thread, so every
|
|
686
|
+
* existing caller is unaffected.
|
|
647
687
|
*
|
|
648
|
-
* For
|
|
649
|
-
* support, and `participantIds` is ignored server-side — a help
|
|
650
|
-
*
|
|
688
|
+
* For `help_lab` the backend seats only the imaging centre plus Natoe
|
|
689
|
+
* support, and `participantIds` is ignored server-side — a help channel must
|
|
690
|
+
* never be widened by the client.
|
|
651
691
|
*/
|
|
652
692
|
kind?: ConversationKind;
|
|
653
693
|
}
|
|
@@ -684,7 +724,7 @@ interface UseConversationReturn {
|
|
|
684
724
|
* when the user sends the first message. If a conversation already exists for
|
|
685
725
|
* the given orderId, it's loaded and joined automatically.
|
|
686
726
|
*/
|
|
687
|
-
declare function useConversation({ orderId, patientData, participantIds, loadHistory, kind, }: UseConversationOptions): UseConversationReturn;
|
|
727
|
+
declare function useConversation({ orderId, conversation: providedConversation, patientData, participantIds, loadHistory, kind, }: UseConversationOptions): UseConversationReturn;
|
|
688
728
|
|
|
689
729
|
interface UseConversationListOptions {
|
|
690
730
|
/**
|
|
@@ -950,6 +990,13 @@ interface PatientHeaderProps {
|
|
|
950
990
|
hideName?: boolean;
|
|
951
991
|
/** Override the displayed name — use the stored conversation.name to avoid reconstructing from patientData components */
|
|
952
992
|
displayName?: string;
|
|
993
|
+
/**
|
|
994
|
+
* Render the support variant: a help channel has no patient, no study and no
|
|
995
|
+
* case, so the demographics line and every case-level action are suppressed
|
|
996
|
+
* and the title is the channel name. Without this the header would show a
|
|
997
|
+
* row of empty fields and a dead "View DICOM" button.
|
|
998
|
+
*/
|
|
999
|
+
isSupportChannel?: boolean;
|
|
953
1000
|
className?: string;
|
|
954
1001
|
}
|
|
955
1002
|
/**
|
|
@@ -967,7 +1014,7 @@ interface PatientHeaderProps {
|
|
|
967
1014
|
* When `hideName=true` (CollabPopup wraps the conversation in its own
|
|
968
1015
|
* window title), only the meta + actions row renders.
|
|
969
1016
|
*/
|
|
970
|
-
declare function PatientHeader({ patientData, participants, onOpenDicom, onOpenCase, onOpenSettings, onBack, hideName, displayName, className, }: PatientHeaderProps): react_jsx_runtime.JSX.Element;
|
|
1017
|
+
declare function PatientHeader({ patientData, participants, onOpenDicom, onOpenCase, onOpenSettings, onBack, hideName, displayName, isSupportChannel, className, }: PatientHeaderProps): react_jsx_runtime.JSX.Element;
|
|
971
1018
|
|
|
972
1019
|
interface MessageListHandle {
|
|
973
1020
|
/** Scroll to a specific message by ID (used by pin jump-to) */
|
|
@@ -1230,4 +1277,4 @@ declare const SUPPORTED_IMAGE_TYPES: string[];
|
|
|
1230
1277
|
/** Max file size in bytes (20MB) */
|
|
1231
1278
|
declare const MAX_FILE_SIZE: number;
|
|
1232
1279
|
|
|
1233
|
-
export { AUDIO_MIME_TYPE, type ChannelEvent, ChannelSettings, type ChannelUpdatePayload, type CollabConfig, type CollabError, CollabInbox, CollabInline, CollabPanel, CollabPopup, CollabProvider, CollabSocket, type Conversation, type ConversationKind, ConversationList, ConversationListItem, type ConversationListItem$1 as ConversationListItemData, type ConversationPreview, type CreateConversationResponse, DEEP_LINK_PREFIX, EVENTS, type InviteUserPayload, MAX_FILE_SIZE, MAX_PINNED_MESSAGES, MESSAGES_PAGE_SIZE, MESSAGE_TYPES, type Message, MessageActionsMenu, MessageBubble, MessageInput, MessageList, type MessageListHandle, type MessageSnapshot, type MessageType, type Participant, ParticipantsList, type PatientData, PatientHeader, PinnedMessagesBar, type PreviewBatchResponse, ReplyPreview, ReplyQuoteBlock, SUPPORTED_IMAGE_TYPES, SeenByIndicator, type SendMessagePayload, THEME_DEFAULTS, THEME_VAR, TYPING_DEBOUNCE_MS, type Theme, type TypingEvent, type UserRole, applyThemeOverrides, isHelpKind, useAudioRecorder, useChannelSettings, useCollab, useConversation, useConversationList, useDeepLinks, useInlineCollab, useMessages, usePinnedMessages, useUnreadCount };
|
|
1280
|
+
export { AUDIO_MIME_TYPE, type ChannelEvent, ChannelSettings, type ChannelUpdatePayload, type CollabConfig, type CollabError, CollabInbox, CollabInline, CollabPanel, CollabPopup, CollabProvider, CollabSocket, type Conversation, type ConversationKind, ConversationList, ConversationListItem, type ConversationListItem$1 as ConversationListItemData, type ConversationPreview, type CreateConversationResponse, DEEP_LINK_PREFIX, EVENTS, type InviteUserPayload, MAX_FILE_SIZE, MAX_PINNED_MESSAGES, MESSAGES_PAGE_SIZE, MESSAGE_TYPES, type Message, MessageActionsMenu, MessageBubble, MessageInput, MessageList, type MessageListHandle, type MessageSnapshot, type MessageType, type Participant, ParticipantsList, type PatientData, PatientHeader, PinnedMessagesBar, type PreviewBatchResponse, ReplyPreview, ReplyQuoteBlock, SUPPORTED_IMAGE_TYPES, SeenByIndicator, type SendMessagePayload, THEME_DEFAULTS, THEME_VAR, TYPING_DEBOUNCE_MS, type Theme, type TypingEvent, type UserRole, applyThemeOverrides, isCaseKind, isHelpKind, useAudioRecorder, useChannelSettings, useCollab, useConversation, useConversationList, useDeepLinks, useInlineCollab, useMessages, usePinnedMessages, useUnreadCount };
|
package/dist/index.js
CHANGED
|
@@ -891,6 +891,7 @@ function cleanChannelName(name) {
|
|
|
891
891
|
// src/hooks/useConversation.ts
|
|
892
892
|
function useConversation({
|
|
893
893
|
orderId,
|
|
894
|
+
conversation: providedConversation,
|
|
894
895
|
patientData,
|
|
895
896
|
participantIds = [],
|
|
896
897
|
loadHistory = true,
|
|
@@ -923,6 +924,7 @@ function useConversation({
|
|
|
923
924
|
const markedReadIdsRef = React4.useRef(/* @__PURE__ */ new Set());
|
|
924
925
|
const buildName = React4.useCallback(
|
|
925
926
|
() => {
|
|
927
|
+
if (!patientData) return "";
|
|
926
928
|
const labName = patientData.labName ?? (config.userRole === "lab" ? config.userName : void 0);
|
|
927
929
|
return buildChannelName({ ...patientData, labName });
|
|
928
930
|
},
|
|
@@ -1022,6 +1024,28 @@ function useConversation({
|
|
|
1022
1024
|
setIsLoading(true);
|
|
1023
1025
|
setError(null);
|
|
1024
1026
|
try {
|
|
1027
|
+
if (providedConversation) {
|
|
1028
|
+
setConversation(providedConversation);
|
|
1029
|
+
setParticipants(providedConversation.participants ?? []);
|
|
1030
|
+
joinChannel(providedConversation);
|
|
1031
|
+
if (loadHistory) {
|
|
1032
|
+
const history = await fetchMessages(providedConversation.id);
|
|
1033
|
+
if (cancelled) return;
|
|
1034
|
+
setMessages(history);
|
|
1035
|
+
setHasMore(history.length >= MESSAGES_PAGE_SIZE);
|
|
1036
|
+
setPinnedMessages(history.filter((m) => m.isPinned));
|
|
1037
|
+
}
|
|
1038
|
+
if (!cancelled) setIsLoading(false);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
if (!orderId) {
|
|
1042
|
+
setConversation(null);
|
|
1043
|
+
setMessages([]);
|
|
1044
|
+
setParticipants([]);
|
|
1045
|
+
setHasMore(false);
|
|
1046
|
+
setIsLoading(false);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1025
1049
|
const preview = await requestPreview(orderId, kind);
|
|
1026
1050
|
if (cancelled) return;
|
|
1027
1051
|
if (!preview) {
|
|
@@ -1083,6 +1107,12 @@ function useConversation({
|
|
|
1083
1107
|
const ensureConversation = React4.useCallback(
|
|
1084
1108
|
async (payload) => {
|
|
1085
1109
|
if (conversation) return { conv: conversation, persistedByCreate: false };
|
|
1110
|
+
if (!orderId) {
|
|
1111
|
+
throw {
|
|
1112
|
+
code: "NO_CONVERSATION",
|
|
1113
|
+
message: "This conversation is not available yet. Please reopen it and try again."
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1086
1116
|
if (ensureConversationInFlight.current) {
|
|
1087
1117
|
const conv = await ensureConversationInFlight.current;
|
|
1088
1118
|
return { conv, persistedByCreate: false };
|
|
@@ -1570,15 +1600,19 @@ function PatientHeader({
|
|
|
1570
1600
|
onBack,
|
|
1571
1601
|
hideName = false,
|
|
1572
1602
|
displayName,
|
|
1603
|
+
isSupportChannel = false,
|
|
1573
1604
|
className
|
|
1574
1605
|
}) {
|
|
1575
|
-
const hasDicom = !!(patientData.studyId && patientData.storageId);
|
|
1576
|
-
const resolvedName = resolveDisplayName(patientData, displayName);
|
|
1606
|
+
const hasDicom = !isSupportChannel && !!(patientData.studyId && patientData.storageId);
|
|
1607
|
+
const resolvedName = isSupportChannel ? displayName || patientData.patientName || "Natoe Support" : resolveDisplayName(patientData, displayName);
|
|
1577
1608
|
const metaParts = [];
|
|
1578
|
-
if (
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
if (
|
|
1609
|
+
if (isSupportChannel) {
|
|
1610
|
+
metaParts.push("Help and support");
|
|
1611
|
+
}
|
|
1612
|
+
if (!isSupportChannel && patientData.patientAge) metaParts.push(String(patientData.patientAge));
|
|
1613
|
+
if (!isSupportChannel && patientData.patientSex) metaParts.push(String(patientData.patientSex));
|
|
1614
|
+
if (!isSupportChannel && patientData.studyType) metaParts.push(patientData.studyType);
|
|
1615
|
+
if (!isSupportChannel && patientData.bodyParts && patientData.bodyParts.length > 0) {
|
|
1582
1616
|
metaParts.push(patientData.bodyParts.join(", "));
|
|
1583
1617
|
}
|
|
1584
1618
|
const hasPrimaryActions = hasDicom && onOpenDicom || onOpenCase;
|
|
@@ -4535,6 +4569,7 @@ var DARK_THEME_OVERRIDES = {
|
|
|
4535
4569
|
};
|
|
4536
4570
|
function CollabPanel({
|
|
4537
4571
|
orderId,
|
|
4572
|
+
conversation: providedConversation,
|
|
4538
4573
|
patientData,
|
|
4539
4574
|
participantIds,
|
|
4540
4575
|
kind,
|
|
@@ -4576,7 +4611,13 @@ function CollabPanel({
|
|
|
4576
4611
|
loadMoreMessages,
|
|
4577
4612
|
pinMessage,
|
|
4578
4613
|
unpinMessage
|
|
4579
|
-
} = useConversation({
|
|
4614
|
+
} = useConversation({
|
|
4615
|
+
orderId,
|
|
4616
|
+
conversation: providedConversation,
|
|
4617
|
+
patientData,
|
|
4618
|
+
participantIds,
|
|
4619
|
+
kind
|
|
4620
|
+
});
|
|
4580
4621
|
React4.useEffect(() => {
|
|
4581
4622
|
onConversationChange?.(conversation);
|
|
4582
4623
|
}, [conversation, onConversationChange]);
|
|
@@ -4585,11 +4626,11 @@ function CollabPanel({
|
|
|
4585
4626
|
});
|
|
4586
4627
|
const { handleDeepLink } = useDeepLinks();
|
|
4587
4628
|
const handleOpenDicom = () => {
|
|
4588
|
-
if (patientData
|
|
4629
|
+
if (patientData?.studyId && patientData.storageId && config.onOpenDicom) {
|
|
4589
4630
|
config.onOpenDicom(patientData.studyId, patientData.storageId);
|
|
4590
4631
|
}
|
|
4591
4632
|
};
|
|
4592
|
-
const handleOpenCase = !hideOpenCase && config.onOpenCase ? () => config.onOpenCase?.(patientData.orderId, patientData.displayOrderId) : void 0;
|
|
4633
|
+
const handleOpenCase = !hideOpenCase && config.onOpenCase && patientData?.orderId ? () => config.onOpenCase?.(patientData.orderId, patientData.displayOrderId) : void 0;
|
|
4593
4634
|
const handleJumpToMessage = (messageId) => {
|
|
4594
4635
|
messageListRef.current?.scrollToMessage(messageId);
|
|
4595
4636
|
};
|
|
@@ -4662,7 +4703,8 @@ function CollabPanel({
|
|
|
4662
4703
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4663
4704
|
PatientHeader,
|
|
4664
4705
|
{
|
|
4665
|
-
patientData,
|
|
4706
|
+
patientData: patientData ?? { orderId: "", patientName: conversation?.name ?? "Natoe Support" },
|
|
4707
|
+
isSupportChannel: !patientData,
|
|
4666
4708
|
participants,
|
|
4667
4709
|
onOpenDicom: handleOpenDicom,
|
|
4668
4710
|
onOpenCase: handleOpenCase,
|
|
@@ -5901,7 +5943,10 @@ function useConversationList(options) {
|
|
|
5901
5943
|
|
|
5902
5944
|
// src/core/types.ts
|
|
5903
5945
|
function isHelpKind(kind) {
|
|
5904
|
-
return kind === "help_lab"
|
|
5946
|
+
return kind === "help_lab";
|
|
5947
|
+
}
|
|
5948
|
+
function isCaseKind(kind) {
|
|
5949
|
+
return kind === void 0 || kind === "case";
|
|
5905
5950
|
}
|
|
5906
5951
|
function ConversationListItem({
|
|
5907
5952
|
item,
|
|
@@ -6537,6 +6582,7 @@ function CollabInbox({
|
|
|
6537
6582
|
);
|
|
6538
6583
|
}
|
|
6539
6584
|
function buildPatientData(item) {
|
|
6585
|
+
if (isHelpKind(item.kind) || !item.orderId) return void 0;
|
|
6540
6586
|
const parsed = parseChannelName(item.name, item.orderId);
|
|
6541
6587
|
if (item.patientSnapshot) {
|
|
6542
6588
|
return {
|
|
@@ -6874,6 +6920,7 @@ exports.THEME_DEFAULTS = THEME_DEFAULTS;
|
|
|
6874
6920
|
exports.THEME_VAR = THEME_VAR;
|
|
6875
6921
|
exports.TYPING_DEBOUNCE_MS = TYPING_DEBOUNCE_MS;
|
|
6876
6922
|
exports.applyThemeOverrides = applyThemeOverrides;
|
|
6923
|
+
exports.isCaseKind = isCaseKind;
|
|
6877
6924
|
exports.isHelpKind = isHelpKind;
|
|
6878
6925
|
exports.useAudioRecorder = useAudioRecorder;
|
|
6879
6926
|
exports.useChannelSettings = useChannelSettings;
|