@flamingo-stack/openframe-frontend-core 0.0.539 → 0.0.540-snapshot.20260814144743

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 (106) hide show
  1. package/dist/{chunk-EWO5DH33.js → chunk-2MO4NEIR.js} +2 -2
  2. package/dist/{chunk-TROZIN2D.js → chunk-3KEAUJUU.js} +2 -2
  3. package/dist/{chunk-CJMPFRVH.cjs → chunk-4JL2WTIC.cjs} +9 -9
  4. package/dist/{chunk-CJMPFRVH.cjs.map → chunk-4JL2WTIC.cjs.map} +1 -1
  5. package/dist/{chunk-S7L62JWW.cjs → chunk-52FXIY2M.cjs} +5 -5
  6. package/dist/{chunk-S7L62JWW.cjs.map → chunk-52FXIY2M.cjs.map} +1 -1
  7. package/dist/{chunk-F3XRVBGI.cjs → chunk-57K53PKB.cjs} +26 -26
  8. package/dist/{chunk-F3XRVBGI.cjs.map → chunk-57K53PKB.cjs.map} +1 -1
  9. package/dist/{chunk-OWFU3G4S.cjs → chunk-6B6RCBVM.cjs} +87 -87
  10. package/dist/{chunk-OWFU3G4S.cjs.map → chunk-6B6RCBVM.cjs.map} +1 -1
  11. package/dist/{chunk-EESFDY5E.js → chunk-A5USNYZA.js} +2 -2
  12. package/dist/{chunk-NDUFYBZG.js → chunk-BYKPDIVC.js} +783 -722
  13. package/dist/chunk-BYKPDIVC.js.map +1 -0
  14. package/dist/{chunk-7HHJACP5.cjs → chunk-COZAC2RY.cjs} +4 -4
  15. package/dist/{chunk-7HHJACP5.cjs.map → chunk-COZAC2RY.cjs.map} +1 -1
  16. package/dist/{chunk-YKWGJS77.cjs → chunk-EVJ7DVCI.cjs} +61 -61
  17. package/dist/{chunk-YKWGJS77.cjs.map → chunk-EVJ7DVCI.cjs.map} +1 -1
  18. package/dist/{chunk-FXPELLEG.cjs → chunk-FESVTVRZ.cjs} +11 -11
  19. package/dist/{chunk-FXPELLEG.cjs.map → chunk-FESVTVRZ.cjs.map} +1 -1
  20. package/dist/{chunk-THFLCYDF.cjs → chunk-FUF6YX7H.cjs} +7 -7
  21. package/dist/{chunk-THFLCYDF.cjs.map → chunk-FUF6YX7H.cjs.map} +1 -1
  22. package/dist/{chunk-GIYY6ALS.js → chunk-HUXNMQD7.js} +6 -6
  23. package/dist/{chunk-5AVACJ3L.js → chunk-ITUVIG7K.js} +2 -2
  24. package/dist/{chunk-DR2PFOYX.js → chunk-J6F4342G.js} +2 -2
  25. package/dist/{chunk-HNNNY6NI.js → chunk-JOQBVXM3.js} +2 -2
  26. package/dist/{chunk-LPL53U5K.cjs → chunk-JWSWACXB.cjs} +14 -14
  27. package/dist/{chunk-LPL53U5K.cjs.map → chunk-JWSWACXB.cjs.map} +1 -1
  28. package/dist/{chunk-Q3WPMUZA.cjs → chunk-KJZVKS7X.cjs} +3 -3
  29. package/dist/{chunk-Q3WPMUZA.cjs.map → chunk-KJZVKS7X.cjs.map} +1 -1
  30. package/dist/{chunk-LAIABVLE.js → chunk-KRXQVZRX.js} +5 -5
  31. package/dist/{chunk-RCGRPTFN.cjs → chunk-NJ42DNRT.cjs} +31 -31
  32. package/dist/{chunk-RCGRPTFN.cjs.map → chunk-NJ42DNRT.cjs.map} +1 -1
  33. package/dist/{chunk-XGCXH7SP.cjs → chunk-NJ7QTEJM.cjs} +26 -26
  34. package/dist/{chunk-XGCXH7SP.cjs.map → chunk-NJ7QTEJM.cjs.map} +1 -1
  35. package/dist/{chunk-CABB5F4R.js → chunk-QZEEP2FO.js} +4 -4
  36. package/dist/{chunk-T33MZQQA.js → chunk-RNVMGZSM.js} +2 -2
  37. package/dist/{chunk-5BXAGDV5.js → chunk-RZWZCDBG.js} +2 -2
  38. package/dist/{chunk-Y5NBRXSK.js → chunk-T2NU3GZ7.js} +4 -4
  39. package/dist/{chunk-I5MSZCOS.cjs → chunk-T7ZTT7TO.cjs} +69 -8
  40. package/dist/chunk-T7ZTT7TO.cjs.map +1 -0
  41. package/dist/{chunk-ME3DCQPR.cjs → chunk-VKBGJGYF.cjs} +37 -37
  42. package/dist/{chunk-ME3DCQPR.cjs.map → chunk-VKBGJGYF.cjs.map} +1 -1
  43. package/dist/{chunk-EDD6ZOVC.js → chunk-XFYSYVXN.js} +2 -2
  44. package/dist/components/case-studies/index.cjs +8 -8
  45. package/dist/components/case-studies/index.js +2 -2
  46. package/dist/components/chat/index.cjs +2 -2
  47. package/dist/components/chat/index.js +1 -1
  48. package/dist/components/contact/index.cjs +3 -3
  49. package/dist/components/contact/index.js +2 -2
  50. package/dist/components/docs/index.cjs +5 -5
  51. package/dist/components/docs/index.js +4 -4
  52. package/dist/components/embeds/index.cjs +3 -3
  53. package/dist/components/embeds/index.js +2 -2
  54. package/dist/components/faq/index.cjs +3 -3
  55. package/dist/components/faq/index.js +2 -2
  56. package/dist/components/features/index.cjs +2 -2
  57. package/dist/components/features/index.js +1 -1
  58. package/dist/components/help-center-pages/index.cjs +22 -22
  59. package/dist/components/help-center-pages/index.js +13 -13
  60. package/dist/components/index.cjs +134 -132
  61. package/dist/components/index.cjs.map +1 -1
  62. package/dist/components/index.js +13 -11
  63. package/dist/components/index.js.map +1 -1
  64. package/dist/components/meeting-scheduler/index.cjs +34 -34
  65. package/dist/components/meeting-scheduler/index.js +3 -3
  66. package/dist/components/navigation/index.cjs +2 -2
  67. package/dist/components/navigation/index.js +1 -1
  68. package/dist/components/onboarding-guides/index.cjs +5 -5
  69. package/dist/components/onboarding-guides/index.js +4 -4
  70. package/dist/components/related-content/index.cjs +3 -3
  71. package/dist/components/related-content/index.js +2 -2
  72. package/dist/components/tickets/index.cjs +6 -6
  73. package/dist/components/tickets/index.js +5 -5
  74. package/dist/components/ui/index.cjs +4 -2
  75. package/dist/components/ui/index.cjs.map +1 -1
  76. package/dist/components/ui/index.d.ts +1 -0
  77. package/dist/components/ui/index.d.ts.map +1 -1
  78. package/dist/components/ui/index.js +3 -1
  79. package/dist/components/ui/reopen-ticket-modal.d.ts +6 -5
  80. package/dist/components/ui/reopen-ticket-modal.d.ts.map +1 -1
  81. package/dist/components/ui/reopen-ticket-request-modal.d.ts +33 -0
  82. package/dist/components/ui/reopen-ticket-request-modal.d.ts.map +1 -0
  83. package/dist/index.cjs +4 -2
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.js +3 -1
  86. package/package.json +1 -1
  87. package/src/components/ui/index.ts +1 -0
  88. package/src/components/ui/reopen-ticket-modal.tsx +9 -8
  89. package/src/components/ui/reopen-ticket-request-modal.tsx +112 -0
  90. package/src/stories/ReopenTicketModal.stories.tsx +8 -8
  91. package/src/stories/ReopenTicketRequestModal.stories.tsx +90 -0
  92. package/dist/chunk-I5MSZCOS.cjs.map +0 -1
  93. package/dist/chunk-NDUFYBZG.js.map +0 -1
  94. /package/dist/{chunk-EWO5DH33.js.map → chunk-2MO4NEIR.js.map} +0 -0
  95. /package/dist/{chunk-TROZIN2D.js.map → chunk-3KEAUJUU.js.map} +0 -0
  96. /package/dist/{chunk-EESFDY5E.js.map → chunk-A5USNYZA.js.map} +0 -0
  97. /package/dist/{chunk-GIYY6ALS.js.map → chunk-HUXNMQD7.js.map} +0 -0
  98. /package/dist/{chunk-5AVACJ3L.js.map → chunk-ITUVIG7K.js.map} +0 -0
  99. /package/dist/{chunk-DR2PFOYX.js.map → chunk-J6F4342G.js.map} +0 -0
  100. /package/dist/{chunk-HNNNY6NI.js.map → chunk-JOQBVXM3.js.map} +0 -0
  101. /package/dist/{chunk-LAIABVLE.js.map → chunk-KRXQVZRX.js.map} +0 -0
  102. /package/dist/{chunk-CABB5F4R.js.map → chunk-QZEEP2FO.js.map} +0 -0
  103. /package/dist/{chunk-T33MZQQA.js.map → chunk-RNVMGZSM.js.map} +0 -0
  104. /package/dist/{chunk-5BXAGDV5.js.map → chunk-RZWZCDBG.js.map} +0 -0
  105. /package/dist/{chunk-Y5NBRXSK.js.map → chunk-T2NU3GZ7.js.map} +0 -0
  106. /package/dist/{chunk-EDD6ZOVC.js.map → chunk-XFYSYVXN.js.map} +0 -0
@@ -0,0 +1,112 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { Button } from './button';
5
+ import { CheckboxBlock } from './checkbox-block';
6
+ import { ModalV2, ModalV2Footer, ModalV2Header, ModalV2Title } from './modal-v2';
7
+ import { Textarea } from './textarea';
8
+
9
+ /** Backend trims and caps the reason at 1000 chars; cap it client-side too. */
10
+ const REASON_MAX_LENGTH = 1000;
11
+
12
+ export interface ReopenTicketRequestSelection {
13
+ /** Trimmed; `null` when the field was left blank. */
14
+ reason: string | null;
15
+ /** Present ONLY when the handoff option was shown — the tech-closed
16
+ * variant omits the field entirely (mirrors `TicketReopenInput`). */
17
+ handoffToTechnician?: boolean;
18
+ }
19
+
20
+ export interface ReopenTicketRequestModalProps {
21
+ isOpen: boolean;
22
+ onClose: () => void;
23
+ /**
24
+ * Fae resolved the ticket (`Ticket.resolvedBy === END_USER`): reopening
25
+ * resumes the AI conversation, and the checkbox lets the user route to a
26
+ * technician instead. When a technician closed the ticket the option is
27
+ * hidden — reopening goes to a technician regardless, and the selection
28
+ * omits `handoffToTechnician`.
29
+ */
30
+ showHandoffOption?: boolean;
31
+ /** Confirm in-flight: buttons lock and the modal refuses to close. */
32
+ isPending?: boolean;
33
+ onConfirm: (selection: ReopenTicketRequestSelection) => void;
34
+ }
35
+
36
+ /**
37
+ * Client-side Reopen Ticket dialog (Figma openframe---fae-chat 346-10518):
38
+ * the end user reopening their own resolved ticket from the closed chat.
39
+ * Unlike the admin `ReopenTicketModal` there is no status/assignee pair —
40
+ * the backend picks the target via `requestTicketReopen`; the user only
41
+ * supplies an optional reason and (for Fae-closed tickets) the handoff
42
+ * choice. Presentational — the consumer performs the reopen in `onConfirm`.
43
+ */
44
+ export function ReopenTicketRequestModal({
45
+ isOpen,
46
+ onClose,
47
+ showHandoffOption = false,
48
+ isPending = false,
49
+ onConfirm,
50
+ }: ReopenTicketRequestModalProps) {
51
+ const [reason, setReason] = React.useState('');
52
+ const [handoff, setHandoff] = React.useState(false);
53
+
54
+ // Re-seed the inputs every time the modal opens.
55
+ React.useEffect(() => {
56
+ if (!isOpen) return;
57
+ setReason('');
58
+ setHandoff(false);
59
+ }, [isOpen]);
60
+
61
+ const handleConfirm = () => {
62
+ if (isPending) return;
63
+ const trimmedReason = reason.trim();
64
+ onConfirm({
65
+ reason: trimmedReason ? trimmedReason.slice(0, REASON_MAX_LENGTH) : null,
66
+ ...(showHandoffOption ? { handoffToTechnician: handoff } : {}),
67
+ });
68
+ };
69
+
70
+ return (
71
+ <ModalV2 isOpen={isOpen} onClose={isPending ? () => {} : onClose} className="text-left md:max-w-[600px]">
72
+ <ModalV2Header>
73
+ <ModalV2Title>Reopen Ticket</ModalV2Title>
74
+ </ModalV2Header>
75
+
76
+ <p className="text-h4 text-ods-text-primary">
77
+ This ticket will be reopened and the AI assistant will continue helping you.
78
+ </p>
79
+
80
+ <div className="w-full">
81
+ <Textarea
82
+ label="Reason"
83
+ value={reason}
84
+ onChange={event => setReason(event.target.value)}
85
+ placeholder="Describe what's still not working (optional)"
86
+ maxLength={REASON_MAX_LENGTH}
87
+ disabled={isPending}
88
+ />
89
+ </div>
90
+
91
+ {showHandoffOption && (
92
+ <CheckboxBlock
93
+ id="reopen-handoff-to-technician"
94
+ checked={handoff}
95
+ onCheckedChange={setHandoff}
96
+ label="Hand off to a Technician"
97
+ description="A technician will review the ticket and reply when available."
98
+ disabled={isPending}
99
+ />
100
+ )}
101
+
102
+ <ModalV2Footer>
103
+ <Button type="button" variant="outline" onClick={onClose} disabled={isPending} className="flex-1">
104
+ Cancel
105
+ </Button>
106
+ <Button type="button" variant="accent" onClick={handleConfirm} loading={isPending} className="flex-1">
107
+ Reopen Ticket
108
+ </Button>
109
+ </ModalV2Footer>
110
+ </ModalV2>
111
+ );
112
+ }
@@ -17,10 +17,10 @@ const meta = {
17
17
  description: {
18
18
  component:
19
19
  'Reopen Ticket dialog (Figma openframe---tickets 8456-17581): shown when a technician reopens a ' +
20
- 'Resolved/Archived ticket. Same Status + Assigned pair as TakeOverTicketModal plus an optional free-text ' +
21
- 'reason. Unlike Take Over, the assignee is optional - a reopened ticket may stay unassigned. ' +
22
- 'Presentational - the consumer supplies option lists (design default pre-selects Tech Required), ' +
23
- 'default selections, and performs the actual reopen in onConfirm.',
20
+ 'Resolved/Archived ticket. Same Status + Assigned pair as TakeOverTicketModal - both required before ' +
21
+ 'the CTA unlocks - plus an optional free-text reason. Presentational - the consumer supplies option ' +
22
+ 'lists (design default pre-selects Tech Required), default selections, and performs the actual reopen ' +
23
+ 'in onConfirm.',
24
24
  },
25
25
  },
26
26
  },
@@ -98,11 +98,11 @@ export const Default: Story = {
98
98
  };
99
99
 
100
100
  /**
101
- * No assignee - the reopen still confirms and the ticket stays unassigned
102
- * (flagged for triage). This is the deliberate difference from Take Over,
103
- * where the CTA locks until a technician is picked.
101
+ * No assignee pre-selected (e.g. the ticket was unassigned): the CTA stays
102
+ * locked until a technician is chosen - both fields are required, matching
103
+ * Take Over.
104
104
  */
105
- export const NoAssignee: Story = {
105
+ export const NoDefaultAssignee: Story = {
106
106
  args: { ...Default.args, initialAssigneeId: null },
107
107
  render: args => <ControlledModal {...args} />,
108
108
  };
@@ -0,0 +1,90 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { useState } from 'react';
3
+ import { fn } from 'storybook/test';
4
+ import { Button } from '../components/ui/button';
5
+ import {
6
+ ReopenTicketRequestModal,
7
+ type ReopenTicketRequestModalProps,
8
+ type ReopenTicketRequestSelection,
9
+ } from '../components/ui/reopen-ticket-request-modal';
10
+
11
+ const meta = {
12
+ title: 'UI/ReopenTicketRequestModal',
13
+ component: ReopenTicketRequestModal,
14
+ parameters: {
15
+ layout: 'centered',
16
+ docs: {
17
+ description: {
18
+ component:
19
+ 'Client-side Reopen Ticket dialog (Figma openframe---fae-chat 346-10518): the end user reopening their ' +
20
+ 'own resolved ticket from the closed chat. No status/assignee pair - the backend picks the target via ' +
21
+ 'requestTicketReopen; the user supplies an optional reason and, for Fae-closed tickets, the ' +
22
+ 'hand-off-to-a-technician choice. Tech-closed tickets hide the checkbox and omit the field entirely.',
23
+ },
24
+ },
25
+ },
26
+ argTypes: {
27
+ isPending: { control: 'boolean' },
28
+ showHandoffOption: { control: 'boolean' },
29
+ },
30
+ } satisfies Meta<typeof ReopenTicketRequestModal>;
31
+
32
+ export default meta;
33
+ type Story = StoryObj<typeof meta>;
34
+
35
+ function ControlledModal(props: Partial<ReopenTicketRequestModalProps> & { pendingOnConfirm?: boolean }) {
36
+ const [isOpen, setIsOpen] = useState(true);
37
+ const [isPending, setIsPending] = useState(false);
38
+
39
+ const handleConfirm = (selection: ReopenTicketRequestSelection) => {
40
+ props.onConfirm?.(selection);
41
+ if (!props.pendingOnConfirm) {
42
+ setIsOpen(false);
43
+ return;
44
+ }
45
+ // Simulate the requestTicketReopen round-trip.
46
+ setIsPending(true);
47
+ setTimeout(() => {
48
+ setIsPending(false);
49
+ setIsOpen(false);
50
+ }, 1500);
51
+ };
52
+
53
+ return (
54
+ <>
55
+ <Button variant="outline" onClick={() => setIsOpen(true)}>
56
+ Reopen Ticket
57
+ </Button>
58
+ <ReopenTicketRequestModal
59
+ isOpen={isOpen}
60
+ onClose={() => setIsOpen(false)}
61
+ {...props}
62
+ isPending={props.isPending ?? isPending}
63
+ onConfirm={handleConfirm}
64
+ />
65
+ </>
66
+ );
67
+ }
68
+
69
+ /** Fae-closed ticket (`resolvedBy === END_USER`) - the handoff checkbox is offered. */
70
+ export const FaeClosed: Story = {
71
+ args: {
72
+ isOpen: true,
73
+ onClose: fn(),
74
+ onConfirm: fn(),
75
+ showHandoffOption: true,
76
+ },
77
+ render: args => <ControlledModal {...args} />,
78
+ };
79
+
80
+ /** Technician-closed ticket - no checkbox; the selection omits `handoffToTechnician`. */
81
+ export const TechnicianClosed: Story = {
82
+ args: { ...FaeClosed.args, showHandoffOption: false },
83
+ render: args => <ControlledModal {...args} />,
84
+ };
85
+
86
+ /** Confirm shows the pending state (buttons lock, modal refuses to close). */
87
+ export const PendingOnConfirm: Story = {
88
+ args: { ...FaeClosed.args },
89
+ render: args => <ControlledModal {...args} pendingOnConfirm />,
90
+ };