@qoretechnologies/reqraft 0.10.65-pr.148.gf86d88c → 0.10.65
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/.claude/CLAUDE.md +0 -1
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +1 -13
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -1
- package/dist/components/supportTicket/InterfaceReferenceTags.js +4 -11
- package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -1
- package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -1
- package/dist/components/supportTicket/ReferencePicker.js +1 -1
- package/dist/components/supportTicket/ReferencePicker.js.map +1 -1
- package/dist/components/supportTicket/index.d.ts +0 -2
- package/dist/components/supportTicket/index.d.ts.map +1 -1
- package/dist/components/supportTicket/index.js +0 -2
- package/dist/components/supportTicket/index.js.map +1 -1
- package/dist/components/supportTicket/meta.d.ts +2 -62
- package/dist/components/supportTicket/meta.d.ts.map +1 -1
- package/dist/components/supportTicket/meta.js +3 -116
- package/dist/components/supportTicket/meta.js.map +1 -1
- package/dist/components/supportTicket/ticketModel.d.ts +0 -10
- package/dist/components/supportTicket/ticketModel.d.ts.map +1 -1
- package/dist/components/ticketReferences/TicketReferences.d.ts +1 -10
- package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -1
- package/dist/components/ticketReferences/TicketReferences.js +8 -12
- package/dist/components/ticketReferences/TicketReferences.js.map +1 -1
- package/dist/components/ticketThread/TicketThread.d.ts +1 -30
- package/dist/components/ticketThread/TicketThread.d.ts.map +1 -1
- package/dist/components/ticketThread/TicketThread.js +13 -51
- package/dist/components/ticketThread/TicketThread.js.map +1 -1
- package/dist/components/ticketThread/index.d.ts +1 -1
- package/dist/components/ticketThread/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/StorageProvider.d.ts +0 -11
- package/dist/providers/StorageProvider.d.ts.map +1 -1
- package/dist/providers/StorageProvider.js +3 -21
- package/dist/providers/StorageProvider.js.map +1 -1
- package/dist/stores/currentUser/currentUser.d.ts +0 -42
- package/dist/stores/currentUser/currentUser.d.ts.map +1 -1
- package/dist/stores/currentUser/currentUser.js.map +1 -1
- package/package.json +9 -9
- package/src/components/form/engine/FormEngine.stories.tsx +5 -8
- package/src/components/form/fields/array/ArrayAutoField.stories.tsx +3 -6
- package/src/components/form/fields/boolean/Boolean.stories.tsx +3 -7
- package/src/components/form/fields/markdown/Markdown.stories.tsx +0 -6
- package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +1 -4
- package/src/components/form/fields/template/TemplateField.stories.tsx +0 -4
- package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +0 -56
- package/src/components/supportTicket/InterfaceReferenceTags.tsx +7 -27
- package/src/components/supportTicket/ReferencePicker.tsx +1 -4
- package/src/components/supportTicket/index.ts +0 -2
- package/src/components/supportTicket/meta.ts +4 -156
- package/src/components/supportTicket/ticketModel.ts +0 -10
- package/src/components/ticketReferences/TicketReferences.stories.tsx +0 -40
- package/src/components/ticketReferences/TicketReferences.tsx +10 -24
- package/src/components/ticketThread/TicketThread.stories.tsx +1 -103
- package/src/components/ticketThread/TicketThread.tsx +15 -131
- package/src/components/ticketThread/index.ts +0 -1
- package/src/index.tsx +0 -1
- package/src/providers/StorageProvider.tsx +3 -20
- package/src/stores/currentUser/currentUser.tsx +0 -43
- package/dist/components/supportTicket/WorkspaceAccessStatus.d.ts +0 -55
- package/dist/components/supportTicket/WorkspaceAccessStatus.d.ts.map +0 -1
- package/dist/components/supportTicket/WorkspaceAccessStatus.js +0 -79
- package/dist/components/supportTicket/WorkspaceAccessStatus.js.map +0 -1
- package/dist/components/supportTicket/accessMeta.d.ts +0 -50
- package/dist/components/supportTicket/accessMeta.d.ts.map +0 -1
- package/dist/components/supportTicket/accessMeta.js +0 -174
- package/dist/components/supportTicket/accessMeta.js.map +0 -1
- package/src/components/supportTicket/WorkspaceAccessStatus.stories.tsx +0 -105
- package/src/components/supportTicket/WorkspaceAccessStatus.tsx +0 -176
- package/src/components/supportTicket/accessMeta.ts +0 -210
|
@@ -23,7 +23,6 @@ import { ImageLightbox } from '../imageLightbox';
|
|
|
23
23
|
import {
|
|
24
24
|
defaultInterfaceIcon,
|
|
25
25
|
IInterfaceReference,
|
|
26
|
-
interfaceReferenceKey,
|
|
27
26
|
toInterfaceKindMark,
|
|
28
27
|
TResolveInterfaceIcon,
|
|
29
28
|
TTicketViewerRole,
|
|
@@ -74,15 +73,6 @@ export interface ITicketReferencesProps {
|
|
|
74
73
|
onDownloadAttachment?: (attachmentId: string, filename: string) => void;
|
|
75
74
|
/** open a referenced interface; reference chips are static (informational) when omitted */
|
|
76
75
|
onInterfaceClick?: (reference: IInterfaceReference) => void;
|
|
77
|
-
/**
|
|
78
|
-
* Whether a reference can be opened right now; consulted only when `onInterfaceClick`
|
|
79
|
-
* is given. A refused row offers no Open action and carries `unlinkableReason` in its
|
|
80
|
-
* metadata line — in the row itself, so it reads on a touch screen too, where a
|
|
81
|
-
* tooltip would never show. See `InterfaceReferenceTags` for the cases.
|
|
82
|
-
*/
|
|
83
|
-
isInterfaceLinkable?: (reference: IInterfaceReference) => boolean;
|
|
84
|
-
/** Why a reference is not linkable, shown in the row's metadata after its kind. */
|
|
85
|
-
unlinkableReason?: (reference: IInterfaceReference) => string | undefined;
|
|
86
76
|
/** Reveal the message a reference or file came from, in the conversation.
|
|
87
77
|
* Omitted ⇒ the action is not offered. It is also omitted per-row for anything filed
|
|
88
78
|
* against the ticket itself rather than a reply, which has no message to reveal. */
|
|
@@ -207,9 +197,7 @@ export interface IResolvedInterfaceReference extends IInterfaceReference {
|
|
|
207
197
|
const dedupeRefs = (refs: IResolvedInterfaceReference[]): IResolvedInterfaceReference[] => {
|
|
208
198
|
const byKey = new Map<string, IResolvedInterfaceReference>();
|
|
209
199
|
for (const r of refs) {
|
|
210
|
-
|
|
211
|
-
// server stored on the ticket are one row
|
|
212
|
-
const key = interfaceReferenceKey(r);
|
|
200
|
+
const key = `${r.interface_kind}:${r.interface_name}`;
|
|
213
201
|
const kept = byKey.get(key);
|
|
214
202
|
if (!kept) {
|
|
215
203
|
byKey.set(key, r);
|
|
@@ -433,8 +421,6 @@ export const TicketReferences = ({
|
|
|
433
421
|
fetchAttachmentUrl,
|
|
434
422
|
onDownloadAttachment,
|
|
435
423
|
onInterfaceClick,
|
|
436
|
-
isInterfaceLinkable,
|
|
437
|
-
unlinkableReason,
|
|
438
424
|
onShowInChat,
|
|
439
425
|
resolveInterfaceIcon,
|
|
440
426
|
preferKindImage = false,
|
|
@@ -805,28 +791,28 @@ export const TicketReferences = ({
|
|
|
805
791
|
// the same vocabulary and precedence as the reference chips: the kind's
|
|
806
792
|
// font icon, and the chosen image once it has loaded
|
|
807
793
|
const { icon, image } = markOf(reference);
|
|
808
|
-
// a row the consumer refuses offers no Open, and says why in its metadata
|
|
809
|
-
// line rather than in a tooltip, so the reason reads on touch as well
|
|
810
|
-
const linkable = !!onInterfaceClick && (isInterfaceLinkable?.(reference) ?? true);
|
|
811
|
-
const reason =
|
|
812
|
-
onInterfaceClick && !linkable ? unlinkableReason?.(reference) : undefined;
|
|
813
794
|
return (
|
|
814
795
|
<ReqoreEntityRow
|
|
815
|
-
key={
|
|
796
|
+
key={
|
|
797
|
+
reference.reference_id ??
|
|
798
|
+
`${reference.interface_kind}:${reference.interface_name}`
|
|
799
|
+
}
|
|
816
800
|
icon={icon}
|
|
817
801
|
iconImage={isImageLoaded(image) ? image : undefined}
|
|
818
802
|
label={reference.interface_name}
|
|
819
803
|
metadata={
|
|
820
804
|
<ReqoreTextEffect effect={META_EFFECT}>
|
|
821
|
-
{
|
|
805
|
+
{reference.interface_kind}
|
|
822
806
|
</ReqoreTextEffect>
|
|
823
807
|
}
|
|
824
808
|
flat
|
|
825
809
|
tooltip={
|
|
826
|
-
|
|
810
|
+
onInterfaceClick ?
|
|
811
|
+
`Open ${reference.interface_kind} ${reference.interface_name}`
|
|
812
|
+
: undefined
|
|
827
813
|
}
|
|
828
814
|
actions={rowActions(
|
|
829
|
-
|
|
815
|
+
onInterfaceClick ?
|
|
830
816
|
{
|
|
831
817
|
icon: 'ArrowRightUpLine',
|
|
832
818
|
tooltip: 'Open',
|
|
@@ -5,7 +5,7 @@ import styled from 'styled-components';
|
|
|
5
5
|
import { thinScrollbar } from '../../helpers/scrollbar';
|
|
6
6
|
import { StoryMeta } from '../../types';
|
|
7
7
|
import type { IInterfaceReference, TTicketMessageAuthor } from '../supportTicket/meta';
|
|
8
|
-
import {
|
|
8
|
+
import { ITicketThreadMessage, TicketThread } from './TicketThread';
|
|
9
9
|
|
|
10
10
|
/* The drawer body's scroll area — the thin Qorus scrollbar rather than the platform
|
|
11
11
|
* default, matching what the app's ThreadScroll should carry. */
|
|
@@ -827,105 +827,3 @@ export const Everything: Story = {
|
|
|
827
827
|
await expect(canvas.getByText('pgsql-omq')).toBeInTheDocument();
|
|
828
828
|
},
|
|
829
829
|
};
|
|
830
|
-
|
|
831
|
-
// The delegated-access lifecycle as the control plane writes it into the thread
|
|
832
|
-
// (qorus#693): a request from support, then the customer's answer and what followed.
|
|
833
|
-
const SUPPORT_ACCESS_THREAD: ITicketThreadMessage[] = [
|
|
834
|
-
{ message_id: 'o1', author_type: 'customer', author_id: 'acme', body: 'The nightly reconcile has been failing since Tuesday.', created: ago(400) },
|
|
835
|
-
{
|
|
836
|
-
message_id: 'sa1',
|
|
837
|
-
author_type: 'system',
|
|
838
|
-
author_id: 'system',
|
|
839
|
-
system_event: 'support_access.requested',
|
|
840
|
-
system_detail: { reason: 'To read the nightly-reconcile job and its last three runs.', suggested_hours: 24 },
|
|
841
|
-
body: 'Qorus Support asks to read this workspace for **24 hours** to look at `nightly-reconcile` and its last three runs. Nothing is changed; every read is recorded here.',
|
|
842
|
-
created: ago(300),
|
|
843
|
-
},
|
|
844
|
-
{ message_id: 'sa2', author_type: 'system', author_id: 'system', system_event: 'support_access.granted', body: 'Workspace access granted until **tomorrow 14:00**.', created: ago(280) },
|
|
845
|
-
{ message_id: 'sa3', author_type: 'system', author_id: 'system', system_event: 'support_access.first_used', body: 'Qorus Support opened the workspace.', created: ago(200) },
|
|
846
|
-
{ message_id: 'st1', author_type: 'staff', author_id: 'nick', body: 'Found it: the job runs with the pool default from 3.2. Fix coming in 3.2.1.', created: ago(150) },
|
|
847
|
-
{ message_id: 'sa4', author_type: 'system', author_id: 'system', system_event: 'support_access.extended', body: 'Workspace access extended until **Friday 14:00**.', created: ago(100) },
|
|
848
|
-
{ message_id: 'sa5', author_type: 'system', author_id: 'system', system_event: 'support_access.revoked', body: 'Workspace access revoked by you.', created: ago(40) },
|
|
849
|
-
{ message_id: 'sa6', author_type: 'system', author_id: 'system', system_event: 'support_access.expired', body: 'Workspace access expired.', created: ago(5) },
|
|
850
|
-
];
|
|
851
|
-
|
|
852
|
-
/**
|
|
853
|
-
* A support request for workspace access is the one system message that asks the reader
|
|
854
|
-
* something, so it renders as a card rather than a line — and the customer's surface can
|
|
855
|
-
* put its answer on it through `systemEventAction`. Every other access event is a line
|
|
856
|
-
* marked with its own icon.
|
|
857
|
-
*/
|
|
858
|
-
export const SupportAccessRequestCard: Story = {
|
|
859
|
-
parameters: {
|
|
860
|
-
docs: {
|
|
861
|
-
description: {
|
|
862
|
-
story:
|
|
863
|
-
'Renders the delegated-access events in a customer thread in the notification look: the support request as a card (frosted surface, icon tile, the Grant action supplied by the consumer under the text), and the granted, opened, extended, revoked and expired events as compact pills centred in the thread (qorus#693).',
|
|
864
|
-
},
|
|
865
|
-
},
|
|
866
|
-
},
|
|
867
|
-
args: {
|
|
868
|
-
viewerRole: 'customer',
|
|
869
|
-
messages: SUPPORT_ACCESS_THREAD,
|
|
870
|
-
systemEventAction: fn(
|
|
871
|
-
(message: ITicketThreadMessage): ITicketSystemEventAction | undefined =>
|
|
872
|
-
message.system_event === 'support_access.requested'
|
|
873
|
-
? { label: 'Grant access', icon: 'ShieldCheckLine', onClick: fn() }
|
|
874
|
-
: undefined
|
|
875
|
-
),
|
|
876
|
-
},
|
|
877
|
-
async play({ canvasElement }) {
|
|
878
|
-
const canvas = within(canvasElement);
|
|
879
|
-
await expect(canvas.getByText('Workspace access requested')).toBeInTheDocument();
|
|
880
|
-
await expect(canvasElement.querySelector('.reqore-ticket-system-card')).not.toBeNull();
|
|
881
|
-
const grant = canvas.getByText('Grant access');
|
|
882
|
-
await expect(grant).toBeInTheDocument();
|
|
883
|
-
await expect(canvas.getByText(/Workspace access granted until/)).toBeInTheDocument();
|
|
884
|
-
await expect(canvas.getByText(/Workspace access expired/)).toBeInTheDocument();
|
|
885
|
-
},
|
|
886
|
-
};
|
|
887
|
-
|
|
888
|
-
/** The staff side of the same thread: the request card renders with no action, because
|
|
889
|
-
* the answer is the customer's, and the lines read the same. */
|
|
890
|
-
export const SupportAccessRequestCardStaffView: Story = {
|
|
891
|
-
parameters: {
|
|
892
|
-
docs: {
|
|
893
|
-
description: {
|
|
894
|
-
story:
|
|
895
|
-
'Renders the same delegated-access thread for staff: the request card carries no action (only the customer can answer), the lines are unchanged, and the staff reply sits on the right.',
|
|
896
|
-
},
|
|
897
|
-
},
|
|
898
|
-
},
|
|
899
|
-
args: {
|
|
900
|
-
viewerRole: 'staff',
|
|
901
|
-
messages: SUPPORT_ACCESS_THREAD,
|
|
902
|
-
},
|
|
903
|
-
async play({ canvasElement }) {
|
|
904
|
-
const canvas = within(canvasElement);
|
|
905
|
-
await expect(canvas.getByText('Workspace access requested')).toBeInTheDocument();
|
|
906
|
-
await expect(canvas.queryByText('Grant access')).toBeNull();
|
|
907
|
-
await expect(canvasElement.querySelectorAll('.reqore-ticket-system-card-action')).toHaveLength(0);
|
|
908
|
-
},
|
|
909
|
-
};
|
|
910
|
-
|
|
911
|
-
/** The request card at phone width: the card and its action stay inside the screen. */
|
|
912
|
-
export const SupportAccessRequestCardMobile: Story = {
|
|
913
|
-
parameters: {
|
|
914
|
-
qlip: { viewport: { width: 390, height: 844 } },
|
|
915
|
-
docs: {
|
|
916
|
-
description: {
|
|
917
|
-
story:
|
|
918
|
-
'Renders the delegated-access thread at 390px: the request card and its Grant action fit the screen, and the event lines wrap rather than overflow.',
|
|
919
|
-
},
|
|
920
|
-
},
|
|
921
|
-
},
|
|
922
|
-
args: SupportAccessRequestCard.args,
|
|
923
|
-
async play({ canvasElement }) {
|
|
924
|
-
const canvas = within(canvasElement);
|
|
925
|
-
await expect(canvas.getByText('Grant access')).toBeInTheDocument();
|
|
926
|
-
const card = canvasElement.querySelector('.reqore-ticket-system-card') as HTMLElement;
|
|
927
|
-
await expect(card).not.toBeNull();
|
|
928
|
-
await waitFor(() => expect(card.getBoundingClientRect().right).toBeLessThanOrEqual(window.innerWidth));
|
|
929
|
-
},
|
|
930
|
-
};
|
|
931
|
-
|
|
@@ -5,13 +5,11 @@ import {
|
|
|
5
5
|
ReqoreControlGroup,
|
|
6
6
|
ReqoreEmptyState,
|
|
7
7
|
ReqoreIcon,
|
|
8
|
-
ReqoreNotification,
|
|
9
8
|
ReqoreP,
|
|
10
9
|
ReqoreSkeleton,
|
|
11
10
|
ReqoreTimeAgo,
|
|
12
11
|
useReqoreTheme,
|
|
13
12
|
} from '@qoretechnologies/reqore';
|
|
14
|
-
import { TReqoreIntent } from '@qoretechnologies/reqore/dist/constants/theme';
|
|
15
13
|
import { getReadableColor } from '@qoretechnologies/reqore/dist/helpers/colors';
|
|
16
14
|
import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
|
|
17
15
|
import { rgba } from 'polished';
|
|
@@ -21,11 +19,8 @@ import styled from 'styled-components';
|
|
|
21
19
|
import { formatBytes, isImage } from '../../helpers/common';
|
|
22
20
|
import { ImageLightbox } from '../imageLightbox';
|
|
23
21
|
import {
|
|
24
|
-
dedupeInterfaceReferences,
|
|
25
22
|
IInterfaceReference,
|
|
26
23
|
InterfaceReferenceTags,
|
|
27
|
-
TICKET_SYSTEM_EVENT_META,
|
|
28
|
-
ticketSystemEventMeta,
|
|
29
24
|
TResolveInterfaceIcon,
|
|
30
25
|
TTicketMessageAuthor,
|
|
31
26
|
TTicketViewerRole,
|
|
@@ -74,23 +69,6 @@ export interface ITicketThreadMessage {
|
|
|
74
69
|
attachments?: ITicketThreadAttachment[];
|
|
75
70
|
/** Qorus interfaces this message references, as `{kind, name}` snapshots. */
|
|
76
71
|
referenced_interfaces?: IInterfaceReference[];
|
|
77
|
-
/** For a `system` message: the event it records (`support_access.requested`, …); see
|
|
78
|
-
* `ITicketMessage.system_event`. Decides the line's mark, and the card variant. */
|
|
79
|
-
system_event?: string;
|
|
80
|
-
/** For a `system` message: the event's structured payload, as the server wrote it. */
|
|
81
|
-
system_detail?: Record<string, unknown>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* An action a consumer attaches to a system event's card — the customer's Grant on a
|
|
86
|
-
* `support_access.requested` card. The thread renders it as the card's action; what it
|
|
87
|
-
* does (open the grant drawer, call the API) is the consumer's.
|
|
88
|
-
*/
|
|
89
|
-
export interface ITicketSystemEventAction {
|
|
90
|
-
label: string;
|
|
91
|
-
icon?: IReqoreIconName;
|
|
92
|
-
intent?: TReqoreIntent;
|
|
93
|
-
onClick: () => void;
|
|
94
72
|
}
|
|
95
73
|
|
|
96
74
|
export interface ITicketThreadProps {
|
|
@@ -119,17 +97,6 @@ export interface ITicketThreadProps {
|
|
|
119
97
|
/** open a referenced interface; reference chips are static when omitted (e.g. the staff
|
|
120
98
|
* view, which can't reach the customer's instance, leaves this out). */
|
|
121
99
|
onInterfaceClick?: (reference: IInterfaceReference) => void;
|
|
122
|
-
/** Whether a reference chip can be opened right now, and why not; passed through to
|
|
123
|
-
* every message's `InterfaceReferenceTags` — see the cases there. */
|
|
124
|
-
isInterfaceLinkable?: (reference: IInterfaceReference) => boolean;
|
|
125
|
-
unlinkableReason?: (reference: IInterfaceReference) => string | undefined;
|
|
126
|
-
/**
|
|
127
|
-
* The action to offer on a system event's card, if any — asked per system message, so a
|
|
128
|
-
* consumer answers for the events and states it handles (the IDE offers Grant on a
|
|
129
|
-
* `support_access.requested` card while the request is open; the staff portal offers
|
|
130
|
-
* nothing). Omitted ⇒ cards carry no action.
|
|
131
|
-
*/
|
|
132
|
-
systemEventAction?: (message: ITicketThreadMessage) => ITicketSystemEventAction | undefined;
|
|
133
100
|
/** The TICKET's own references (picked when filing). Echoed as chips on the opening
|
|
134
101
|
* message so the conversation carries them too — when they lived only in the header
|
|
135
102
|
* rail + References tab, readers looked for them in the thread and concluded the
|
|
@@ -563,94 +530,6 @@ const AttachmentList = ({
|
|
|
563
530
|
);
|
|
564
531
|
};
|
|
565
532
|
|
|
566
|
-
/** The anchor a "show in chat" scroll lands on; the notification itself takes no id. */
|
|
567
|
-
const StyledSystemCard = styled.div`
|
|
568
|
-
width: 100%;
|
|
569
|
-
`;
|
|
570
|
-
|
|
571
|
-
/** A one-line access event sits centred in the thread, as the plain system lines do. */
|
|
572
|
-
const StyledSystemPill = styled.div`
|
|
573
|
-
display: flex;
|
|
574
|
-
justify-content: center;
|
|
575
|
-
width: 100%;
|
|
576
|
-
`;
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* A system message. The request for workspace access renders as a card — it asks the
|
|
580
|
-
* reader something, and can carry the customer's answer — while every other event stays
|
|
581
|
-
* the centred muted line, marked with its event's icon when the event is known.
|
|
582
|
-
*
|
|
583
|
-
* The card wears the notification look (Nick, 2026-10-20): the same frosted surface,
|
|
584
|
-
* icon tile and action row as a toast, because a thread message that asks the reader to
|
|
585
|
-
* act is a notification that happens to live in the thread. It never times out and
|
|
586
|
-
* cannot be dismissed; the answer is the one action on it.
|
|
587
|
-
*/
|
|
588
|
-
const SystemMessage = ({
|
|
589
|
-
message,
|
|
590
|
-
action,
|
|
591
|
-
}: {
|
|
592
|
-
message: ITicketThreadMessage;
|
|
593
|
-
action?: ITicketSystemEventAction;
|
|
594
|
-
}) => {
|
|
595
|
-
const meta = ticketSystemEventMeta(message.system_event);
|
|
596
|
-
if (message.system_event === 'support_access.requested') {
|
|
597
|
-
return (
|
|
598
|
-
<StyledSystemCard id={ticketMessageDomId(message.message_id)} className='reqore-ticket-system-card'>
|
|
599
|
-
<ReqoreNotification
|
|
600
|
-
fluid
|
|
601
|
-
// in the page, not over it: no drop shadow, no cast light (reqore 0.78.8)
|
|
602
|
-
lift={false}
|
|
603
|
-
icon={meta.icon}
|
|
604
|
-
intent={meta.intent}
|
|
605
|
-
title={meta.label}
|
|
606
|
-
content={<MessageBody>{message.body}</MessageBody>}
|
|
607
|
-
actions={
|
|
608
|
-
action
|
|
609
|
-
? [
|
|
610
|
-
{
|
|
611
|
-
label: action.label,
|
|
612
|
-
icon: action.icon,
|
|
613
|
-
intent: action.intent ?? 'success',
|
|
614
|
-
className: 'reqore-ticket-system-card-action',
|
|
615
|
-
// a notification's actions are plain buttons that keep their label at
|
|
616
|
-
// every width (a phone is where the customer reads this); only a panel
|
|
617
|
-
// action folds to an icon, so nothing has to be asked for here
|
|
618
|
-
// the card stays: the thread, not a click, decides when it goes
|
|
619
|
-
closeOnClick: false,
|
|
620
|
-
onClick: action.onClick,
|
|
621
|
-
},
|
|
622
|
-
]
|
|
623
|
-
: undefined
|
|
624
|
-
}
|
|
625
|
-
/>
|
|
626
|
-
</StyledSystemCard>
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
// A delegated-access event the vocabulary knows is a compact pill in the same look as
|
|
630
|
-
// the request card, so the grant's story reads as one family: asked, granted, opened,
|
|
631
|
-
// extended, revoked, expired. Any other system line stays the plain centred line.
|
|
632
|
-
if (message.system_event && message.system_event in TICKET_SYSTEM_EVENT_META) {
|
|
633
|
-
return (
|
|
634
|
-
<StyledSystemPill id={ticketMessageDomId(message.message_id)} className='reqore-ticket-system-pill'>
|
|
635
|
-
<ReqoreNotification
|
|
636
|
-
compact
|
|
637
|
-
lift={false}
|
|
638
|
-
size='small'
|
|
639
|
-
icon={meta.icon}
|
|
640
|
-
intent={meta.intent}
|
|
641
|
-
content={<ReactMarkdown components={{ p: 'span' }}>{message.body}</ReactMarkdown>}
|
|
642
|
-
/>
|
|
643
|
-
</StyledSystemPill>
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
return (
|
|
647
|
-
<StyledSystemRow id={ticketMessageDomId(message.message_id)}>
|
|
648
|
-
<ReqoreIcon icon={meta.icon} size='tiny' />
|
|
649
|
-
<ReactMarkdown>{message.body}</ReactMarkdown>
|
|
650
|
-
</StyledSystemRow>
|
|
651
|
-
);
|
|
652
|
-
};
|
|
653
|
-
|
|
654
533
|
type TThreadSegment =
|
|
655
534
|
| { kind: 'system'; message: ITicketThreadMessage }
|
|
656
535
|
| { kind: 'bubbles'; messages: ITicketThreadMessage[] };
|
|
@@ -686,9 +565,6 @@ export const TicketThread = ({
|
|
|
686
565
|
resolveInterfaceIcon,
|
|
687
566
|
preferKindImage,
|
|
688
567
|
onInterfaceClick,
|
|
689
|
-
isInterfaceLinkable,
|
|
690
|
-
unlinkableReason,
|
|
691
|
-
systemEventAction,
|
|
692
568
|
ticketReferences,
|
|
693
569
|
}: ITicketThreadProps) => {
|
|
694
570
|
// Open scrolled to the newest message, and follow new arrivals down — a thread is read
|
|
@@ -732,7 +608,15 @@ export const TicketThread = ({
|
|
|
732
608
|
return undefined;
|
|
733
609
|
}
|
|
734
610
|
const opening = messages.find((m) => m.message_id === openingMessageId);
|
|
735
|
-
|
|
611
|
+
const seen = new Set<string>();
|
|
612
|
+
return [...ticketReferences, ...(opening?.referenced_interfaces ?? [])].filter((ref) => {
|
|
613
|
+
const key = `${ref.interface_kind}:${ref.interface_name}`;
|
|
614
|
+
if (seen.has(key)) {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
seen.add(key);
|
|
618
|
+
return true;
|
|
619
|
+
});
|
|
736
620
|
}, [ticketReferences, messages, openingMessageId]);
|
|
737
621
|
|
|
738
622
|
const refsFor = (message: ITicketThreadMessage): IInterfaceReference[] | undefined =>
|
|
@@ -761,11 +645,13 @@ export const TicketThread = ({
|
|
|
761
645
|
<StyledThread ref={threadRef}>
|
|
762
646
|
{segments.map((segment, index) =>
|
|
763
647
|
segment.kind === 'system' ? (
|
|
764
|
-
<
|
|
648
|
+
<StyledSystemRow
|
|
765
649
|
key={segment.message.message_id}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
650
|
+
id={ticketMessageDomId(segment.message.message_id)}
|
|
651
|
+
>
|
|
652
|
+
<ReqoreIcon icon='InformationLine' size='tiny' />
|
|
653
|
+
<ReactMarkdown>{segment.message.body}</ReactMarkdown>
|
|
654
|
+
</StyledSystemRow>
|
|
769
655
|
) : (
|
|
770
656
|
<ReqoreBubbleGroup key={`group-${index}`}>
|
|
771
657
|
{segment.messages.map((message) => (
|
|
@@ -817,8 +703,6 @@ export const TicketThread = ({
|
|
|
817
703
|
resolveInterfaceIcon={resolveInterfaceIcon}
|
|
818
704
|
preferKindImage={preferKindImage}
|
|
819
705
|
onInterfaceClick={onInterfaceClick}
|
|
820
|
-
isInterfaceLinkable={isInterfaceLinkable}
|
|
821
|
-
unlinkableReason={unlinkableReason}
|
|
822
706
|
/>
|
|
823
707
|
</div>
|
|
824
708
|
) : null}
|
package/src/index.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { ReqraftStorageContext, TReqraftStorage } from '../contexts/StorageConte
|
|
|
6
6
|
import { useFetch } from '../hooks/useFetch/useFetch';
|
|
7
7
|
import { useReqraftProperty } from '../hooks/useReqraftProperty';
|
|
8
8
|
import { TReqraftStorageValue } from '../hooks/useStorage/useStorage';
|
|
9
|
-
import { currentUserStore
|
|
9
|
+
import { currentUserStore } from '../stores/currentUser/currentUser';
|
|
10
10
|
import { isQueryCancelled } from '../utils/fetch';
|
|
11
11
|
import { IReqraftProviderProps } from './ReqraftProvider';
|
|
12
12
|
|
|
@@ -27,18 +27,6 @@ export const applyStorageWrite = (
|
|
|
27
27
|
value: unknown
|
|
28
28
|
): TReqraftStorage => set(cloneDeep(storage) ?? {}, path, value);
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
* Whether the current user's storage may be persisted. The server answers
|
|
32
|
-
* `storage_writable: false` for an identity whose storage is not its own to keep — a
|
|
33
|
-
* staff member in an interactive support session reads a customer's workspace as the
|
|
34
|
-
* workspace's reader principal, and a preference they flip must not land in that
|
|
35
|
-
* principal's storage (qorus#693). A write for such a user is applied in memory only,
|
|
36
|
-
* so the page behaves as it always does, and nothing is sent. A user the flag is
|
|
37
|
-
* absent for is writable: that is every server from before the flag existed.
|
|
38
|
-
*/
|
|
39
|
-
export const storageIsWritable = (user?: Pick<ICurrentUser, 'storage_writable'>): boolean =>
|
|
40
|
-
user?.storage_writable !== false;
|
|
41
|
-
|
|
42
30
|
export interface IReqraftStorageProviderProps
|
|
43
31
|
extends Pick<IReqraftProviderProps, 'waitForStorage'> {
|
|
44
32
|
children: ReactNode;
|
|
@@ -107,10 +95,7 @@ export const ReqraftUserProvider = ({ children, waitForStorage }: IReqraftStorag
|
|
|
107
95
|
|
|
108
96
|
updateCurrentUserStorage(updatedStorage);
|
|
109
97
|
|
|
110
|
-
|
|
111
|
-
if (storageIsWritable(currentUserStore.getState().currentUser)) {
|
|
112
|
-
load({ body: { storage: updatedStorage } });
|
|
113
|
-
}
|
|
98
|
+
load({ body: { storage: updatedStorage } });
|
|
114
99
|
},
|
|
115
100
|
[appName, load, updateCurrentUserStorage]
|
|
116
101
|
);
|
|
@@ -126,9 +111,7 @@ export const ReqraftUserProvider = ({ children, waitForStorage }: IReqraftStorag
|
|
|
126
111
|
|
|
127
112
|
updateCurrentUserStorage(updatedStorage);
|
|
128
113
|
|
|
129
|
-
|
|
130
|
-
load({ body: { storage_path: _path } });
|
|
131
|
-
}
|
|
114
|
+
load({ body: { storage_path: _path } });
|
|
132
115
|
},
|
|
133
116
|
[appName, load, updateCurrentUserStorage]
|
|
134
117
|
);
|
|
@@ -1,39 +1,6 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
2
|
import { IReqraftFetchResponse, isQueryCancelled, query } from '../../utils/fetch';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* The interactive support session a current user is in, when it is one (qorus#693
|
|
6
|
-
* Layer 3): a staff member reading a customer's workspace in the IDE, read-only, as
|
|
7
|
-
* the workspace's support reader. Exactly what the server puts on the request context
|
|
8
|
-
* for such a session, never a token. Absent for every ordinary user.
|
|
9
|
-
*/
|
|
10
|
-
export interface ICurrentUserSupportSession {
|
|
11
|
-
/** the session's id on the control plane */
|
|
12
|
-
session_id: string;
|
|
13
|
-
/** the customer's grant the session runs under */
|
|
14
|
-
grant_id: string;
|
|
15
|
-
/** the ticket the grant belongs to */
|
|
16
|
-
ticket_id: string;
|
|
17
|
-
/** the ticket's number, for display */
|
|
18
|
-
ticket_number?: number;
|
|
19
|
-
/** the control-plane staff identity the session belongs to */
|
|
20
|
-
actor: string;
|
|
21
|
-
/** the staff member's display name, when the control plane has it */
|
|
22
|
-
actor_name?: string;
|
|
23
|
-
/** the workspace's portable namespace */
|
|
24
|
-
group_namespace: number;
|
|
25
|
-
/** the workspace group's name */
|
|
26
|
-
group_name?: string;
|
|
27
|
-
/** the identity profile, `workspace-read` */
|
|
28
|
-
profile: string;
|
|
29
|
-
/** when the session ends at the latest: the grant's expiry (ISO 8601) */
|
|
30
|
-
expires: string;
|
|
31
|
-
/** where the IDE opens — `kind` and `name` or `id` — when the staff member came from a reference */
|
|
32
|
-
target?: Record<string, unknown>;
|
|
33
|
-
/** the control-plane page to return to */
|
|
34
|
-
portal_url?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
4
|
export interface ICurrentUser {
|
|
38
5
|
provider: string;
|
|
39
6
|
username: string;
|
|
@@ -52,16 +19,6 @@ export interface ICurrentUser {
|
|
|
52
19
|
storage?: Record<string, any>;
|
|
53
20
|
sandboxed?: boolean;
|
|
54
21
|
default_group?: string;
|
|
55
|
-
/**
|
|
56
|
-
* Whether the user's storage can be written back to the server. `false` for an
|
|
57
|
-
* identity whose storage is not its own to keep — an interactive support session
|
|
58
|
-
* reads a workspace as its reader principal — and then every storage write stays
|
|
59
|
-
* in memory for the page's lifetime (see `ReqraftUserProvider`). Absent means
|
|
60
|
-
* writable, as on every server from before the flag.
|
|
61
|
-
*/
|
|
62
|
-
storage_writable?: boolean;
|
|
63
|
-
/** present only in an interactive support session (qorus#693 Layer 3) */
|
|
64
|
-
support_session?: ICurrentUserSupportSession;
|
|
65
22
|
}
|
|
66
23
|
|
|
67
24
|
export interface ICurrentUserStore {
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { TSizes } from '@qoretechnologies/reqore/dist/constants/sizes';
|
|
2
|
-
import { TReqoreIntent } from '@qoretechnologies/reqore/dist/constants/theme';
|
|
3
|
-
import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
|
|
4
|
-
import { TSupportAccessPolicy, TSupportGrantState } from './accessMeta';
|
|
5
|
-
import { TTicketViewerRole } from './meta';
|
|
6
|
-
/** What the value needs of a grant: the plane's `SupportAccessGrant`, a subset. */
|
|
7
|
-
export interface IWorkspaceAccessGrantSummary {
|
|
8
|
-
state: TSupportGrantState | string;
|
|
9
|
-
issued_at?: string;
|
|
10
|
-
expires_at?: string;
|
|
11
|
-
until_resolved?: boolean;
|
|
12
|
-
created_by_type?: 'customer' | 'staff' | 'system' | string;
|
|
13
|
-
ended_at?: string;
|
|
14
|
-
end_cause?: string;
|
|
15
|
-
}
|
|
16
|
-
/** What the value needs of an open staff request. */
|
|
17
|
-
export interface IWorkspaceAccessRequestSummary {
|
|
18
|
-
requested_at: string;
|
|
19
|
-
suggested_duration?: string;
|
|
20
|
-
}
|
|
21
|
-
export interface IWorkspaceAccessStatusAction {
|
|
22
|
-
label: string;
|
|
23
|
-
icon?: IReqoreIconName;
|
|
24
|
-
intent?: TReqoreIntent;
|
|
25
|
-
onClick: () => void;
|
|
26
|
-
loading?: boolean;
|
|
27
|
-
tooltip?: string;
|
|
28
|
-
}
|
|
29
|
-
export interface IWorkspaceAccessStatusProps {
|
|
30
|
-
/** the ticket's active grant, or the last one when none is active */
|
|
31
|
-
grant?: IWorkspaceAccessGrantSummary;
|
|
32
|
-
/** the open staff request, when there is one and no active grant */
|
|
33
|
-
request?: IWorkspaceAccessRequestSummary;
|
|
34
|
-
/** the account's setting; `never` is said when nothing else is there to say */
|
|
35
|
-
policy?: TSupportAccessPolicy | string;
|
|
36
|
-
/** who is reading: decides "by you" against "by the customer" */
|
|
37
|
-
viewerRole: TTicketViewerRole;
|
|
38
|
-
/** the clock, in ms; a story passes a fixed one */
|
|
39
|
-
now: number;
|
|
40
|
-
/** the one thing the viewer may do right now, rendered beside the sentence */
|
|
41
|
-
action?: IWorkspaceAccessStatusAction;
|
|
42
|
-
/** the rail's size by default */
|
|
43
|
-
size?: TSizes;
|
|
44
|
-
className?: string;
|
|
45
|
-
}
|
|
46
|
-
/** The sentence, its mark and its colour for the state the props describe. */
|
|
47
|
-
export declare const describeWorkspaceAccess: ({ grant, request, policy, viewerRole, now }: Pick<IWorkspaceAccessStatusProps, 'grant' | 'request' | 'policy' | 'viewerRole' | 'now'>) => {
|
|
48
|
-
text: string;
|
|
49
|
-
tooltip?: string;
|
|
50
|
-
intent: TReqoreIntent;
|
|
51
|
-
icon: IReqoreIconName;
|
|
52
|
-
muted?: boolean;
|
|
53
|
-
};
|
|
54
|
-
export declare const WorkspaceAccessStatus: ({ grant, request, policy, viewerRole, now, action, size, className, }: IWorkspaceAccessStatusProps) => import("react/jsx-runtime").JSX.Element;
|
|
55
|
-
//# sourceMappingURL=WorkspaceAccessStatus.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceAccessStatus.d.ts","sourceRoot":"","sources":["../../../src/components/supportTicket/WorkspaceAccessStatus.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,+CAA+C,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,+CAA+C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAQL,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAc3C,mFAAmF;AACnF,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qDAAqD;AACrD,MAAM,WAAW,8BAA8B;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC1C,qEAAqE;IACrE,KAAK,CAAC,EAAE,4BAA4B,CAAC;IACrC,oEAAoE;IACpE,OAAO,CAAC,EAAE,8BAA8B,CAAC;IACzC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAAC;IACvC,iEAAiE;IACjE,UAAU,EAAE,iBAAiB,CAAC;IAC9B,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,MAAM,CAAC,EAAE,4BAA4B,CAAC;IACtC,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,gDACW,KAAK,2BAA2B,EAAE,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,YAAY,GAAG,KAAK,CAAC,KACpI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAqDjG,CAAC;AAEF,eAAO,MAAM,qBAAqB,0EAS/B,2BAA2B,4CA2B7B,CAAC"}
|