@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.
Files changed (70) hide show
  1. package/.claude/CLAUDE.md +0 -1
  2. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +1 -13
  3. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -1
  4. package/dist/components/supportTicket/InterfaceReferenceTags.js +4 -11
  5. package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -1
  6. package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -1
  7. package/dist/components/supportTicket/ReferencePicker.js +1 -1
  8. package/dist/components/supportTicket/ReferencePicker.js.map +1 -1
  9. package/dist/components/supportTicket/index.d.ts +0 -2
  10. package/dist/components/supportTicket/index.d.ts.map +1 -1
  11. package/dist/components/supportTicket/index.js +0 -2
  12. package/dist/components/supportTicket/index.js.map +1 -1
  13. package/dist/components/supportTicket/meta.d.ts +2 -62
  14. package/dist/components/supportTicket/meta.d.ts.map +1 -1
  15. package/dist/components/supportTicket/meta.js +3 -116
  16. package/dist/components/supportTicket/meta.js.map +1 -1
  17. package/dist/components/supportTicket/ticketModel.d.ts +0 -10
  18. package/dist/components/supportTicket/ticketModel.d.ts.map +1 -1
  19. package/dist/components/ticketReferences/TicketReferences.d.ts +1 -10
  20. package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -1
  21. package/dist/components/ticketReferences/TicketReferences.js +8 -12
  22. package/dist/components/ticketReferences/TicketReferences.js.map +1 -1
  23. package/dist/components/ticketThread/TicketThread.d.ts +1 -30
  24. package/dist/components/ticketThread/TicketThread.d.ts.map +1 -1
  25. package/dist/components/ticketThread/TicketThread.js +13 -51
  26. package/dist/components/ticketThread/TicketThread.js.map +1 -1
  27. package/dist/components/ticketThread/index.d.ts +1 -1
  28. package/dist/components/ticketThread/index.d.ts.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/providers/StorageProvider.d.ts +0 -11
  33. package/dist/providers/StorageProvider.d.ts.map +1 -1
  34. package/dist/providers/StorageProvider.js +3 -21
  35. package/dist/providers/StorageProvider.js.map +1 -1
  36. package/dist/stores/currentUser/currentUser.d.ts +0 -42
  37. package/dist/stores/currentUser/currentUser.d.ts.map +1 -1
  38. package/dist/stores/currentUser/currentUser.js.map +1 -1
  39. package/package.json +9 -9
  40. package/src/components/form/engine/FormEngine.stories.tsx +5 -8
  41. package/src/components/form/fields/array/ArrayAutoField.stories.tsx +3 -6
  42. package/src/components/form/fields/boolean/Boolean.stories.tsx +3 -7
  43. package/src/components/form/fields/markdown/Markdown.stories.tsx +0 -6
  44. package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +1 -4
  45. package/src/components/form/fields/template/TemplateField.stories.tsx +0 -4
  46. package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +0 -56
  47. package/src/components/supportTicket/InterfaceReferenceTags.tsx +7 -27
  48. package/src/components/supportTicket/ReferencePicker.tsx +1 -4
  49. package/src/components/supportTicket/index.ts +0 -2
  50. package/src/components/supportTicket/meta.ts +4 -156
  51. package/src/components/supportTicket/ticketModel.ts +0 -10
  52. package/src/components/ticketReferences/TicketReferences.stories.tsx +0 -40
  53. package/src/components/ticketReferences/TicketReferences.tsx +10 -24
  54. package/src/components/ticketThread/TicketThread.stories.tsx +1 -103
  55. package/src/components/ticketThread/TicketThread.tsx +15 -131
  56. package/src/components/ticketThread/index.ts +0 -1
  57. package/src/index.tsx +0 -1
  58. package/src/providers/StorageProvider.tsx +3 -20
  59. package/src/stores/currentUser/currentUser.tsx +0 -43
  60. package/dist/components/supportTicket/WorkspaceAccessStatus.d.ts +0 -55
  61. package/dist/components/supportTicket/WorkspaceAccessStatus.d.ts.map +0 -1
  62. package/dist/components/supportTicket/WorkspaceAccessStatus.js +0 -79
  63. package/dist/components/supportTicket/WorkspaceAccessStatus.js.map +0 -1
  64. package/dist/components/supportTicket/accessMeta.d.ts +0 -50
  65. package/dist/components/supportTicket/accessMeta.d.ts.map +0 -1
  66. package/dist/components/supportTicket/accessMeta.js +0 -174
  67. package/dist/components/supportTicket/accessMeta.js.map +0 -1
  68. package/src/components/supportTicket/WorkspaceAccessStatus.stories.tsx +0 -105
  69. package/src/components/supportTicket/WorkspaceAccessStatus.tsx +0 -176
  70. 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
- // identity by normalized kind + name, so a `qog` cited in a reply and the `fsm` the
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={reference.reference_id ?? interfaceReferenceKey(reference)}
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
- {reason ? `${reference.interface_kind} · ${reason}` : reference.interface_kind}
805
+ {reference.interface_kind}
822
806
  </ReqoreTextEffect>
823
807
  }
824
808
  flat
825
809
  tooltip={
826
- linkable ? `Open ${reference.interface_kind} ${reference.interface_name}` : undefined
810
+ onInterfaceClick ?
811
+ `Open ${reference.interface_kind} ${reference.interface_name}`
812
+ : undefined
827
813
  }
828
814
  actions={rowActions(
829
- linkable ?
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 { ITicketSystemEventAction, ITicketThreadMessage, TicketThread } from './TicketThread';
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
- return dedupeInterfaceReferences([...ticketReferences, ...(opening?.referenced_interfaces ?? [])]);
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
- <SystemMessage
648
+ <StyledSystemRow
765
649
  key={segment.message.message_id}
766
- message={segment.message}
767
- action={systemEventAction?.(segment.message)}
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}
@@ -1,6 +1,5 @@
1
1
  export { TicketThread, ticketMessageDomId } from './TicketThread';
2
2
  export type {
3
- ITicketSystemEventAction,
4
3
  ITicketThreadAttachment,
5
4
  ITicketThreadMessage,
6
5
  ITicketThreadProps,
package/src/index.tsx CHANGED
@@ -36,7 +36,6 @@ export { currentUserStore as ReqraftCurrentUserStore } from './stores/currentUse
36
36
  export type {
37
37
  ICurrentUser,
38
38
  ICurrentUserStore,
39
- ICurrentUserSupportSession,
40
39
  } from './stores/currentUser/currentUser';
41
40
  export {
42
41
  fetchConfig,
@@ -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, ICurrentUser } from '../stores/currentUser/currentUser';
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
- // in memory only for a user whose storage is not theirs to keep
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
- if (storageIsWritable(currentUserStore.getState().currentUser)) {
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"}