@flamingo-stack/openframe-frontend-core 0.0.535 → 0.0.536-1768.4650.1
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/chat-protocol/events.d.ts +15 -1
- package/dist/chat-protocol/events.d.ts.map +1 -1
- package/dist/chat-protocol/index.cjs +15 -0
- package/dist/chat-protocol/index.cjs.map +1 -1
- package/dist/chat-protocol/index.js +15 -0
- package/dist/chat-protocol/index.js.map +1 -1
- package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
- package/dist/{chunk-LIX5OUHH.js → chunk-2TQQIXQ3.js} +2 -2
- package/dist/{chunk-ZKKEKG73.cjs → chunk-3FOJEMRF.cjs} +3 -3
- package/dist/{chunk-ZKKEKG73.cjs.map → chunk-3FOJEMRF.cjs.map} +1 -1
- package/dist/{chunk-Y72MXG5N.js → chunk-47VVYAJW.js} +2 -2
- package/dist/{chunk-U2Q7GEX2.cjs → chunk-4NMIWFTP.cjs} +26 -26
- package/dist/{chunk-U2Q7GEX2.cjs.map → chunk-4NMIWFTP.cjs.map} +1 -1
- package/dist/{chunk-QUVIT4JP.js → chunk-5QENIFGO.js} +5 -5
- package/dist/{chunk-ZR5CJTNR.cjs → chunk-7ZE3SPVS.cjs} +4 -4
- package/dist/{chunk-ZR5CJTNR.cjs.map → chunk-7ZE3SPVS.cjs.map} +1 -1
- package/dist/{chunk-7MHYRBBQ.cjs → chunk-B2N2NDZX.cjs} +5 -5
- package/dist/{chunk-7MHYRBBQ.cjs.map → chunk-B2N2NDZX.cjs.map} +1 -1
- package/dist/{chunk-5OLF6WXF.cjs → chunk-CP4VGMGK.cjs} +37 -37
- package/dist/{chunk-5OLF6WXF.cjs.map → chunk-CP4VGMGK.cjs.map} +1 -1
- package/dist/{chunk-E3YB55RR.cjs → chunk-DSGASYE6.cjs} +193 -37
- package/dist/chunk-DSGASYE6.cjs.map +1 -0
- package/dist/{chunk-P65EZEBW.cjs → chunk-ECLKNH72.cjs} +9 -9
- package/dist/{chunk-P65EZEBW.cjs.map → chunk-ECLKNH72.cjs.map} +1 -1
- package/dist/{chunk-PSBL3S2B.cjs → chunk-GMFYYVNC.cjs} +7 -7
- package/dist/{chunk-PSBL3S2B.cjs.map → chunk-GMFYYVNC.cjs.map} +1 -1
- package/dist/{chunk-LIQJAC6Z.js → chunk-HSJ7LXDC.js} +2 -2
- package/dist/{chunk-KLFGP6PR.js → chunk-I5DOYYPD.js} +2 -2
- package/dist/{chunk-2P4J2YLI.cjs → chunk-KEKCA7QQ.cjs} +87 -87
- package/dist/{chunk-2P4J2YLI.cjs.map → chunk-KEKCA7QQ.cjs.map} +1 -1
- package/dist/{chunk-4DTUUM5V.js → chunk-KQ4NDWIM.js} +4224 -4068
- package/dist/chunk-KQ4NDWIM.js.map +1 -0
- package/dist/{chunk-PLGITXT7.js → chunk-MDBLST2A.js} +2 -2
- package/dist/{chunk-N3AEJTCK.js → chunk-N5XVBG2S.js} +4 -4
- package/dist/{chunk-K6566PEB.js → chunk-O4UN26ET.js} +2 -2
- package/dist/{chunk-JVO5CYMK.js → chunk-OBGFWHDE.js} +6 -6
- package/dist/{chunk-GV4FDDFV.cjs → chunk-OLSAFBZX.cjs} +11 -11
- package/dist/{chunk-GV4FDDFV.cjs.map → chunk-OLSAFBZX.cjs.map} +1 -1
- package/dist/{chunk-NB6BP6JB.js → chunk-PEK3X323.js} +2 -2
- package/dist/{chunk-VPDTDKO5.js → chunk-PUXDD7XV.js} +2 -2
- package/dist/{chunk-TS2EHMEX.cjs → chunk-S5URTCRA.cjs} +26 -26
- package/dist/{chunk-TS2EHMEX.cjs.map → chunk-S5URTCRA.cjs.map} +1 -1
- package/dist/{chunk-4HQFV4PK.cjs → chunk-TLJDUNO2.cjs} +14 -14
- package/dist/{chunk-4HQFV4PK.cjs.map → chunk-TLJDUNO2.cjs.map} +1 -1
- package/dist/{chunk-EHPQKXXW.js → chunk-VKAH66LI.js} +2 -2
- package/dist/{chunk-MZVFZMQ6.js → chunk-WJ75GDIO.js} +4 -4
- package/dist/{chunk-RO67OOKJ.cjs → chunk-Z5TGYKIK.cjs} +31 -31
- package/dist/{chunk-RO67OOKJ.cjs.map → chunk-Z5TGYKIK.cjs.map} +1 -1
- package/dist/{chunk-Y55XQFJK.cjs → chunk-ZIBIQKMA.cjs} +61 -61
- package/dist/{chunk-Y55XQFJK.cjs.map → chunk-ZIBIQKMA.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/ai-assistant-info.d.ts +31 -0
- package/dist/components/chat/ai-assistant-info.d.ts.map +1 -0
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +8 -2
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +2 -0
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +7 -1
- package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
- package/dist/components/chat/stream/message-mutations.d.ts.map +1 -1
- package/dist/components/chat/ticket-escalated-message.d.ts +2 -8
- package/dist/components/chat/ticket-escalated-message.d.ts.map +1 -1
- package/dist/components/chat/ticket-event-message.d.ts +24 -0
- package/dist/components/chat/ticket-event-message.d.ts.map +1 -0
- package/dist/components/chat/types/message.types.d.ts +51 -2
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/chat/utils/message-segment-accumulator.d.ts +13 -1
- package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +22 -22
- package/dist/components/help-center-pages/index.js +13 -13
- package/dist/components/index.cjs +138 -132
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +17 -11
- package/dist/components/index.js.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +34 -34
- package/dist/components/meeting-scheduler/index.js +3 -3
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +6 -6
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/index.cjs +8 -2
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +7 -1
- package/dist/index.cjs +8 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -1
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +44 -0
- package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +32 -0
- package/src/chat-protocol/events.ts +16 -0
- package/src/chat-protocol/nats-decoder.ts +27 -0
- package/src/components/chat/__tests__/ticket-event.test.ts +194 -0
- package/src/components/chat/ai-assistant-info.tsx +71 -0
- package/src/components/chat/chat-message-enhanced.tsx +3 -0
- package/src/components/chat/index.ts +2 -0
- package/src/components/chat/stream/chat-stream-reducer.ts +19 -0
- package/src/components/chat/stream/message-mutations.ts +5 -0
- package/src/components/chat/ticket-escalated-message.tsx +10 -32
- package/src/components/chat/ticket-event-message.tsx +79 -0
- package/src/components/chat/types/message.types.ts +60 -1
- package/src/components/chat/utils/message-segment-accumulator.ts +41 -0
- package/src/components/chat/utils/process-historical-messages.ts +31 -0
- package/src/stories/TicketEventMessage.stories.tsx +100 -0
- package/dist/chunk-4DTUUM5V.js.map +0 -1
- package/dist/chunk-E3YB55RR.cjs.map +0 -1
- /package/dist/{chunk-LIX5OUHH.js.map → chunk-2TQQIXQ3.js.map} +0 -0
- /package/dist/{chunk-Y72MXG5N.js.map → chunk-47VVYAJW.js.map} +0 -0
- /package/dist/{chunk-QUVIT4JP.js.map → chunk-5QENIFGO.js.map} +0 -0
- /package/dist/{chunk-LIQJAC6Z.js.map → chunk-HSJ7LXDC.js.map} +0 -0
- /package/dist/{chunk-KLFGP6PR.js.map → chunk-I5DOYYPD.js.map} +0 -0
- /package/dist/{chunk-PLGITXT7.js.map → chunk-MDBLST2A.js.map} +0 -0
- /package/dist/{chunk-N3AEJTCK.js.map → chunk-N5XVBG2S.js.map} +0 -0
- /package/dist/{chunk-K6566PEB.js.map → chunk-O4UN26ET.js.map} +0 -0
- /package/dist/{chunk-JVO5CYMK.js.map → chunk-OBGFWHDE.js.map} +0 -0
- /package/dist/{chunk-NB6BP6JB.js.map → chunk-PEK3X323.js.map} +0 -0
- /package/dist/{chunk-VPDTDKO5.js.map → chunk-PUXDD7XV.js.map} +0 -0
- /package/dist/{chunk-EHPQKXXW.js.map → chunk-VKAH66LI.js.map} +0 -0
- /package/dist/{chunk-MZVFZMQ6.js.map → chunk-WJ75GDIO.js.map} +0 -0
|
@@ -18,6 +18,7 @@ export const MESSAGE_TYPE = {
|
|
|
18
18
|
APPROVAL_RESULT: 'APPROVAL_RESULT',
|
|
19
19
|
ESCALATION_OFFER: 'ESCALATION_OFFER',
|
|
20
20
|
TICKET_ESCALATED: 'TICKET_ESCALATED',
|
|
21
|
+
TICKET_EVENT: 'TICKET_EVENT',
|
|
21
22
|
ERROR: 'ERROR',
|
|
22
23
|
MESSAGE_START: 'MESSAGE_START',
|
|
23
24
|
MESSAGE_END: 'MESSAGE_END',
|
|
@@ -158,6 +159,34 @@ export interface TicketEscalatedData {
|
|
|
158
159
|
text?: string
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
// ========== Ticket Event Types ==========
|
|
163
|
+
|
|
164
|
+
/** Known `TicketEventData.kind` values. The vocabulary is OPEN on the wire:
|
|
165
|
+
* an unknown kind still renders (as a neutral info line) rather than being
|
|
166
|
+
* dropped, so the backend can add lifecycle kinds without a client release. */
|
|
167
|
+
export const TICKET_EVENT_KIND = {
|
|
168
|
+
RESOLVED: 'RESOLVED',
|
|
169
|
+
REOPENED: 'REOPENED',
|
|
170
|
+
} as const
|
|
171
|
+
|
|
172
|
+
export type TicketEventKind =
|
|
173
|
+
| typeof TICKET_EVENT_KIND[keyof typeof TICKET_EVENT_KIND]
|
|
174
|
+
| (string & {})
|
|
175
|
+
|
|
176
|
+
/** Ticket lifecycle receipt (`TICKET_EVENT`) — the ticket was resolved,
|
|
177
|
+
* reopened, etc. Same field names live (NATS chunk) and persisted
|
|
178
|
+
* (GraphQL `TicketEventData` inside `messageData`), so ONE mapper covers
|
|
179
|
+
* both paths. All actor fields are optional: the event stays renderable
|
|
180
|
+
* when the backend can't attribute it. */
|
|
181
|
+
export interface TicketEventData {
|
|
182
|
+
kind: TicketEventKind
|
|
183
|
+
actorId?: string
|
|
184
|
+
actorName?: string
|
|
185
|
+
/** Who acted — e.g. an AI agent vs a human. Open string like `kind`. */
|
|
186
|
+
actorType?: string
|
|
187
|
+
reason?: string
|
|
188
|
+
}
|
|
189
|
+
|
|
161
190
|
/**
|
|
162
191
|
* Single tool call inside a batch approval request.
|
|
163
192
|
* Mirrors backend PendingToolCallDto.
|
|
@@ -310,6 +339,23 @@ export type TicketEscalatedSegment = {
|
|
|
310
339
|
data: TicketEscalatedData
|
|
311
340
|
}
|
|
312
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Ticket lifecycle receipt (resolved / reopened / an unknown future kind).
|
|
344
|
+
* Arrives as a standalone `TICKET_EVENT` chunk outside MESSAGE_START/END —
|
|
345
|
+
* same delivery shape as `ticket_escalated`.
|
|
346
|
+
*
|
|
347
|
+
* `streamSeq` is the chunk's JetStream sequence, kept ON the segment because
|
|
348
|
+
* it is the event's only stable identity: the accumulator upserts on it so a
|
|
349
|
+
* redelivered chunk (catch-up over hydrated history) replaces rather than
|
|
350
|
+
* stacks a second card, while two genuinely distinct events of the same kind
|
|
351
|
+
* (resolve → reopen → resolve) keep distinct sequences and both render.
|
|
352
|
+
*/
|
|
353
|
+
export type TicketEventSegment = {
|
|
354
|
+
type: 'ticket_event'
|
|
355
|
+
data: TicketEventData
|
|
356
|
+
streamSeq?: number
|
|
357
|
+
}
|
|
358
|
+
|
|
313
359
|
export type ErrorSegment = {
|
|
314
360
|
type: 'error'
|
|
315
361
|
title: string
|
|
@@ -322,7 +368,7 @@ export type ContextCompactionSegment = {
|
|
|
322
368
|
summary?: string
|
|
323
369
|
}
|
|
324
370
|
|
|
325
|
-
export type MessageSegment = TextSegment | ThinkingSegment | GuideSegment | AskSegment | ToolExecutionSegment | ApprovalRequestSegment | ApprovalBatchSegment | EscalationOfferSegment | TicketEscalatedSegment | ErrorSegment | ContextCompactionSegment
|
|
371
|
+
export type MessageSegment = TextSegment | ThinkingSegment | GuideSegment | AskSegment | ToolExecutionSegment | ApprovalRequestSegment | ApprovalBatchSegment | EscalationOfferSegment | TicketEscalatedSegment | TicketEventSegment | ErrorSegment | ContextCompactionSegment
|
|
326
372
|
|
|
327
373
|
export type MessageContent = string | MessageSegment[]
|
|
328
374
|
|
|
@@ -427,6 +473,18 @@ export interface TicketEscalatedMessageData extends MessageDataBase {
|
|
|
427
473
|
text?: string
|
|
428
474
|
}
|
|
429
475
|
|
|
476
|
+
/** Persisted ticket lifecycle row — same field names as the live chunk
|
|
477
|
+
* (see `TicketEventData`), decoded by the SAME mapper on replay. Fields are
|
|
478
|
+
* wire-shaped (`| null`): every one is a nullable GraphQL scalar. */
|
|
479
|
+
export interface TicketEventMessageData extends MessageDataBase {
|
|
480
|
+
type: 'TICKET_EVENT'
|
|
481
|
+
kind?: string | null
|
|
482
|
+
actorId?: string | null
|
|
483
|
+
actorName?: string | null
|
|
484
|
+
actorType?: string | null
|
|
485
|
+
reason?: string | null
|
|
486
|
+
}
|
|
487
|
+
|
|
430
488
|
export interface ErrorMessageData extends MessageDataBase {
|
|
431
489
|
type: 'ERROR'
|
|
432
490
|
error?: string
|
|
@@ -473,6 +531,7 @@ export type MessageData =
|
|
|
473
531
|
| ApprovalResultMessageData
|
|
474
532
|
| EscalationOfferMessageData
|
|
475
533
|
| TicketEscalatedMessageData
|
|
534
|
+
| TicketEventMessageData
|
|
476
535
|
| ErrorMessageData
|
|
477
536
|
| AIMetadataMessageData
|
|
478
537
|
| SystemMessageData
|
|
@@ -20,6 +20,8 @@ import type {
|
|
|
20
20
|
EscalationOfferSegment,
|
|
21
21
|
TicketEscalatedData,
|
|
22
22
|
TicketEscalatedSegment,
|
|
23
|
+
TicketEventData,
|
|
24
|
+
TicketEventSegment,
|
|
23
25
|
ContextCompactionSegment,
|
|
24
26
|
ErrorSegment,
|
|
25
27
|
PendingApproval,
|
|
@@ -535,6 +537,42 @@ export class MessageSegmentAccumulator {
|
|
|
535
537
|
return this.getSegments()
|
|
536
538
|
}
|
|
537
539
|
|
|
540
|
+
/**
|
|
541
|
+
* Add a ticket lifecycle receipt (resolved / reopened / unknown kind).
|
|
542
|
+
*
|
|
543
|
+
* Upsert identity is the chunk's stream sequence when BOTH sides know it;
|
|
544
|
+
* otherwise full payload equality. History-hydrated segments come back
|
|
545
|
+
* seq-less (the persisted row's seq lives on the message, not the
|
|
546
|
+
* `messageData`), so a JetStream catch-up redelivery of the same event must
|
|
547
|
+
* still match its hydrated twin — payload equality is what catches that.
|
|
548
|
+
* Two genuinely distinct events (resolve → reopen → resolve) differ in seq
|
|
549
|
+
* or payload and both render.
|
|
550
|
+
*/
|
|
551
|
+
addTicketEvent(data: TicketEventData, streamSeq?: number): MessageSegment[] {
|
|
552
|
+
const segment: TicketEventSegment = {
|
|
553
|
+
type: 'ticket_event',
|
|
554
|
+
data,
|
|
555
|
+
...(streamSeq !== undefined ? { streamSeq } : {}),
|
|
556
|
+
}
|
|
557
|
+
const existingIndex = this.segments.findIndex((s) => {
|
|
558
|
+
if (s.type !== 'ticket_event') return false
|
|
559
|
+
if (s.streamSeq !== undefined && streamSeq !== undefined) return s.streamSeq === streamSeq
|
|
560
|
+
return (
|
|
561
|
+
s.data.kind === data.kind &&
|
|
562
|
+
s.data.actorId === data.actorId &&
|
|
563
|
+
s.data.actorName === data.actorName &&
|
|
564
|
+
s.data.actorType === data.actorType &&
|
|
565
|
+
s.data.reason === data.reason
|
|
566
|
+
)
|
|
567
|
+
})
|
|
568
|
+
if (existingIndex !== -1) {
|
|
569
|
+
this.segments[existingIndex] = segment
|
|
570
|
+
return this.getSegments()
|
|
571
|
+
}
|
|
572
|
+
this.segments.push(segment)
|
|
573
|
+
return this.getSegments()
|
|
574
|
+
}
|
|
575
|
+
|
|
538
576
|
/**
|
|
539
577
|
* Update status of an existing approval segment (single, batch, or
|
|
540
578
|
* escalation offer).
|
|
@@ -680,6 +718,9 @@ export class MessageSegmentAccumulator {
|
|
|
680
718
|
case 'ticket_escalated':
|
|
681
719
|
this.addTicketEscalated(segment.data)
|
|
682
720
|
break
|
|
721
|
+
case 'ticket_event':
|
|
722
|
+
this.addTicketEvent(segment.data, segment.streamSeq)
|
|
723
|
+
break
|
|
683
724
|
case 'error':
|
|
684
725
|
this.addError(segment.title, segment.details)
|
|
685
726
|
break
|
|
@@ -243,6 +243,23 @@ export function decodeHistoricalMessageData(data: MessageData): ChatStreamEvent
|
|
|
243
243
|
}
|
|
244
244
|
return null
|
|
245
245
|
|
|
246
|
+
// Same completeness gate as the live decoder: `kind` (an OPEN string —
|
|
247
|
+
// unknown kinds render as a neutral line) is the only required field.
|
|
248
|
+
// Field names match the chunk, so ONE mapping covers both paths; the
|
|
249
|
+
// typeof gates also fold the row's GraphQL nulls to undefined.
|
|
250
|
+
case MESSAGE_TYPE.TICKET_EVENT: {
|
|
251
|
+
const kind = 'kind' in data && typeof data.kind === 'string' ? data.kind.trim() : ''
|
|
252
|
+
if (!kind) return null
|
|
253
|
+
return {
|
|
254
|
+
type: 'ticket-event',
|
|
255
|
+
kind,
|
|
256
|
+
actorId: typeof data.actorId === 'string' ? data.actorId : undefined,
|
|
257
|
+
actorName: typeof data.actorName === 'string' ? data.actorName : undefined,
|
|
258
|
+
actorType: typeof data.actorType === 'string' ? data.actorType : undefined,
|
|
259
|
+
reason: typeof data.reason === 'string' && data.reason.trim() ? data.reason : undefined,
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
246
263
|
case MESSAGE_TYPE.ERROR:
|
|
247
264
|
if ('error' in data) {
|
|
248
265
|
return {
|
|
@@ -336,6 +353,20 @@ function applyHistoryEvent(
|
|
|
336
353
|
})
|
|
337
354
|
break
|
|
338
355
|
|
|
356
|
+
// Seq-less on purpose: the persisted row's sequence lives on the message
|
|
357
|
+
// (`lastChunkStreamSeq`), not in `messageData`. A live redelivery of the
|
|
358
|
+
// same event matches this segment by payload equality (see
|
|
359
|
+
// `addTicketEvent`).
|
|
360
|
+
case 'ticket-event':
|
|
361
|
+
accumulator.addTicketEvent({
|
|
362
|
+
kind: event.kind,
|
|
363
|
+
actorId: event.actorId,
|
|
364
|
+
actorName: event.actorName,
|
|
365
|
+
actorType: event.actorType,
|
|
366
|
+
reason: event.reason,
|
|
367
|
+
})
|
|
368
|
+
break
|
|
369
|
+
|
|
339
370
|
case 'escalation-offer-resolved':
|
|
340
371
|
// Recorded, not applied here: `applyOfferResolutions` runs after every
|
|
341
372
|
// flush and covers the same-bubble case as well as the cross-bubble one.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
import { TicketEventMessage } from "../components/chat/ticket-event-message";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `ticket_event` message segment — the ticket lifecycle receipt (Figma:
|
|
8
|
+
* `ai-assistant-info`, types resolved-fae / resolved-tech / reopened-fae /
|
|
9
|
+
* reopened-tech / reopened-reason).
|
|
10
|
+
*
|
|
11
|
+
* Decoded from the standalone `TICKET_EVENT` chunk (live) or the persisted
|
|
12
|
+
* `TicketEventData` row (history). `kind` is an OPEN vocabulary: unknown
|
|
13
|
+
* kinds render as a neutral info line rather than being dropped, so the
|
|
14
|
+
* backend can add lifecycle kinds without a client release.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Fixed so the timestamp doesn't churn between snapshots. */
|
|
18
|
+
const TIMESTAMP = new Date("2026-08-07T14:47:00");
|
|
19
|
+
|
|
20
|
+
const plainDecorator = (Story: React.ComponentType) => (
|
|
21
|
+
<div style={{ maxWidth: 600, background: "var(--color-bg)" }} className="p-4">
|
|
22
|
+
<Story />
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const meta = {
|
|
27
|
+
title: "Chat/Client/TicketEventMessage",
|
|
28
|
+
component: TicketEventMessage,
|
|
29
|
+
tags: ["autodocs"],
|
|
30
|
+
parameters: {
|
|
31
|
+
docs: {
|
|
32
|
+
description: {
|
|
33
|
+
component:
|
|
34
|
+
"Renders a `TICKET_EVENT` block: a green check for RESOLVED, a pink info tile for REOPENED and for any kind this build doesn't know, with copy composed from the event's actor and reason fields.",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
decorators: [plainDecorator],
|
|
39
|
+
} satisfies Meta<typeof TicketEventMessage>;
|
|
40
|
+
|
|
41
|
+
export default meta;
|
|
42
|
+
type Story = StoryObj<typeof meta>;
|
|
43
|
+
|
|
44
|
+
/** Fae (the AI agent) resolved the ticket — Figma type=resolved-fae. */
|
|
45
|
+
export const ResolvedByFae: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
data: { kind: "RESOLVED", actorId: "fae", actorName: "Fae", actorType: "AI" },
|
|
48
|
+
timestamp: TIMESTAMP,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** A human technician resolved the ticket — Figma type=resolved-tech. */
|
|
53
|
+
export const ResolvedByTechnician: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
data: { kind: "RESOLVED", actorId: "u-42", actorName: "Roman Smith", actorType: "TECHNICIAN" },
|
|
56
|
+
timestamp: TIMESTAMP,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** Reopened with the user's stated reason — Figma type=reopened-reason. */
|
|
61
|
+
export const ReopenedWithReason: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
data: {
|
|
64
|
+
kind: "REOPENED",
|
|
65
|
+
actorId: "u-7",
|
|
66
|
+
actorName: "John Smith",
|
|
67
|
+
actorType: "CLIENT",
|
|
68
|
+
reason: "The printer stopped working again",
|
|
69
|
+
},
|
|
70
|
+
timestamp: TIMESTAMP,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** Reopened, no reason, AI keeps assisting — Figma type=reopened-fae. */
|
|
75
|
+
export const ReopenedFaeContinues: Story = {
|
|
76
|
+
args: {
|
|
77
|
+
data: { kind: "REOPENED", actorId: "fae", actorName: "Fae", actorType: "AI" },
|
|
78
|
+
timestamp: TIMESTAMP,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Reopened, no reason, routed to a human — Figma type=reopened-tech. */
|
|
83
|
+
export const ReopenedTechnicianReplies: Story = {
|
|
84
|
+
args: {
|
|
85
|
+
data: { kind: "REOPENED", actorId: "u-42", actorName: "Roman Smith", actorType: "TECHNICIAN" },
|
|
86
|
+
timestamp: TIMESTAMP,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A kind the client has never seen. The open vocabulary means the backend can
|
|
92
|
+
* add lifecycle kinds without a client release — this story is the regression
|
|
93
|
+
* guard: neutral info line, humanized title, never dropped.
|
|
94
|
+
*/
|
|
95
|
+
export const UnknownKind: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
data: { kind: "ON_HOLD", actorName: "Roman Smith", reason: "Waiting for replacement hardware" },
|
|
98
|
+
timestamp: TIMESTAMP,
|
|
99
|
+
},
|
|
100
|
+
};
|