@natoe/colab 0.1.31 → 0.1.34

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 CHANGED
@@ -465,6 +465,49 @@ interface CollabInboxProps {
465
465
  */
466
466
  declare function CollabInbox({ initialConversationId, onSelectConversation, className, style, }: CollabInboxProps): react_jsx_runtime.JSX.Element;
467
467
 
468
+ interface HelpPanelProps {
469
+ /**
470
+ * The lab's help channel, already resolved by the host from
471
+ * `GET /help_channel`. Required — unlike a case thread there is nothing to
472
+ * look up and nothing to lazily create, so a HelpPanel without a channel has
473
+ * nothing to render.
474
+ */
475
+ conversation: Conversation;
476
+ /** Show "seen by" under own messages (default true) */
477
+ showSeenBy?: boolean;
478
+ /** Rendered above the composer — the host's case-reference control lives here. */
479
+ composerAccessory?: React.ReactNode;
480
+ className?: string;
481
+ style?: React.CSSProperties;
482
+ }
483
+ /**
484
+ * The imaging centre's support thread.
485
+ *
486
+ * A deliberately SEPARATE shell from CollabPanel rather than CollabPanel with
487
+ * flags. The two surfaces only look similar — CollabPanel is case-shaped, and
488
+ * serving support through it meant suppressing the patient header, the DICOM
489
+ * button, the open-case action, the participant roster and channel settings one
490
+ * at a time. Every case-oriented feature added another thing to suppress, and
491
+ * missing one is not cosmetic: a roster control that slipped through crashed
492
+ * the whole tree on an unmapped participant role.
493
+ *
494
+ * Building additively instead means a support thread can never inherit a
495
+ * case affordance by accident. What IS shared is the part worth sharing — the
496
+ * engine. useConversation, MessageList and MessageInput are identical here, so
497
+ * socket lifecycle, optimistic sends, read receipts, typing, attachments,
498
+ * audio and pagination all behave exactly as they do on a case thread, with no
499
+ * second implementation to drift.
500
+ *
501
+ * Deliberately absent, and none of it by oversight:
502
+ * - patient / study header — there is no patient
503
+ * - View DICOM, Open case — there is no order
504
+ * - participant roster — the roster is fixed by the backend (this lab
505
+ * plus Natoe support) and is not editable
506
+ * - channel settings — invite / remove / leave do not apply
507
+ * - pinned messages — a support thread is chronological
508
+ */
509
+ declare function HelpPanel({ conversation, showSeenBy, composerAccessory, className, style, }: HelpPanelProps): react_jsx_runtime.JSX.Element;
510
+
468
511
  type MessageCallback = (message: Message) => void;
469
512
  type TypingCallback = (event: TypingEvent) => void;
470
513
  type ParticipantCallback = (participant: Participant) => void;
@@ -1280,4 +1323,4 @@ declare const SUPPORTED_IMAGE_TYPES: string[];
1280
1323
  /** Max file size in bytes (20MB) */
1281
1324
  declare const MAX_FILE_SIZE: number;
1282
1325
 
1283
- 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 };
1326
+ 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, HelpPanel, 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
@@ -465,6 +465,49 @@ interface CollabInboxProps {
465
465
  */
466
466
  declare function CollabInbox({ initialConversationId, onSelectConversation, className, style, }: CollabInboxProps): react_jsx_runtime.JSX.Element;
467
467
 
468
+ interface HelpPanelProps {
469
+ /**
470
+ * The lab's help channel, already resolved by the host from
471
+ * `GET /help_channel`. Required — unlike a case thread there is nothing to
472
+ * look up and nothing to lazily create, so a HelpPanel without a channel has
473
+ * nothing to render.
474
+ */
475
+ conversation: Conversation;
476
+ /** Show "seen by" under own messages (default true) */
477
+ showSeenBy?: boolean;
478
+ /** Rendered above the composer — the host's case-reference control lives here. */
479
+ composerAccessory?: React.ReactNode;
480
+ className?: string;
481
+ style?: React.CSSProperties;
482
+ }
483
+ /**
484
+ * The imaging centre's support thread.
485
+ *
486
+ * A deliberately SEPARATE shell from CollabPanel rather than CollabPanel with
487
+ * flags. The two surfaces only look similar — CollabPanel is case-shaped, and
488
+ * serving support through it meant suppressing the patient header, the DICOM
489
+ * button, the open-case action, the participant roster and channel settings one
490
+ * at a time. Every case-oriented feature added another thing to suppress, and
491
+ * missing one is not cosmetic: a roster control that slipped through crashed
492
+ * the whole tree on an unmapped participant role.
493
+ *
494
+ * Building additively instead means a support thread can never inherit a
495
+ * case affordance by accident. What IS shared is the part worth sharing — the
496
+ * engine. useConversation, MessageList and MessageInput are identical here, so
497
+ * socket lifecycle, optimistic sends, read receipts, typing, attachments,
498
+ * audio and pagination all behave exactly as they do on a case thread, with no
499
+ * second implementation to drift.
500
+ *
501
+ * Deliberately absent, and none of it by oversight:
502
+ * - patient / study header — there is no patient
503
+ * - View DICOM, Open case — there is no order
504
+ * - participant roster — the roster is fixed by the backend (this lab
505
+ * plus Natoe support) and is not editable
506
+ * - channel settings — invite / remove / leave do not apply
507
+ * - pinned messages — a support thread is chronological
508
+ */
509
+ declare function HelpPanel({ conversation, showSeenBy, composerAccessory, className, style, }: HelpPanelProps): react_jsx_runtime.JSX.Element;
510
+
468
511
  type MessageCallback = (message: Message) => void;
469
512
  type TypingCallback = (event: TypingEvent) => void;
470
513
  type ParticipantCallback = (participant: Participant) => void;
@@ -1280,4 +1323,4 @@ declare const SUPPORTED_IMAGE_TYPES: string[];
1280
1323
  /** Max file size in bytes (20MB) */
1281
1324
  declare const MAX_FILE_SIZE: number;
1282
1325
 
1283
- 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 };
1326
+ 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, HelpPanel, 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
@@ -1645,7 +1645,7 @@ function PatientHeader({
1645
1645
  }
1646
1646
  )
1647
1647
  ] }) : null;
1648
- const settingsButton = onOpenSettings ? /* @__PURE__ */ jsxRuntime.jsxs(
1648
+ const settingsButton = onOpenSettings && !isSupportChannel ? /* @__PURE__ */ jsxRuntime.jsxs(
1649
1649
  "button",
1650
1650
  {
1651
1651
  onClick: onOpenSettings,
@@ -3937,6 +3937,7 @@ function fallbackName(role) {
3937
3937
  return "User";
3938
3938
  }
3939
3939
  }
3940
+ var FALLBACK_ROLE_COLOR = { bg: "#f1f5f9", text: "#64748b" };
3940
3941
  function RoleBadge({ role }) {
3941
3942
  const colors = {
3942
3943
  radiologist: { bg: "#eef2ff", text: "#4f46e5" },
@@ -3948,8 +3949,8 @@ function RoleBadge({ role }) {
3948
3949
  admin: { bg: "#f1f5f9", text: "#64748b" }
3949
3950
  // slate
3950
3951
  };
3951
- const color = colors[role];
3952
- return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles9.roleBadge, backgroundColor: color.bg, color: color.text }, children: role });
3952
+ const color = colors[role] ?? FALLBACK_ROLE_COLOR;
3953
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles9.roleBadge, backgroundColor: color.bg, color: color.text }, children: fallbackName(role) });
3953
3954
  }
3954
3955
  var styles9 = {
3955
3956
  list: {
@@ -4863,6 +4864,7 @@ function CollabPopup({
4863
4864
  const previouslyFocused = React4.useRef(null);
4864
4865
  const titleId = React4.useId();
4865
4866
  const titleText = cleanChannelName(loadedConversation?.name) || (patientData ? buildChannelName(patientData) : "Natoe Support");
4867
+ const isSupportChannel = !patientData;
4866
4868
  const handleMouseDown = React4.useCallback(
4867
4869
  (e) => {
4868
4870
  if (!e.target.closest("[data-drag-handle]")) return;
@@ -4981,7 +4983,7 @@ function CollabPopup({
4981
4983
  })()
4982
4984
  ] }),
4983
4985
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles13.titleActions, children: [
4984
- loadedConversation && /* @__PURE__ */ jsxRuntime.jsxs(
4986
+ loadedConversation && !isSupportChannel && /* @__PURE__ */ jsxRuntime.jsxs(
4985
4987
  "button",
4986
4988
  {
4987
4989
  type: "button",
@@ -6687,6 +6689,143 @@ var styles17 = {
6687
6689
  maxWidth: "320px"
6688
6690
  }
6689
6691
  };
6692
+ ensureGlobalStyles();
6693
+ function HelpPanel({
6694
+ conversation,
6695
+ showSeenBy = true,
6696
+ composerAccessory,
6697
+ className,
6698
+ style
6699
+ }) {
6700
+ const { config } = useCollab();
6701
+ const { handleDeepLink } = useDeepLinks();
6702
+ const messageListRef = React4.useRef(null);
6703
+ const {
6704
+ messages,
6705
+ typingUsers,
6706
+ isLoading,
6707
+ hasMore,
6708
+ error,
6709
+ replyTo,
6710
+ setReplyTo,
6711
+ sendMessage,
6712
+ retryMessage,
6713
+ sendAudioMessage,
6714
+ sendFileMessage,
6715
+ sendTyping,
6716
+ markAsRead,
6717
+ loadMoreMessages
6718
+ } = useConversation({ conversation, kind: "help_lab" });
6719
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6720
+ "div",
6721
+ {
6722
+ className: [ROOT_CLASS, className].filter(Boolean).join(" "),
6723
+ style: { ...styles18.container, ...style },
6724
+ children: [
6725
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { style: styles18.header, children: [
6726
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles18.avatar, "aria-hidden": "true", children: "N" }),
6727
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles18.headerText, children: [
6728
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles18.title, children: "Natoe Support" }),
6729
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles18.subtitle, children: "Questions about your account, billing or a case" })
6730
+ ] })
6731
+ ] }),
6732
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles18.error, role: "alert", children: error }),
6733
+ isLoading && messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles18.loading, children: /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(
6734
+ MessageList,
6735
+ {
6736
+ ref: messageListRef,
6737
+ messages,
6738
+ currentUserId: config.userId,
6739
+ showSeenBy,
6740
+ typingUsers,
6741
+ hasMore,
6742
+ isLoading,
6743
+ onLoadMore: loadMoreMessages,
6744
+ onDeepLinkClick: handleDeepLink,
6745
+ onMessageVisible: markAsRead,
6746
+ onReply: setReplyTo,
6747
+ onRetry: retryMessage,
6748
+ pinDisabled: true
6749
+ }
6750
+ ),
6751
+ composerAccessory,
6752
+ /* @__PURE__ */ jsxRuntime.jsx(
6753
+ MessageInput,
6754
+ {
6755
+ onSendText: sendMessage,
6756
+ onSendAudio: sendAudioMessage,
6757
+ onSendFile: sendFileMessage,
6758
+ onTyping: sendTyping,
6759
+ replyTo,
6760
+ onCancelReply: () => setReplyTo(null),
6761
+ placeholder: "Ask Natoe support\u2026"
6762
+ }
6763
+ )
6764
+ ]
6765
+ }
6766
+ );
6767
+ }
6768
+ var styles18 = {
6769
+ container: {
6770
+ display: "flex",
6771
+ flexDirection: "column",
6772
+ height: "100%",
6773
+ minHeight: 0,
6774
+ fontFamily: FONT_STACK,
6775
+ background: COLOR.white
6776
+ },
6777
+ header: {
6778
+ display: "flex",
6779
+ alignItems: "center",
6780
+ gap: SPACE.S3,
6781
+ padding: `${SPACE.S3} ${SPACE.S4}`,
6782
+ borderBottom: `1px solid ${COLOR.neutral200}`,
6783
+ flex: "0 0 auto"
6784
+ },
6785
+ avatar: {
6786
+ flex: "0 0 auto",
6787
+ width: 32,
6788
+ height: 32,
6789
+ borderRadius: RADIUS.full,
6790
+ background: COLOR.primary,
6791
+ color: COLOR.white,
6792
+ display: "flex",
6793
+ alignItems: "center",
6794
+ justifyContent: "center",
6795
+ fontSize: FONT_SIZE.md,
6796
+ fontWeight: FONT_WEIGHT.semibold
6797
+ },
6798
+ headerText: {
6799
+ display: "flex",
6800
+ flexDirection: "column",
6801
+ minWidth: 0
6802
+ },
6803
+ title: {
6804
+ fontSize: FONT_SIZE.md,
6805
+ fontWeight: FONT_WEIGHT.semibold,
6806
+ color: COLOR.neutral900
6807
+ },
6808
+ subtitle: {
6809
+ fontSize: FONT_SIZE.sm,
6810
+ color: COLOR.neutral500,
6811
+ overflow: "hidden",
6812
+ textOverflow: "ellipsis",
6813
+ whiteSpace: "nowrap"
6814
+ },
6815
+ error: {
6816
+ padding: `${SPACE.S2} ${SPACE.S4}`,
6817
+ fontSize: FONT_SIZE.sm,
6818
+ color: COLOR.danger,
6819
+ background: COLOR.dangerBg,
6820
+ flex: "0 0 auto"
6821
+ },
6822
+ loading: {
6823
+ flex: "1 1 auto",
6824
+ display: "flex",
6825
+ alignItems: "center",
6826
+ justifyContent: "center"
6827
+ }
6828
+ };
6690
6829
  function useMessages({
6691
6830
  conversationId,
6692
6831
  initialLimit = MESSAGES_PAGE_SIZE
@@ -6904,6 +7043,7 @@ exports.ConversationList = ConversationList;
6904
7043
  exports.ConversationListItem = ConversationListItem;
6905
7044
  exports.DEEP_LINK_PREFIX = DEEP_LINK_PREFIX;
6906
7045
  exports.EVENTS = EVENTS;
7046
+ exports.HelpPanel = HelpPanel;
6907
7047
  exports.MAX_FILE_SIZE = MAX_FILE_SIZE;
6908
7048
  exports.MAX_PINNED_MESSAGES = MAX_PINNED_MESSAGES;
6909
7049
  exports.MESSAGES_PAGE_SIZE = MESSAGES_PAGE_SIZE;