@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.
- package/dist/{chunk-EWO5DH33.js → chunk-2MO4NEIR.js} +2 -2
- package/dist/{chunk-TROZIN2D.js → chunk-3KEAUJUU.js} +2 -2
- package/dist/{chunk-CJMPFRVH.cjs → chunk-4JL2WTIC.cjs} +9 -9
- package/dist/{chunk-CJMPFRVH.cjs.map → chunk-4JL2WTIC.cjs.map} +1 -1
- package/dist/{chunk-S7L62JWW.cjs → chunk-52FXIY2M.cjs} +5 -5
- package/dist/{chunk-S7L62JWW.cjs.map → chunk-52FXIY2M.cjs.map} +1 -1
- package/dist/{chunk-F3XRVBGI.cjs → chunk-57K53PKB.cjs} +26 -26
- package/dist/{chunk-F3XRVBGI.cjs.map → chunk-57K53PKB.cjs.map} +1 -1
- package/dist/{chunk-OWFU3G4S.cjs → chunk-6B6RCBVM.cjs} +87 -87
- package/dist/{chunk-OWFU3G4S.cjs.map → chunk-6B6RCBVM.cjs.map} +1 -1
- package/dist/{chunk-EESFDY5E.js → chunk-A5USNYZA.js} +2 -2
- package/dist/{chunk-NDUFYBZG.js → chunk-BYKPDIVC.js} +783 -722
- package/dist/chunk-BYKPDIVC.js.map +1 -0
- package/dist/{chunk-7HHJACP5.cjs → chunk-COZAC2RY.cjs} +4 -4
- package/dist/{chunk-7HHJACP5.cjs.map → chunk-COZAC2RY.cjs.map} +1 -1
- package/dist/{chunk-YKWGJS77.cjs → chunk-EVJ7DVCI.cjs} +61 -61
- package/dist/{chunk-YKWGJS77.cjs.map → chunk-EVJ7DVCI.cjs.map} +1 -1
- package/dist/{chunk-FXPELLEG.cjs → chunk-FESVTVRZ.cjs} +11 -11
- package/dist/{chunk-FXPELLEG.cjs.map → chunk-FESVTVRZ.cjs.map} +1 -1
- package/dist/{chunk-THFLCYDF.cjs → chunk-FUF6YX7H.cjs} +7 -7
- package/dist/{chunk-THFLCYDF.cjs.map → chunk-FUF6YX7H.cjs.map} +1 -1
- package/dist/{chunk-GIYY6ALS.js → chunk-HUXNMQD7.js} +6 -6
- package/dist/{chunk-5AVACJ3L.js → chunk-ITUVIG7K.js} +2 -2
- package/dist/{chunk-DR2PFOYX.js → chunk-J6F4342G.js} +2 -2
- package/dist/{chunk-HNNNY6NI.js → chunk-JOQBVXM3.js} +2 -2
- package/dist/{chunk-LPL53U5K.cjs → chunk-JWSWACXB.cjs} +14 -14
- package/dist/{chunk-LPL53U5K.cjs.map → chunk-JWSWACXB.cjs.map} +1 -1
- package/dist/{chunk-Q3WPMUZA.cjs → chunk-KJZVKS7X.cjs} +3 -3
- package/dist/{chunk-Q3WPMUZA.cjs.map → chunk-KJZVKS7X.cjs.map} +1 -1
- package/dist/{chunk-LAIABVLE.js → chunk-KRXQVZRX.js} +5 -5
- package/dist/{chunk-RCGRPTFN.cjs → chunk-NJ42DNRT.cjs} +31 -31
- package/dist/{chunk-RCGRPTFN.cjs.map → chunk-NJ42DNRT.cjs.map} +1 -1
- package/dist/{chunk-XGCXH7SP.cjs → chunk-NJ7QTEJM.cjs} +26 -26
- package/dist/{chunk-XGCXH7SP.cjs.map → chunk-NJ7QTEJM.cjs.map} +1 -1
- package/dist/{chunk-CABB5F4R.js → chunk-QZEEP2FO.js} +4 -4
- package/dist/{chunk-T33MZQQA.js → chunk-RNVMGZSM.js} +2 -2
- package/dist/{chunk-5BXAGDV5.js → chunk-RZWZCDBG.js} +2 -2
- package/dist/{chunk-Y5NBRXSK.js → chunk-T2NU3GZ7.js} +4 -4
- package/dist/{chunk-I5MSZCOS.cjs → chunk-T7ZTT7TO.cjs} +69 -8
- package/dist/chunk-T7ZTT7TO.cjs.map +1 -0
- package/dist/{chunk-ME3DCQPR.cjs → chunk-VKBGJGYF.cjs} +37 -37
- package/dist/{chunk-ME3DCQPR.cjs.map → chunk-VKBGJGYF.cjs.map} +1 -1
- package/dist/{chunk-EDD6ZOVC.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-modal.d.ts +6 -5
- package/dist/components/ui/reopen-ticket-modal.d.ts.map +1 -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-modal.tsx +9 -8
- package/src/components/ui/reopen-ticket-request-modal.tsx +112 -0
- package/src/stories/ReopenTicketModal.stories.tsx +8 -8
- package/src/stories/ReopenTicketRequestModal.stories.tsx +90 -0
- package/dist/chunk-I5MSZCOS.cjs.map +0 -1
- package/dist/chunk-NDUFYBZG.js.map +0 -1
- /package/dist/{chunk-EWO5DH33.js.map → chunk-2MO4NEIR.js.map} +0 -0
- /package/dist/{chunk-TROZIN2D.js.map → chunk-3KEAUJUU.js.map} +0 -0
- /package/dist/{chunk-EESFDY5E.js.map → chunk-A5USNYZA.js.map} +0 -0
- /package/dist/{chunk-GIYY6ALS.js.map → chunk-HUXNMQD7.js.map} +0 -0
- /package/dist/{chunk-5AVACJ3L.js.map → chunk-ITUVIG7K.js.map} +0 -0
- /package/dist/{chunk-DR2PFOYX.js.map → chunk-J6F4342G.js.map} +0 -0
- /package/dist/{chunk-HNNNY6NI.js.map → chunk-JOQBVXM3.js.map} +0 -0
- /package/dist/{chunk-LAIABVLE.js.map → chunk-KRXQVZRX.js.map} +0 -0
- /package/dist/{chunk-CABB5F4R.js.map → chunk-QZEEP2FO.js.map} +0 -0
- /package/dist/{chunk-T33MZQQA.js.map → chunk-RNVMGZSM.js.map} +0 -0
- /package/dist/{chunk-5BXAGDV5.js.map → chunk-RZWZCDBG.js.map} +0 -0
- /package/dist/{chunk-Y5NBRXSK.js.map → chunk-T2NU3GZ7.js.map} +0 -0
- /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
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
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
|
|
102
|
-
*
|
|
103
|
-
*
|
|
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
|
|
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
|
+
};
|