@flamingo-stack/openframe-frontend-core 0.0.222 → 0.0.223

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.
Files changed (183) hide show
  1. package/dist/{chunk-AIKPR6BE.js → chunk-4VHWSAQR.js} +6069 -5235
  2. package/dist/chunk-4VHWSAQR.js.map +1 -0
  3. package/dist/{chunk-GMNE7P7V.cjs → chunk-4WBVMIKQ.cjs} +26 -26
  4. package/dist/{chunk-GMNE7P7V.cjs.map → chunk-4WBVMIKQ.cjs.map} +1 -1
  5. package/dist/{chunk-QYRV6MKX.js → chunk-53EA4QPZ.js} +13 -7
  6. package/dist/chunk-53EA4QPZ.js.map +1 -0
  7. package/dist/{chunk-4JOWZLEL.cjs → chunk-7TKYFZ3L.cjs} +7 -7
  8. package/dist/{chunk-4JOWZLEL.cjs.map → chunk-7TKYFZ3L.cjs.map} +1 -1
  9. package/dist/{chunk-IZA5WF44.cjs → chunk-ABBU7QOM.cjs} +12 -12
  10. package/dist/{chunk-IZA5WF44.cjs.map → chunk-ABBU7QOM.cjs.map} +1 -1
  11. package/dist/{chunk-KM4KK2A6.cjs → chunk-ENDUMC7Z.cjs} +2558 -1724
  12. package/dist/chunk-ENDUMC7Z.cjs.map +1 -0
  13. package/dist/{chunk-CCSEXVI4.js → chunk-GYYGA24C.js} +2 -2
  14. package/dist/{chunk-RMOHKAHA.js → chunk-ISWWP2BH.js} +3 -3
  15. package/dist/{chunk-QNYH3WUU.cjs → chunk-JUWPBMQM.cjs} +13 -7
  16. package/dist/chunk-JUWPBMQM.cjs.map +1 -0
  17. package/dist/{chunk-A2SFHYVF.cjs → chunk-KXLTWANQ.cjs} +10 -10
  18. package/dist/{chunk-A2SFHYVF.cjs.map → chunk-KXLTWANQ.cjs.map} +1 -1
  19. package/dist/{chunk-ND24XSVT.js → chunk-TA6Z2AKD.js} +3 -3
  20. package/dist/chunk-TA6Z2AKD.js.map +1 -0
  21. package/dist/{chunk-HRIAZQ5D.js → chunk-WOKB6RJ7.js} +3 -3
  22. package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
  23. package/dist/components/chat/chat-composer.d.ts.map +1 -1
  24. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  25. package/dist/components/chat/chat-message-skeleton.d.ts +19 -4
  26. package/dist/components/chat/chat-message-skeleton.d.ts.map +1 -1
  27. package/dist/components/chat/chat-panel-header-mobile.d.ts +24 -0
  28. package/dist/components/chat/chat-panel-header-mobile.d.ts.map +1 -0
  29. package/dist/components/chat/chat-panel-header.d.ts +6 -0
  30. package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
  31. package/dist/components/chat/chat-quick-action-row.d.ts +31 -0
  32. package/dist/components/chat/chat-quick-action-row.d.ts.map +1 -0
  33. package/dist/components/chat/embeddable-chat.d.ts +27 -0
  34. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  35. package/dist/components/chat/entity-cards/dispatch.d.ts +14 -0
  36. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
  37. package/dist/components/chat/entity-cards/github-activity-card.d.ts +10 -1
  38. package/dist/components/chat/entity-cards/github-activity-card.d.ts.map +1 -1
  39. package/dist/components/chat/guide-welcome.d.ts.map +1 -1
  40. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +9 -0
  41. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  42. package/dist/components/chat/hooks/use-slash-commands.d.ts +26 -0
  43. package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
  44. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +12 -1
  45. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  46. package/dist/components/chat/hooks/use-unified-chat.d.ts +12 -0
  47. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
  48. package/dist/components/chat/index.cjs +9 -3
  49. package/dist/components/chat/index.cjs.map +1 -1
  50. package/dist/components/chat/index.js +8 -2
  51. package/dist/components/chat/mingo-chat-history.d.ts +10 -0
  52. package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
  53. package/dist/components/chat/mingo-info-card.d.ts +63 -0
  54. package/dist/components/chat/mingo-info-card.d.ts.map +1 -0
  55. package/dist/components/chat/mingo-welcome.d.ts +14 -1
  56. package/dist/components/chat/mingo-welcome.d.ts.map +1 -1
  57. package/dist/components/chat/types/chat.types.d.ts +6 -0
  58. package/dist/components/chat/types/chat.types.d.ts.map +1 -1
  59. package/dist/components/chat/types/unified-chat-state.types.d.ts +30 -0
  60. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  61. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  62. package/dist/components/contact/index.cjs +4 -4
  63. package/dist/components/contact/index.js +3 -3
  64. package/dist/components/features/index.cjs +3 -3
  65. package/dist/components/features/index.js +2 -2
  66. package/dist/components/index.cjs +110 -104
  67. package/dist/components/index.cjs.map +1 -1
  68. package/dist/components/index.js +36 -30
  69. package/dist/components/index.js.map +1 -1
  70. package/dist/components/navigation/app-layout-drawer.d.ts +7 -0
  71. package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
  72. package/dist/components/navigation/index.cjs +3 -3
  73. package/dist/components/navigation/index.js +2 -2
  74. package/dist/components/onboarding-guides/index.cjs +26 -26
  75. package/dist/components/onboarding-guides/index.js +4 -4
  76. package/dist/components/tickets/index.cjs +67 -67
  77. package/dist/components/tickets/index.js +5 -5
  78. package/dist/components/ui/actions-menu.d.ts +9 -0
  79. package/dist/components/ui/actions-menu.d.ts.map +1 -1
  80. package/dist/components/ui/dropdown-button.d.ts +2 -2
  81. package/dist/components/ui/dropdown-button.d.ts.map +1 -1
  82. package/dist/components/ui/index.cjs +9 -3
  83. package/dist/components/ui/index.cjs.map +1 -1
  84. package/dist/components/ui/index.js +8 -2
  85. package/dist/components/ui/modal-v2.d.ts.map +1 -1
  86. package/dist/components/ui/more-actions-menu.d.ts +13 -0
  87. package/dist/components/ui/more-actions-menu.d.ts.map +1 -1
  88. package/dist/components/ui/square-avatar.d.ts +4 -0
  89. package/dist/components/ui/square-avatar.d.ts.map +1 -1
  90. package/dist/components/ui/textarea.d.ts.map +1 -1
  91. package/dist/hooks/index.cjs +2 -2
  92. package/dist/hooks/index.js +1 -1
  93. package/dist/hooks/ui/use-auto-limit-tags.d.ts +7 -1
  94. package/dist/hooks/ui/use-auto-limit-tags.d.ts.map +1 -1
  95. package/dist/index.cjs +9 -3
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.js +8 -2
  98. package/package.json +1 -1
  99. package/src/components/chat/chat-archive-page.tsx +32 -7
  100. package/src/components/chat/chat-composer.tsx +6 -2
  101. package/src/components/chat/chat-message-enhanced.tsx +24 -7
  102. package/src/components/chat/chat-message-list.tsx +1 -1
  103. package/src/components/chat/chat-message-skeleton.tsx +60 -60
  104. package/src/components/chat/chat-panel-header-mobile.tsx +94 -0
  105. package/src/components/chat/chat-panel-header.tsx +78 -50
  106. package/src/components/chat/chat-quick-action-row.tsx +146 -0
  107. package/src/components/chat/embeddable-chat.tsx +140 -45
  108. package/src/components/chat/entity-cards/dispatch.tsx +770 -232
  109. package/src/components/chat/entity-cards/github-activity-card.tsx +9 -3
  110. package/src/components/chat/guide-welcome.tsx +11 -8
  111. package/src/components/chat/hooks/use-nats-chat-adapter.ts +80 -7
  112. package/src/components/chat/hooks/use-slash-commands.ts +39 -0
  113. package/src/components/chat/hooks/use-sse-chat-adapter.ts +49 -45
  114. package/src/components/chat/hooks/use-unified-chat.ts +31 -8
  115. package/src/components/chat/mingo-chat-history.tsx +118 -36
  116. package/src/components/chat/mingo-info-card.tsx +214 -0
  117. package/src/components/chat/mingo-welcome.tsx +89 -36
  118. package/src/components/chat/types/chat.types.ts +4 -0
  119. package/src/components/chat/types/unified-chat-state.types.ts +35 -0
  120. package/src/components/chat/utils/process-historical-messages.ts +8 -0
  121. package/src/components/content-loading-container.tsx +1 -1
  122. package/src/components/empty-state.tsx +3 -3
  123. package/src/components/faq-accordion.tsx +2 -2
  124. package/src/components/features/changelog-manager.tsx +2 -2
  125. package/src/components/features/highlight-config-section.tsx +2 -2
  126. package/src/components/features/og-editor-preview.tsx +3 -3
  127. package/src/components/features/release-media-manager.tsx +2 -2
  128. package/src/components/loading/card-skeleton.tsx +3 -3
  129. package/src/components/loading/device-card-skeleton.tsx +1 -1
  130. package/src/components/loading/margin-report-skeleton.tsx +2 -2
  131. package/src/components/loading/organization-card-skeleton.tsx +1 -1
  132. package/src/components/loading/organization-icon-skeleton.tsx +1 -1
  133. package/src/components/loading/page-layout-skeleton.tsx +9 -9
  134. package/src/components/loading/users-grid-skeleton.tsx +2 -2
  135. package/src/components/logs-list.tsx +1 -1
  136. package/src/components/media-carousel.tsx +1 -1
  137. package/src/components/navigation/app-layout-drawer.tsx +70 -6
  138. package/src/components/open-source-features.tsx +2 -2
  139. package/src/components/ui/actions-menu.tsx +36 -13
  140. package/src/components/ui/allowed-domains-input.tsx +3 -3
  141. package/src/components/ui/cursor-pagination.tsx +1 -1
  142. package/src/components/ui/dropdown-button.tsx +6 -6
  143. package/src/components/ui/info-row.tsx +2 -2
  144. package/src/components/ui/modal-v2.tsx +8 -1
  145. package/src/components/ui/more-actions-menu.tsx +13 -0
  146. package/src/components/ui/square-avatar.tsx +6 -1
  147. package/src/components/ui/tags-input.tsx +1 -1
  148. package/src/components/ui/textarea.tsx +18 -4
  149. package/src/components/vendor-icon.tsx +1 -1
  150. package/src/components/vendor-tag.tsx +1 -1
  151. package/src/components/why-it-matters.tsx +1 -1
  152. package/src/hooks/ui/use-auto-limit-tags.ts +22 -8
  153. package/src/stories/ActionsMenu.stories.tsx +256 -0
  154. package/src/stories/AppLayoutSidebar.stories.tsx +6 -0
  155. package/src/stories/Board.stories.tsx +1 -1
  156. package/src/stories/ChatMingoInfoCards.stories.tsx +748 -0
  157. package/src/stories/ChatTicketList.stories.tsx +1 -1
  158. package/src/stories/DataTable.stories.tsx +17 -13
  159. package/src/stories/EmbeddableChat.stories.tsx +233 -1
  160. package/src/stories/MingoInfoCard.stories.tsx +193 -0
  161. package/src/stories/MingoOnboardingCard.stories.tsx +1 -1
  162. package/src/stories/Modal.stories.tsx +1 -0
  163. package/src/stories/NotificationDrawer.stories.tsx +14 -3
  164. package/src/stories/PageActions.stories.tsx +4 -4
  165. package/src/stories/PageContainer.stories.tsx +5 -5
  166. package/src/stories/PageLayout.stories.tsx +1 -1
  167. package/src/stories/TabSelector.stories.tsx +2 -2
  168. package/src/stories/Tag.stories.tsx +10 -5
  169. package/src/stories/TicketInfoSection.stories.tsx +23 -11
  170. package/src/stories/Toaster.stories.tsx +5 -5
  171. package/src/stories/__fixtures__/chat-card-decorator.tsx +44 -0
  172. package/dist/chunk-AIKPR6BE.js.map +0 -1
  173. package/dist/chunk-KM4KK2A6.cjs.map +0 -1
  174. package/dist/chunk-ND24XSVT.js.map +0 -1
  175. package/dist/chunk-QNYH3WUU.cjs.map +0 -1
  176. package/dist/chunk-QYRV6MKX.js.map +0 -1
  177. package/src/stories/.TicketDescriptionViewer.stories.md +0 -39
  178. package/src/stories/.TicketKnowledgeBaseList.stories.md +0 -36
  179. package/src/stories/TicketDescriptionViewer.stories.tsx +0 -70
  180. package/src/stories/TicketKnowledgeBaseList.stories.tsx +0 -51
  181. /package/dist/{chunk-CCSEXVI4.js.map → chunk-GYYGA24C.js.map} +0 -0
  182. /package/dist/{chunk-RMOHKAHA.js.map → chunk-ISWWP2BH.js.map} +0 -0
  183. /package/dist/{chunk-HRIAZQ5D.js.map → chunk-WOKB6RJ7.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from "@storybook/nextjs-vite";
2
2
  import type React from "react";
3
- import type { ChatTicketItemData } from "../components/chat/chat-ticket-item";
3
+ import type { ChatTicketItemData } from "../components/chat/entity-cards/chat-ticket-item";
4
4
  import { ChatTicketList } from "../components/chat/chat-ticket-list";
5
5
 
6
6
  const mockTickets: ChatTicketItemData[] = [
@@ -21,7 +21,7 @@ import {
21
21
  import { Button } from '../components/ui/button'
22
22
  import { Checkbox } from '../components/ui/checkbox'
23
23
  import { Tag } from '../components/ui/tag'
24
- import { MoreActionsMenu } from '../components/ui/more-actions-menu'
24
+ import { ActionsMenuDropdown } from '../components/ui/actions-menu'
25
25
  import { Chevron02RightIcon } from '../components/icons-v2-generated'
26
26
 
27
27
  /* ──────────────────────────────── sample data ───────────────────────────────── */
@@ -580,12 +580,14 @@ export const WithActionsColumn: Story = {
580
580
  data-no-row-click
581
581
  className="flex gap-2 items-center justify-end pointer-events-auto"
582
582
  >
583
- <MoreActionsMenu
584
- items={[
585
- { label: 'Ping', onClick: () => alert(`Ping ${device.hostname}`) },
586
- { label: 'Restart', onClick: () => alert(`Restart ${device.hostname}`) },
587
- { label: 'Remove', onClick: () => alert(`Remove ${device.hostname}`), danger: true },
588
- ]}
583
+ <ActionsMenuDropdown
584
+ groups={[{
585
+ items: [
586
+ { id: 'ping', label: 'Ping', onClick: () => alert(`Ping ${device.hostname}`) },
587
+ { id: 'restart', label: 'Restart', onClick: () => alert(`Restart ${device.hostname}`) },
588
+ { id: 'remove', label: 'Remove', onClick: () => alert(`Remove ${device.hostname}`), danger: true },
589
+ ],
590
+ }]}
589
591
  />
590
592
  </div>
591
593
  )
@@ -1328,12 +1330,14 @@ export const KitchenSink: Story = {
1328
1330
  data-no-row-click
1329
1331
  className="flex gap-2 items-center justify-end pointer-events-auto"
1330
1332
  >
1331
- <MoreActionsMenu
1332
- items={[
1333
- { label: 'Ping', onClick: () => alert(`Ping ${device.hostname}`) },
1334
- { label: 'Restart', onClick: () => alert(`Restart ${device.hostname}`) },
1335
- { label: 'Remove', onClick: () => alert(`Remove ${device.hostname}`), danger: true },
1336
- ]}
1333
+ <ActionsMenuDropdown
1334
+ groups={[{
1335
+ items: [
1336
+ { id: 'ping', label: 'Ping', onClick: () => alert(`Ping ${device.hostname}`) },
1337
+ { id: 'restart', label: 'Restart', onClick: () => alert(`Restart ${device.hostname}`) },
1338
+ { id: 'remove', label: 'Remove', onClick: () => alert(`Remove ${device.hostname}`), danger: true },
1339
+ ],
1340
+ }]}
1337
1341
  />
1338
1342
  </div>
1339
1343
  )
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/nextjs-vite'
2
+ import { within, userEvent } from 'storybook/test'
2
3
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
3
4
  import React, { useMemo } from 'react'
4
5
  import {
@@ -6,9 +7,19 @@ import {
6
7
  type ChatRuntime,
7
8
  } from '../contexts/chat-runtime-context'
8
9
  import { EmbeddableChat } from '../components/chat/embeddable-chat'
9
- import type { UseNatsChatAdapterConfig } from '../components/chat/hooks/use-nats-chat-adapter'
10
+ import type {
11
+ UseNatsChatAdapterConfig,
12
+ FetchDialogsResult,
13
+ FetchDialogMessagesResult,
14
+ } from '../components/chat/hooks/use-nats-chat-adapter'
10
15
  import type { SlashCommandSummary } from '../components/chat/hooks/use-slash-commands'
11
16
  import type { DialogItem } from '../components/chat/types/component.types'
17
+ import type { MingoQuickAction } from '../components/chat/mingo-welcome'
18
+ import {
19
+ BracketCurlyIcon,
20
+ Rocket01Icon,
21
+ SearchIcon,
22
+ } from '../components/icons-v2-generated'
12
23
 
13
24
  // =============================================================================
14
25
  // Stub commands endpoint — module-level constant referenced both by the
@@ -104,6 +115,42 @@ const SAMPLE_DIALOGS: ReadonlyArray<DialogItem> = [
104
115
  { id: 'd8', title: 'SQL Server maintenance plan troubleshooting' },
105
116
  ]
106
117
 
118
+ // Caller-provided Mingo quick-action chips — rendered after the built-in
119
+ // "Start Guide Chat" chip in the welcome/returning-user view. `onClick` just
120
+ // logs; the story demonstrates the chip row layout, not the action behaviour.
121
+ const SAMPLE_QUICK_ACTIONS: ReadonlyArray<MingoQuickAction> = [
122
+ {
123
+ id: 'weekly-log-summary',
124
+ label: 'Weekly Log Summary',
125
+ icon: <SearchIcon size={16} />,
126
+ onClick: () => console.log('[story] quick action: weekly log summary'),
127
+ },
128
+ {
129
+ id: 'run-script',
130
+ label: 'Run a script',
131
+ icon: <BracketCurlyIcon size={16} />,
132
+ onClick: () => console.log('[story] quick action: run a script'),
133
+ },
134
+ {
135
+ id: 'device-health',
136
+ label: 'Device health check',
137
+ icon: <Rocket01Icon size={16} />,
138
+ onClick: () => console.log('[story] quick action: device health check'),
139
+ },
140
+ {
141
+ id: 'summarize-tickets',
142
+ label: 'Summarize open tickets',
143
+ icon: <SearchIcon size={16} />,
144
+ onClick: () => console.log('[story] quick action: summarize tickets'),
145
+ },
146
+ {
147
+ id: 'patch-status',
148
+ label: 'Patch status report',
149
+ icon: <BracketCurlyIcon size={16} />,
150
+ onClick: () => console.log('[story] quick action: patch status report'),
151
+ },
152
+ ]
153
+
107
154
  /** Mingo config whose `fetchDialogs` resolves a single page of history (with
108
155
  * timestamps so the list splits into Today / Yesterday) plus working
109
156
  * rename/archive callbacks — used by the returning-user story. */
@@ -149,6 +196,69 @@ function createMockMingoConfigWithDialogs(): UseNatsChatAdapterConfig {
149
196
  }
150
197
  }
151
198
 
199
+ /**
200
+ * A promise that never settles — keeps whichever adapter loading flag it
201
+ * backs pinned `true`, so the loading UI stays on screen for the story
202
+ * (no fake timers, no flicker to a resolved state).
203
+ */
204
+ const pending = <T,>(): Promise<T> => new Promise<T>(() => {})
205
+
206
+ /**
207
+ * Chats-loading config — `fetchDialogs` never resolves, so the adapter keeps
208
+ * `isDialogsLoading` true with an empty list. EmbeddableChat reports
209
+ * `dialogsInitialLoading` and the Mingo empty state shows its history
210
+ * skeleton instead of the new/returning-user greeting.
211
+ */
212
+ function createMockMingoConfigChatsLoading(): UseNatsChatAdapterConfig {
213
+ return {
214
+ ...createMockMingoConfig(),
215
+ fetchDialogs: () => pending<FetchDialogsResult>(),
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Chat-loading config — the dialog list resolves normally, but
221
+ * `fetchDialogMessages` never resolves. Paired with the story's `play`
222
+ * (which opens the first dialog), this pins `isMessagesLoading` with no
223
+ * messages yet, so the conversation view shows the message-list skeleton.
224
+ */
225
+ function createMockMingoConfigChatLoading(): UseNatsChatAdapterConfig {
226
+ return {
227
+ ...createMockMingoConfigWithDialogs(),
228
+ fetchDialogMessages: () => pending<FetchDialogMessagesResult>(),
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Chats-error config — `fetchDialogs` rejects, so the adapter flips
234
+ * `dialogsError` true with an empty list. EmbeddableChat reports
235
+ * `dialogsLoadError`, and the Mingo empty state shows its load-error + retry
236
+ * affordance instead of the greeting (distinguishing a backend failure from a
237
+ * genuinely empty list). `reloadDialogs` re-invokes this — the retry path is
238
+ * wired, it just keeps failing against the unavailable mock backend.
239
+ */
240
+ function createMockMingoConfigChatsError(): UseNatsChatAdapterConfig {
241
+ return {
242
+ ...createMockMingoConfig(),
243
+ fetchDialogs: () =>
244
+ Promise.reject(new Error('Story: dialog list backend unavailable')),
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Archive-empty config — a populated dialog list (so the header's archive
250
+ * button is present), but `fetchArchivedDialogs` resolves an empty page. Paired
251
+ * with the story's `play` (which opens the archive page), this renders the
252
+ * archive page's empty state.
253
+ */
254
+ function createMockMingoConfigArchiveEmpty(): UseNatsChatAdapterConfig {
255
+ return {
256
+ ...createMockMingoConfigWithDialogs(),
257
+ fetchArchivedDialogs: () =>
258
+ Promise.resolve({ dialogs: [], nextCursor: null }),
259
+ }
260
+ }
261
+
152
262
  // =============================================================================
153
263
  // Mocked slash-commands — fed into the empty-state card list (Figma node
154
264
  // 7363:205938) via a `window.fetch` override below. Each entry must set
@@ -452,6 +562,9 @@ export const BothModes: Story = {
452
562
  * returning-user form: the "New to OpenFrame?" notification is hidden and the
453
563
  * "Start Guide Chat" chip drops from the accent yellow to the muted outline
454
564
  * style. The dialog list itself renders inline via MingoChatHistory.
565
+ *
566
+ * Caller-provided `quickActions` chips render in the pinned chip row after
567
+ * "Start Guide Chat" — the first is `primary` (accent), the rest `outline`.
455
568
  */
456
569
  export const ReturningUser: Story = {
457
570
  render: (args) => (
@@ -464,7 +577,126 @@ export const ReturningUser: Story = {
464
577
  defaultActiveMode="mingo"
465
578
  defaultOpen
466
579
  showInternalTrigger={false}
580
+ mingoWelcome={{ quickActions: SAMPLE_QUICK_ACTIONS }}
467
581
  />
468
582
  ),
469
583
  args: {},
470
584
  }
585
+
586
+ // =============================================================================
587
+ // 5. Chats loading — dialog list fetch in flight
588
+ // =============================================================================
589
+
590
+ /**
591
+ * Dialog-list loading state. The mock `fetchDialogs` never resolves, so the
592
+ * adapter keeps `isDialogsLoading` true with an empty list. The Mingo empty
593
+ * state renders its history skeleton (instead of the new/returning-user
594
+ * greeting), matching what the panel shows on first open before the chat list
595
+ * lands.
596
+ */
597
+ export const ChatsLoading: Story = {
598
+ render: (args) => (
599
+ <EmbeddableChat
600
+ {...args}
601
+ modes={{
602
+ mingo: createMockMingoConfigChatsLoading(),
603
+ }}
604
+ defaultActiveMode="mingo"
605
+ defaultOpen
606
+ showInternalTrigger={false}
607
+ />
608
+ ),
609
+ args: {},
610
+ }
611
+
612
+ // =============================================================================
613
+ // 6. Chat loading — opening a dialog whose history is in flight
614
+ // =============================================================================
615
+
616
+ /**
617
+ * Single-conversation loading state. The dialog list resolves, but
618
+ * `fetchDialogMessages` never resolves. The `play` step opens the first
619
+ * dialog, so the panel flips to the conversation view and shows the
620
+ * message-list skeleton (`isMessagesLoading` with no messages yet) — the
621
+ * "opening a chat" loading frame before bubbles stream in.
622
+ */
623
+ export const ChatLoading: Story = {
624
+ render: (args) => (
625
+ <EmbeddableChat
626
+ {...args}
627
+ modes={{
628
+ mingo: createMockMingoConfigChatLoading(),
629
+ }}
630
+ defaultActiveMode="mingo"
631
+ defaultOpen
632
+ showInternalTrigger={false}
633
+ />
634
+ ),
635
+ args: {},
636
+ play: async () => {
637
+ // The drawer portals to <body>, so query the document, not the canvas.
638
+ const body = within(document.body)
639
+ const row = await body.findByText(SAMPLE_DIALOGS[0].title)
640
+ await userEvent.click(row)
641
+ },
642
+ }
643
+
644
+ // =============================================================================
645
+ // 7. Chats error — dialog list fetch failed
646
+ // =============================================================================
647
+
648
+ /**
649
+ * Dialog-list error state. The mock `fetchDialogs` rejects, so the adapter
650
+ * sets `dialogsError` with an empty list. EmbeddableChat reports
651
+ * `dialogsLoadError` and the Mingo empty state shows its error message + retry
652
+ * button instead of the greeting — what the panel shows when the chat backend
653
+ * is unreachable. Clicking retry re-runs the (still-failing) fetch.
654
+ */
655
+ export const ChatsError: Story = {
656
+ render: (args) => (
657
+ <EmbeddableChat
658
+ {...args}
659
+ modes={{
660
+ mingo: createMockMingoConfigChatsError(),
661
+ }}
662
+ defaultActiveMode="mingo"
663
+ defaultOpen
664
+ showInternalTrigger={false}
665
+ />
666
+ ),
667
+ args: {},
668
+ }
669
+
670
+ // =============================================================================
671
+ // 8. Archive empty — archive page with no archived chats
672
+ // =============================================================================
673
+
674
+ /**
675
+ * Archive page empty state. The dialog list is populated (so the header's
676
+ * archive button renders), but `fetchArchivedDialogs` resolves an empty page.
677
+ * The `play` step clicks the archive button to open the Chat Archive page,
678
+ * which — with nothing archived — shows its centred empty state (icon + title +
679
+ * hint) rather than the dialog list or skeleton.
680
+ */
681
+ export const ArchiveEmpty: Story = {
682
+ render: (args) => (
683
+ <EmbeddableChat
684
+ {...args}
685
+ modes={{
686
+ mingo: createMockMingoConfigArchiveEmpty(),
687
+ }}
688
+ defaultActiveMode="mingo"
689
+ defaultOpen
690
+ showInternalTrigger={false}
691
+ />
692
+ ),
693
+ args: {},
694
+ play: async () => {
695
+ // The drawer portals to <body>, so query the document, not the canvas.
696
+ const body = within(document.body)
697
+ const archiveButton = await body.findByRole('button', {
698
+ name: 'Chat archive',
699
+ })
700
+ await userEvent.click(archiveButton)
701
+ },
702
+ }
@@ -0,0 +1,193 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite'
2
+ import { MingoInfoCard } from '../components/chat/mingo-info-card'
3
+ import { MonitorIcon } from '../components/icons-v2-generated/devices/monitor-icon'
4
+ import { Copy01Icon } from '../components/icons-v2-generated/documents/copy-01-icon'
5
+ import { TrashIcon } from '../components/icons-v2-generated/interface/trash-icon'
6
+
7
+ const AVATAR_SRC = 'https://i.pravatar.cc/80?img=12'
8
+
9
+ const SAMPLE_MENU = [
10
+ {
11
+ items: [
12
+ {
13
+ id: 'copy',
14
+ label: 'Copy link',
15
+ icon: <Copy01Icon size={16} />,
16
+ onClick: () => {
17
+ // eslint-disable-next-line no-console
18
+ console.log('[story] copy link')
19
+ },
20
+ },
21
+ {
22
+ id: 'remove',
23
+ label: 'Remove',
24
+ icon: <TrashIcon size={16} />,
25
+ danger: true,
26
+ onClick: () => {
27
+ // eslint-disable-next-line no-console
28
+ console.log('[story] remove')
29
+ },
30
+ },
31
+ ],
32
+ },
33
+ ]
34
+
35
+ const meta: Meta<typeof MingoInfoCard> = {
36
+ title: 'Chat/MingoInfoCard',
37
+ component: MingoInfoCard,
38
+ parameters: {
39
+ // `fullscreen` (not `centered`): `centered` shrinks the wrapper to the
40
+ // content, so the card's `w-full` resolves against a content-sized box and
41
+ // never stretches. A full-width block container lets the card fill 100% and
42
+ // shrink with the canvas — resize the Storybook viewport to see it reflow.
43
+ layout: 'fullscreen',
44
+ docs: {
45
+ description: {
46
+ component:
47
+ 'Figma node `2410:6007`. Horizontal info row: a leading 40px avatar (`imageSrc`) or boxed glyph (`icon`), a title + optional description column, an optional status pill, and a trailing "⋯" overflow menu separated by a vertical divider. Background `ods-card`, border `ods-border`, `rounded-md`. The card is `w-full` — it fills whatever block container it is placed in.',
48
+ },
49
+ },
50
+ },
51
+ argTypes: {
52
+ icon: { control: false },
53
+ menuGroups: { control: false },
54
+ onClick: { control: false },
55
+ },
56
+ decorators: [
57
+ (Story) => (
58
+ <div className="w-full bg-ods-bg p-6">
59
+ <Story />
60
+ </div>
61
+ ),
62
+ ],
63
+ }
64
+
65
+ export default meta
66
+ type Story = StoryObj<typeof meta>
67
+
68
+ // =============================================================================
69
+ // The two Figma media variants
70
+ // =============================================================================
71
+
72
+ export const WithImage: Story = {
73
+ args: {
74
+ title: 'Card Title',
75
+ description: 'Card Description',
76
+ imageSrc: AVATAR_SRC,
77
+ imageAlt: 'Acme Corp',
78
+ status: { label: 'Status' },
79
+ menuGroups: SAMPLE_MENU,
80
+ },
81
+ }
82
+
83
+ export const WithIcon: Story = {
84
+ args: {
85
+ title: 'Card Title',
86
+ description: 'Card Description',
87
+ icon: <MonitorIcon size={24} />,
88
+ status: { label: 'Status' },
89
+ menuGroups: SAMPLE_MENU,
90
+ },
91
+ }
92
+
93
+ // =============================================================================
94
+ // Optional-element permutations
95
+ // =============================================================================
96
+
97
+ export const WithoutStatus: Story = {
98
+ args: {
99
+ title: 'Card Title',
100
+ description: 'Card Description',
101
+ icon: <MonitorIcon size={24} />,
102
+ menuGroups: SAMPLE_MENU,
103
+ },
104
+ }
105
+
106
+ export const WithoutDescription: Story = {
107
+ args: {
108
+ title: 'Card Title',
109
+ imageSrc: AVATAR_SRC,
110
+ imageAlt: 'Acme Corp',
111
+ status: { label: 'Status' },
112
+ menuGroups: SAMPLE_MENU,
113
+ },
114
+ }
115
+
116
+ export const WithoutMenu: Story = {
117
+ args: {
118
+ title: 'Card Title',
119
+ description: 'Card Description',
120
+ icon: <MonitorIcon size={24} />,
121
+ status: { label: 'Status' },
122
+ },
123
+ }
124
+
125
+ export const Clickable: Story = {
126
+ args: {
127
+ title: 'Card Title',
128
+ description: 'Click anywhere on the row',
129
+ imageSrc: AVATAR_SRC,
130
+ imageAlt: 'Acme Corp',
131
+ status: { label: 'Online' },
132
+ menuGroups: SAMPLE_MENU,
133
+ onClick: () => {
134
+ // eslint-disable-next-line no-console
135
+ console.log('[story] card clicked')
136
+ },
137
+ },
138
+ }
139
+
140
+ export const StatusVariants: Story = {
141
+ parameters: {
142
+ docs: {
143
+ description: {
144
+ story:
145
+ 'The `status.variant` maps to the shared `Tag` palette — `success` (default), `warning`, `error`, `grey`.',
146
+ },
147
+ },
148
+ },
149
+ render: () => (
150
+ <div className="flex flex-col gap-3">
151
+ <MingoInfoCard
152
+ title="Production"
153
+ description="All systems operational"
154
+ icon={<MonitorIcon size={24} />}
155
+ status={{ label: 'Online', variant: 'success' }}
156
+ menuGroups={SAMPLE_MENU}
157
+ />
158
+ <MingoInfoCard
159
+ title="Staging"
160
+ description="Elevated latency detected"
161
+ icon={<MonitorIcon size={24} />}
162
+ status={{ label: 'Degraded', variant: 'warning' }}
163
+ menuGroups={SAMPLE_MENU}
164
+ />
165
+ <MingoInfoCard
166
+ title="Legacy host"
167
+ description="Unreachable for 12 minutes"
168
+ icon={<MonitorIcon size={24} />}
169
+ status={{ label: 'Offline', variant: 'error' }}
170
+ menuGroups={SAMPLE_MENU}
171
+ />
172
+ <MingoInfoCard
173
+ title="Archived host"
174
+ description="Decommissioned last quarter"
175
+ icon={<MonitorIcon size={24} />}
176
+ status={{ label: 'Archived', variant: 'grey' }}
177
+ menuGroups={SAMPLE_MENU}
178
+ />
179
+ </div>
180
+ ),
181
+ }
182
+
183
+ export const LongContentTruncates: Story = {
184
+ args: {
185
+ title: 'Some Really Long Card Title That Should Truncate Cleanly Without Wrapping',
186
+ description:
187
+ 'An equally long description line that also truncates instead of pushing the status pill and overflow menu off the card',
188
+ imageSrc: AVATAR_SRC,
189
+ imageAlt: 'Acme Corp',
190
+ status: { label: 'Status' },
191
+ menuGroups: SAMPLE_MENU,
192
+ },
193
+ }
@@ -61,7 +61,7 @@ export const WithoutDescription: Story = {
61
61
 
62
62
  export const WithoutSlashCommand: Story = {
63
63
  args: {
64
- icon: <Rocket01Icon size={16} />,
64
+ icon: <Rocket02Icon size={16} />,
65
65
  title: 'Get started',
66
66
  description: 'Browse onboarding guides and quick wins',
67
67
  },
@@ -116,6 +116,7 @@ export const WithAutocomplete: Story = {
116
116
  </ModalHeader>
117
117
  <ModalContent className="px-6 py-4">
118
118
  <Autocomplete
119
+ multiple
119
120
  options={autocompleteOptions}
120
121
  value={selected}
121
122
  onChange={setSelected}
@@ -24,6 +24,17 @@ const meta = {
24
24
  },
25
25
  },
26
26
  },
27
+ args: {
28
+ notification: {
29
+ id: 'preview',
30
+ variant: 'default',
31
+ title: 'Preview',
32
+ description: 'Preview notification',
33
+ createdAt: 0,
34
+ read: true,
35
+ },
36
+ onComplete: () => {},
37
+ },
27
38
  } satisfies Meta<typeof NotificationTile>;
28
39
 
29
40
  export default meta;
@@ -264,7 +275,7 @@ function PlaygroundControls() {
264
275
 
265
276
  <div className="flex flex-wrap gap-2">
266
277
  <Button
267
- variant="success"
278
+ variant="accent"
268
279
  onClick={() =>
269
280
  fire('success', 'Device Deployment Complete', 'HP EliteBook deployed to Marketing Dept')
270
281
  }
@@ -280,13 +291,13 @@ function PlaygroundControls() {
280
291
  Fire error
281
292
  </Button>
282
293
  <Button
283
- variant="warning"
294
+ variant="outline"
284
295
  onClick={() => fire('warning', 'Tech Required', 'Approval is required to execute the command.')}
285
296
  >
286
297
  Fire warning
287
298
  </Button>
288
299
  <Button
289
- variant="info"
300
+ variant="transparent"
290
301
  onClick={() => fire('info', 'Update available', 'A new agent version is ready to install.')}
291
302
  >
292
303
  Fire info
@@ -16,7 +16,7 @@ PageActions component for displaying action buttons in page headers.
16
16
  **Three variants:**
17
17
  1. \`icon-buttons\` - Buttons with icons, collapses to dropdown menu on mobile
18
18
  2. \`primary-buttons\` - Primary + outline buttons, fixed to bottom on mobile
19
- 3. \`menu-primary\` - MoreActionsMenu ("...") + primary button on desktop, all actions expand to fixed bottom bar on mobile
19
+ 3. \`menu-primary\` - ActionsMenuDropdown ("...") + primary button on desktop, all actions expand to fixed bottom bar on mobile
20
20
 
21
21
  Use with \`PageContainer\` headerActions prop for consistent page layouts.
22
22
  `,
@@ -52,7 +52,7 @@ type Story = StoryObj<typeof meta>
52
52
 
53
53
  /**
54
54
  * Icon buttons variant - used for list pages like Scripts.
55
- * Shows buttons with icons on desktop, collapses to MoreActionsMenu on mobile.
55
+ * Shows buttons with icons on desktop, collapses to ActionsMenuDropdown on mobile.
56
56
  */
57
57
  export const IconButtons: Story = {
58
58
  args: {
@@ -151,7 +151,7 @@ export const SingleIconButton: Story = {
151
151
  parameters: {
152
152
  docs: {
153
153
  description: {
154
- story: 'When there is only one action in icon-buttons variant, it displays as an icon button on mobile instead of collapsing to a MoreActionsMenu.',
154
+ story: 'When there is only one action in icon-buttons variant, it displays as an icon button on mobile instead of collapsing to an ActionsMenuDropdown.',
155
155
  },
156
156
  },
157
157
  },
@@ -207,7 +207,7 @@ export const WithStates: Story = {
207
207
  }
208
208
 
209
209
  /**
210
- * Menu + primary variant - shows MoreActionsMenu ("...") + primary button on desktop.
210
+ * Menu + primary variant - shows ActionsMenuDropdown ("...") + primary button on desktop.
211
211
  * On mobile all actions expand into a fixed bottom bar.
212
212
  */
213
213
  export const MenuPrimary: Story = {
@@ -85,7 +85,7 @@ export const ListPage: Story = {
85
85
  title: 'Devices',
86
86
  subtitle: '24 devices found',
87
87
  actions: [
88
- { label: 'Add Device', onClick: () => {}, icon: <Plus className="w-5 h-5" />, variant: 'primary' },
88
+ { label: 'Add Device', onClick: () => {}, icon: <Plus className="w-5 h-5" />, variant: 'accent' },
89
89
  ],
90
90
  children: <PlaceholderContent />,
91
91
  },
@@ -101,7 +101,7 @@ export const ListPageMultipleActions: Story = {
101
101
  subtitle: 'Manage your scripts',
102
102
  actions: [
103
103
  { label: 'Edit', onClick: () => {}, icon: <Edit className="w-5 h-5" />, variant: 'outline' },
104
- { label: 'Create Script', onClick: () => {}, icon: <Plus className="w-5 h-5" />, variant: 'primary' },
104
+ { label: 'Create Script', onClick: () => {}, icon: <Plus className="w-5 h-5" />, variant: 'accent' },
105
105
  ],
106
106
  children: <PlaceholderContent />,
107
107
  },
@@ -133,7 +133,7 @@ export const DetailPageWithActions: Story = {
133
133
  backButton: { onClick: () => {} },
134
134
  actions: [
135
135
  { label: 'Delete', onClick: () => {}, icon: <Trash2 className="w-5 h-5" />, variant: 'outline' },
136
- { label: 'Run Script', onClick: () => {}, variant: 'primary' },
136
+ { label: 'Run Script', onClick: () => {}, variant: 'accent' },
137
137
  ],
138
138
  children: <PlaceholderContent />,
139
139
  },
@@ -151,7 +151,7 @@ export const FormPage: Story = {
151
151
  backButton: { label: 'Cancel', onClick: () => {} },
152
152
  actions: [
153
153
  { label: 'Save Draft', onClick: () => {}, variant: 'outline' },
154
- { label: 'Publish', onClick: () => {}, variant: 'primary' },
154
+ { label: 'Publish', onClick: () => {}, variant: 'accent' },
155
155
  ],
156
156
  children: (
157
157
  <div className="flex flex-col gap-6">
@@ -208,7 +208,7 @@ export const WithHeaderActions: Story = {
208
208
  <Button variant="outline" size="small-legacy">Export</Button>
209
209
  ),
210
210
  actions: [
211
- { label: 'Add Device', onClick: () => {}, icon: <Plus className="w-5 h-5" />, variant: 'primary' },
211
+ { label: 'Add Device', onClick: () => {}, icon: <Plus className="w-5 h-5" />, variant: 'accent' },
212
212
  ],
213
213
  children: <PlaceholderContent />,
214
214
  },
@@ -133,7 +133,7 @@ export const WithSubtitleOnly: Story = {
133
133
  {
134
134
  label: 'Add Payment Method',
135
135
  onClick: fn(),
136
- variant: 'primary',
136
+ variant: 'accent',
137
137
  icon: <PlusCircleIcon size={24} />,
138
138
  },
139
139
  ],