@flamingo-stack/openframe-frontend-core 0.0.540 → 0.0.541
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/{chunk-AQO2DWL2.js → chunk-2MO4NEIR.js} +2 -2
- package/dist/{chunk-5W4R3BUR.js → chunk-3KEAUJUU.js} +2 -2
- package/dist/{chunk-5ZRO72K4.cjs → chunk-4JL2WTIC.cjs} +9 -9
- package/dist/{chunk-5ZRO72K4.cjs.map → chunk-4JL2WTIC.cjs.map} +1 -1
- package/dist/{chunk-4PZEGH6E.cjs → chunk-52FXIY2M.cjs} +5 -5
- package/dist/{chunk-4PZEGH6E.cjs.map → chunk-52FXIY2M.cjs.map} +1 -1
- package/dist/{chunk-DC23N4XW.cjs → chunk-57K53PKB.cjs} +26 -26
- package/dist/{chunk-DC23N4XW.cjs.map → chunk-57K53PKB.cjs.map} +1 -1
- package/dist/{chunk-PV6L6F3T.cjs → chunk-6B6RCBVM.cjs} +87 -87
- package/dist/{chunk-PV6L6F3T.cjs.map → chunk-6B6RCBVM.cjs.map} +1 -1
- package/dist/{chunk-MKDOZF4C.js → chunk-A5USNYZA.js} +2 -2
- package/dist/{chunk-NQSGY5HW.js → chunk-BYKPDIVC.js} +780 -719
- package/dist/chunk-BYKPDIVC.js.map +1 -0
- package/dist/{chunk-YPTOQDCY.cjs → chunk-COZAC2RY.cjs} +4 -4
- package/dist/{chunk-YPTOQDCY.cjs.map → chunk-COZAC2RY.cjs.map} +1 -1
- package/dist/{chunk-IHGRWAUC.cjs → chunk-EVJ7DVCI.cjs} +61 -61
- package/dist/{chunk-IHGRWAUC.cjs.map → chunk-EVJ7DVCI.cjs.map} +1 -1
- package/dist/{chunk-4RXV54PY.cjs → chunk-FESVTVRZ.cjs} +11 -11
- package/dist/{chunk-4RXV54PY.cjs.map → chunk-FESVTVRZ.cjs.map} +1 -1
- package/dist/{chunk-TBMMC4GS.cjs → chunk-FUF6YX7H.cjs} +7 -7
- package/dist/{chunk-TBMMC4GS.cjs.map → chunk-FUF6YX7H.cjs.map} +1 -1
- package/dist/{chunk-XPGB6J75.js → chunk-HUXNMQD7.js} +6 -6
- package/dist/{chunk-F5R4IMRF.js → chunk-ITUVIG7K.js} +2 -2
- package/dist/{chunk-Y5TDZNWA.js → chunk-J6F4342G.js} +2 -2
- package/dist/{chunk-WA3IEPEY.js → chunk-JOQBVXM3.js} +2 -2
- package/dist/{chunk-6PBD57IZ.cjs → chunk-JWSWACXB.cjs} +14 -14
- package/dist/{chunk-6PBD57IZ.cjs.map → chunk-JWSWACXB.cjs.map} +1 -1
- package/dist/{chunk-IRTAZKWK.cjs → chunk-KJZVKS7X.cjs} +3 -3
- package/dist/{chunk-IRTAZKWK.cjs.map → chunk-KJZVKS7X.cjs.map} +1 -1
- package/dist/{chunk-IP4IZ4PB.js → chunk-KRXQVZRX.js} +5 -5
- package/dist/{chunk-N24ZUZZZ.cjs → chunk-NJ42DNRT.cjs} +31 -31
- package/dist/{chunk-N24ZUZZZ.cjs.map → chunk-NJ42DNRT.cjs.map} +1 -1
- package/dist/{chunk-GRU75FOX.cjs → chunk-NJ7QTEJM.cjs} +26 -26
- package/dist/{chunk-GRU75FOX.cjs.map → chunk-NJ7QTEJM.cjs.map} +1 -1
- package/dist/{chunk-VYQLCGFR.js → chunk-QZEEP2FO.js} +4 -4
- package/dist/{chunk-X5VMEFIA.js → chunk-RNVMGZSM.js} +2 -2
- package/dist/{chunk-2BKR65LQ.js → chunk-RZWZCDBG.js} +2 -2
- package/dist/{chunk-VUVKZBDO.js → chunk-T2NU3GZ7.js} +4 -4
- package/dist/{chunk-75W6ETMW.cjs → chunk-T7ZTT7TO.cjs} +66 -5
- package/dist/chunk-T7ZTT7TO.cjs.map +1 -0
- package/dist/{chunk-MXKY67SV.cjs → chunk-VKBGJGYF.cjs} +37 -37
- package/dist/{chunk-MXKY67SV.cjs.map → chunk-VKBGJGYF.cjs.map} +1 -1
- package/dist/{chunk-6WUUZ43M.js → chunk-XFYSYVXN.js} +2 -2
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +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 +134 -132
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +13 -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 +4 -2
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +3 -1
- package/dist/components/ui/reopen-ticket-request-modal.d.ts +33 -0
- package/dist/components/ui/reopen-ticket-request-modal.d.ts.map +1 -0
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/reopen-ticket-request-modal.tsx +112 -0
- package/src/stories/ReopenTicketRequestModal.stories.tsx +90 -0
- package/dist/chunk-75W6ETMW.cjs.map +0 -1
- package/dist/chunk-NQSGY5HW.js.map +0 -1
- /package/dist/{chunk-AQO2DWL2.js.map → chunk-2MO4NEIR.js.map} +0 -0
- /package/dist/{chunk-5W4R3BUR.js.map → chunk-3KEAUJUU.js.map} +0 -0
- /package/dist/{chunk-MKDOZF4C.js.map → chunk-A5USNYZA.js.map} +0 -0
- /package/dist/{chunk-XPGB6J75.js.map → chunk-HUXNMQD7.js.map} +0 -0
- /package/dist/{chunk-F5R4IMRF.js.map → chunk-ITUVIG7K.js.map} +0 -0
- /package/dist/{chunk-Y5TDZNWA.js.map → chunk-J6F4342G.js.map} +0 -0
- /package/dist/{chunk-WA3IEPEY.js.map → chunk-JOQBVXM3.js.map} +0 -0
- /package/dist/{chunk-IP4IZ4PB.js.map → chunk-KRXQVZRX.js.map} +0 -0
- /package/dist/{chunk-VYQLCGFR.js.map → chunk-QZEEP2FO.js.map} +0 -0
- /package/dist/{chunk-X5VMEFIA.js.map → chunk-RNVMGZSM.js.map} +0 -0
- /package/dist/{chunk-2BKR65LQ.js.map → chunk-RZWZCDBG.js.map} +0 -0
- /package/dist/{chunk-VUVKZBDO.js.map → chunk-T2NU3GZ7.js.map} +0 -0
- /package/dist/{chunk-6WUUZ43M.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
|
+
}
|
|
@@ -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
|
+
};
|