@qoretechnologies/reqraft 0.10.13-pr.74.gccefa54 → 0.10.13

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 (125) hide show
  1. package/.claude/CLAUDE.md +8 -11
  2. package/dist/components/form/engine/FormEngine.d.ts +1 -1
  3. package/dist/helpers/common.d.ts +0 -5
  4. package/dist/helpers/common.d.ts.map +1 -1
  5. package/dist/helpers/common.js +1 -20
  6. package/dist/helpers/common.js.map +1 -1
  7. package/dist/index.d.ts +0 -5
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -7
  10. package/dist/index.js.map +1 -1
  11. package/dist/providers/StorageProvider.d.ts +13 -0
  12. package/dist/providers/StorageProvider.d.ts.map +1 -1
  13. package/dist/providers/StorageProvider.js +29 -5
  14. package/dist/providers/StorageProvider.js.map +1 -1
  15. package/package.json +3 -3
  16. package/src/helpers/common.ts +0 -19
  17. package/src/hooks/useStorage/useStorage.stories.tsx +52 -0
  18. package/src/index.tsx +0 -5
  19. package/src/providers/StorageProvider.tsx +30 -4
  20. package/dist/components/composer/Composer.d.ts +0 -108
  21. package/dist/components/composer/Composer.d.ts.map +0 -1
  22. package/dist/components/composer/Composer.js +0 -238
  23. package/dist/components/composer/Composer.js.map +0 -1
  24. package/dist/components/composer/helpers.d.ts +0 -21
  25. package/dist/components/composer/helpers.d.ts.map +0 -1
  26. package/dist/components/composer/helpers.js +0 -75
  27. package/dist/components/composer/helpers.js.map +0 -1
  28. package/dist/components/composer/index.d.ts +0 -3
  29. package/dist/components/composer/index.d.ts.map +0 -1
  30. package/dist/components/composer/index.js +0 -19
  31. package/dist/components/composer/index.js.map +0 -1
  32. package/dist/components/imageLightbox/ImageLightbox.d.ts +0 -17
  33. package/dist/components/imageLightbox/ImageLightbox.d.ts.map +0 -1
  34. package/dist/components/imageLightbox/ImageLightbox.js +0 -129
  35. package/dist/components/imageLightbox/ImageLightbox.js.map +0 -1
  36. package/dist/components/imageLightbox/index.d.ts +0 -2
  37. package/dist/components/imageLightbox/index.d.ts.map +0 -1
  38. package/dist/components/imageLightbox/index.js +0 -18
  39. package/dist/components/imageLightbox/index.js.map +0 -1
  40. package/dist/components/supportTicket/AttachmentChips.d.ts +0 -10
  41. package/dist/components/supportTicket/AttachmentChips.d.ts.map +0 -1
  42. package/dist/components/supportTicket/AttachmentChips.js +0 -119
  43. package/dist/components/supportTicket/AttachmentChips.js.map +0 -1
  44. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +0 -35
  45. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +0 -1
  46. package/dist/components/supportTicket/InterfaceReferenceTags.js +0 -31
  47. package/dist/components/supportTicket/InterfaceReferenceTags.js.map +0 -1
  48. package/dist/components/supportTicket/ReferencePicker.d.ts +0 -41
  49. package/dist/components/supportTicket/ReferencePicker.d.ts.map +0 -1
  50. package/dist/components/supportTicket/ReferencePicker.js +0 -86
  51. package/dist/components/supportTicket/ReferencePicker.js.map +0 -1
  52. package/dist/components/supportTicket/TicketHeader.d.ts +0 -86
  53. package/dist/components/supportTicket/TicketHeader.d.ts.map +0 -1
  54. package/dist/components/supportTicket/TicketHeader.js +0 -149
  55. package/dist/components/supportTicket/TicketHeader.js.map +0 -1
  56. package/dist/components/supportTicket/TicketMetaTags.d.ts +0 -32
  57. package/dist/components/supportTicket/TicketMetaTags.d.ts.map +0 -1
  58. package/dist/components/supportTicket/TicketMetaTags.js +0 -21
  59. package/dist/components/supportTicket/TicketMetaTags.js.map +0 -1
  60. package/dist/components/supportTicket/TicketReplyBox.d.ts +0 -63
  61. package/dist/components/supportTicket/TicketReplyBox.d.ts.map +0 -1
  62. package/dist/components/supportTicket/TicketReplyBox.js +0 -115
  63. package/dist/components/supportTicket/TicketReplyBox.js.map +0 -1
  64. package/dist/components/supportTicket/fileToAttachment.d.ts +0 -9
  65. package/dist/components/supportTicket/fileToAttachment.d.ts.map +0 -1
  66. package/dist/components/supportTicket/fileToAttachment.js +0 -28
  67. package/dist/components/supportTicket/fileToAttachment.js.map +0 -1
  68. package/dist/components/supportTicket/index.d.ts +0 -9
  69. package/dist/components/supportTicket/index.d.ts.map +0 -1
  70. package/dist/components/supportTicket/index.js +0 -25
  71. package/dist/components/supportTicket/index.js.map +0 -1
  72. package/dist/components/supportTicket/meta.d.ts +0 -63
  73. package/dist/components/supportTicket/meta.d.ts.map +0 -1
  74. package/dist/components/supportTicket/meta.js +0 -79
  75. package/dist/components/supportTicket/meta.js.map +0 -1
  76. package/dist/components/ticketReferences/TicketReferences.d.ts +0 -52
  77. package/dist/components/ticketReferences/TicketReferences.d.ts.map +0 -1
  78. package/dist/components/ticketReferences/TicketReferences.js +0 -453
  79. package/dist/components/ticketReferences/TicketReferences.js.map +0 -1
  80. package/dist/components/ticketReferences/index.d.ts +0 -3
  81. package/dist/components/ticketReferences/index.d.ts.map +0 -1
  82. package/dist/components/ticketReferences/index.js +0 -6
  83. package/dist/components/ticketReferences/index.js.map +0 -1
  84. package/dist/components/ticketThread/TicketThread.d.ts +0 -52
  85. package/dist/components/ticketThread/TicketThread.d.ts.map +0 -1
  86. package/dist/components/ticketThread/TicketThread.js +0 -248
  87. package/dist/components/ticketThread/TicketThread.js.map +0 -1
  88. package/dist/components/ticketThread/index.d.ts +0 -3
  89. package/dist/components/ticketThread/index.d.ts.map +0 -1
  90. package/dist/components/ticketThread/index.js +0 -7
  91. package/dist/components/ticketThread/index.js.map +0 -1
  92. package/dist/helpers/scrollbar.d.ts +0 -2
  93. package/dist/helpers/scrollbar.d.ts.map +0 -1
  94. package/dist/helpers/scrollbar.js +0 -25
  95. package/dist/helpers/scrollbar.js.map +0 -1
  96. package/src/components/composer/Composer.stories.tsx +0 -238
  97. package/src/components/composer/Composer.tsx +0 -545
  98. package/src/components/composer/helpers.ts +0 -74
  99. package/src/components/composer/index.ts +0 -2
  100. package/src/components/imageLightbox/ImageLightbox.stories.tsx +0 -156
  101. package/src/components/imageLightbox/ImageLightbox.tsx +0 -169
  102. package/src/components/imageLightbox/index.ts +0 -1
  103. package/src/components/supportTicket/AttachmentChips.stories.tsx +0 -122
  104. package/src/components/supportTicket/AttachmentChips.tsx +0 -142
  105. package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +0 -134
  106. package/src/components/supportTicket/InterfaceReferenceTags.tsx +0 -85
  107. package/src/components/supportTicket/ReferencePicker.stories.tsx +0 -296
  108. package/src/components/supportTicket/ReferencePicker.tsx +0 -237
  109. package/src/components/supportTicket/TicketConversation.stories.tsx +0 -122
  110. package/src/components/supportTicket/TicketHeader.stories.tsx +0 -183
  111. package/src/components/supportTicket/TicketHeader.tsx +0 -247
  112. package/src/components/supportTicket/TicketMetaTags.stories.tsx +0 -103
  113. package/src/components/supportTicket/TicketMetaTags.tsx +0 -85
  114. package/src/components/supportTicket/TicketReplyBox.stories.tsx +0 -355
  115. package/src/components/supportTicket/TicketReplyBox.tsx +0 -146
  116. package/src/components/supportTicket/fileToAttachment.ts +0 -24
  117. package/src/components/supportTicket/index.ts +0 -8
  118. package/src/components/supportTicket/meta.ts +0 -145
  119. package/src/components/ticketReferences/TicketReferences.stories.tsx +0 -301
  120. package/src/components/ticketReferences/TicketReferences.tsx +0 -899
  121. package/src/components/ticketReferences/index.ts +0 -2
  122. package/src/components/ticketThread/TicketThread.stories.tsx +0 -751
  123. package/src/components/ticketThread/TicketThread.tsx +0 -650
  124. package/src/components/ticketThread/index.ts +0 -6
  125. package/src/helpers/scrollbar.ts +0 -32
@@ -1,301 +0,0 @@
1
- import { StoryObj } from '@storybook/react-vite';
2
- import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
3
-
4
- import { StoryMeta } from '../../types';
5
- import { IInterfaceReference } from '../supportTicket/meta';
6
- import { ITicketReferenceAttachment, TicketReferences } from './TicketReferences';
7
-
8
- const NOW = Date.parse('2026-06-30T12:00:00Z');
9
- const ago = (mins: number) => new Date(NOW - mins * 60_000).toISOString();
10
-
11
- // The stories stand in for the authed blob-download endpoint by handing back an
12
- // inline SVG data-URI per image id, so thumbnails + the lightbox render offline.
13
- const svgUrl = (label: string, color: string) =>
14
- `data:image/svg+xml;utf8,${encodeURIComponent(
15
- `<svg xmlns="http://www.w3.org/2000/svg" width="640" height="400">` +
16
- `<rect width="640" height="400" fill="${color}"/>` +
17
- `<text x="320" y="215" font-size="30" fill="#ffffff" text-anchor="middle" ` +
18
- `font-family="sans-serif">${label}</text></svg>`
19
- )}`;
20
-
21
- const REFERENCES: IInterfaceReference[] = [
22
- { interface_kind: 'workflow', interface_name: 'order-sync:1.0', reference_id: 'r1' },
23
- { interface_kind: 'workflow', interface_name: 'invoice-sync:2.3', reference_id: 'r2' },
24
- { interface_kind: 'service', interface_name: 'datasource-pool:2.3', reference_id: 'r3' },
25
- { interface_kind: 'job', interface_name: 'nightly-reconcile', reference_id: 'r4' },
26
- { interface_kind: 'connection', interface_name: 'pg-main-ro', reference_id: 'r5' },
27
- ];
28
-
29
- // Uploaders come already resolved from the endpoint: customer uploads carry the
30
- // account, a staff reply's attachment reads "Qorus Support" on the customer plane.
31
- const ATTACHMENTS: ITicketReferenceAttachment[] = [
32
- { attachment_id: 'img-1', filename: 'error-screenshot.png', content_type: 'image/png', size_bytes: 148_221, uploaded_by: 'acme-prod', uploader_type: 'customer', created: ago(180), message_id: 'm1' },
33
- { attachment_id: 'img-2', filename: 'pool-metrics.png', content_type: 'image/png', size_bytes: 96_540, uploaded_by: 'Qorus Support', uploader_type: 'staff', created: ago(120), message_id: 'm2' },
34
- { attachment_id: 'img-3', filename: 'stack-trace.png', content_type: 'image/png', size_bytes: 210_004, uploaded_by: 'acme-prod', uploader_type: 'customer', created: ago(60), message_id: 'm3' },
35
- { attachment_id: 'file-1', filename: 'nightly-trace.log', content_type: 'text/x-log', size_bytes: 219_136, uploaded_by: 'acme-prod', uploader_type: 'customer', created: ago(180), message_id: 'm1' },
36
- { attachment_id: 'file-2', filename: 'incident-report.pdf', content_type: 'application/pdf', size_bytes: 512_900, uploaded_by: 'Qorus Support', uploader_type: 'staff', created: ago(90), message_id: 'm2' },
37
- { attachment_id: 'file-3', filename: 'pool-config.yaml', content_type: 'application/yaml', size_bytes: 2_048, uploaded_by: 'acme-prod', uploader_type: 'customer', created: ago(90) },
38
- { attachment_id: 'file-4', filename: 'diagnostics.zip', content_type: 'application/zip', size_bytes: 3_400_000, uploaded_by: 'acme-prod', uploader_type: 'customer', created: ago(30), message_id: 'm3' },
39
- ];
40
-
41
- const IMG_URLS: Record<string, string> = {
42
- 'img-1': svgUrl('error-screenshot', '#7b3fe4'),
43
- 'img-2': svgUrl('pool-metrics', '#2872a0'),
44
- 'img-3': svgUrl('stack-trace', '#b23c3c'),
45
- };
46
- const fetchAttachmentUrl = (id: string) => Promise.resolve(IMG_URLS[id] ?? svgUrl(id, '#555555'));
47
-
48
- const meta = {
49
- component: TicketReferences,
50
- title: 'Components/TicketReferences',
51
- parameters: { layout: 'padded' },
52
- args: {
53
- references: REFERENCES,
54
- attachments: ATTACHMENTS,
55
- viewerRole: 'staff',
56
- onDownloadAttachment: fn(),
57
- fetchAttachmentUrl,
58
- },
59
- } satisfies StoryMeta<typeof TicketReferences>;
60
-
61
- export default meta;
62
- type Story = StoryObj<typeof meta>;
63
-
64
- /** A busy ticket: 5 grouped interface references, 3 screenshots, 4 files, and the
65
- * search / type-filter / sort strip a ticket this full earns. */
66
- export const Populated: Story = {
67
- parameters: {
68
- docs: {
69
- description: {
70
- story:
71
- 'Renders a busy ticket with five grouped interface references, three screenshots, and four files — full enough to surface the search box, type-filter, and sort strip.',
72
- },
73
- },
74
- },
75
- play: async ({ canvasElement }) => {
76
- const canvas = within(canvasElement);
77
- await expect(canvas.getByText('Linked interfaces')).toBeInTheDocument();
78
- await expect(canvas.getByText('Screenshots')).toBeInTheDocument();
79
- await expect(canvas.getByText('Files')).toBeInTheDocument();
80
- // references render as rows carrying their kind as metadata
81
- await expect(canvasElement.textContent).toContain('workflow');
82
- await expect(canvasElement.textContent).toContain('order-sync:1.0');
83
- // a file row with provenance (staff view shows uploaders as stored)
84
- await expect(canvas.getByText('incident-report.pdf')).toBeInTheDocument();
85
- await expect(canvasElement.textContent).toContain('added by acme-prod');
86
- // the control strip: search box + the type-filter and sort dropdowns
87
- await expect(
88
- canvas.getByPlaceholderText('Search files and references…')
89
- ).toBeInTheDocument();
90
- await expect(canvas.getByText('All types')).toBeInTheDocument();
91
- await expect(canvas.getByText('Newest first')).toBeInTheDocument();
92
- // thumbnails resolve to images
93
- await waitFor(() =>
94
- expect(
95
- canvasElement.querySelector('button[title="error-screenshot.png"] img')
96
- ).not.toBeNull()
97
- );
98
- },
99
- };
100
-
101
- /** The control strip in action: search narrows to matches, a no-match query offers
102
- * "Clear filters", and the type filter collapses the view to one family. */
103
- export const Filtering: Story = {
104
- parameters: {
105
- docs: {
106
- description: {
107
- story:
108
- "Renders the control strip in action — searching narrows to matching files and references, a no-match query offers 'Clear filters', and the type filter collapses the view to a single family.",
109
- },
110
- },
111
- },
112
- play: async ({ canvasElement }) => {
113
- const canvas = within(canvasElement);
114
- const box = canvas.getByPlaceholderText('Search files and references…');
115
-
116
- // search narrows across both files and screenshots by filename
117
- await userEvent.type(box, 'pool');
118
- await waitFor(() => {
119
- // "pool-*" survive, "error-screenshot"/"nightly-trace" fall away
120
- expect(canvas.getByText('pool-config.yaml')).toBeInTheDocument();
121
- expect(canvasElement.querySelector('button[title="pool-metrics.png"]')).not.toBeNull();
122
- expect(canvasElement.querySelector('button[title="error-screenshot.png"]')).toBeNull();
123
- expect(canvas.queryByText('nightly-trace.log')).toBeNull();
124
- });
125
-
126
- // a query that matches nothing surfaces the empty state + a clear affordance
127
- await userEvent.clear(box);
128
- await userEvent.type(box, 'zzz-no-such-file');
129
- await waitFor(() =>
130
- expect(canvasElement.textContent).toContain('No matching references or files')
131
- );
132
- await userEvent.click(canvas.getByText('Clear filters'));
133
- await waitFor(() => expect(canvas.getByText('Files')).toBeInTheDocument());
134
-
135
- // the type filter collapses to a single family (Screenshots only)
136
- await userEvent.click(canvas.getByText('All types'));
137
- await userEvent.click(await within(document.body).findByText(/^Screenshots \(3\)$/));
138
- await waitFor(() => {
139
- expect(canvas.queryByText('Files')).toBeNull();
140
- expect(canvas.queryByText('Linked interfaces')).toBeNull();
141
- expect(canvasElement.querySelector('button[title="stack-trace.png"]')).not.toBeNull();
142
- });
143
- },
144
- };
145
-
146
- /** Clicking a loaded screenshot opens the paging lightbox (portalled to body). */
147
- export const LightboxOpens: Story = {
148
- parameters: {
149
- docs: {
150
- description: {
151
- story:
152
- 'Renders the references view and, when a loaded screenshot is clicked, opens the paging lightbox portalled to the body.',
153
- },
154
- },
155
- },
156
- play: async ({ canvasElement }) => {
157
- const thumb = await waitFor(() => {
158
- const el = canvasElement.querySelector<HTMLButtonElement>(
159
- 'button[title="error-screenshot.png"]'
160
- );
161
- if (!el || !el.querySelector('img')) {
162
- throw new Error('thumbnail not ready');
163
- }
164
- return el;
165
- });
166
- await userEvent.click(thumb);
167
- // error-screenshot is the oldest of the three, so under the default
168
- // newest-first sort it sits last — the counter reads "3 / 3".
169
- await waitFor(() => expect(document.body.textContent).toContain('3 / 3'));
170
- },
171
- };
172
-
173
- /** The same ticket from the customer's side: their own uploads read "you", and a
174
- * staff reply's attachment reads "Qorus Support" — never the raw staff identity. */
175
- export const CustomerView: Story = {
176
- parameters: {
177
- docs: {
178
- description: {
179
- story:
180
- "Renders the same ticket from the customer's side, where their own uploads read 'added by you' and a staff reply's attachment reads 'Qorus Support' rather than the raw staff identity.",
181
- },
182
- },
183
- },
184
- args: { viewerRole: 'customer' },
185
- play: async ({ canvasElement }) => {
186
- await waitFor(() => expect(canvasElement.textContent).toContain('added by you'));
187
- await expect(canvasElement.textContent).toContain('added by Qorus Support');
188
- },
189
- };
190
-
191
- /** Without a fetcher, screenshots degrade to download tiles rather than thumbnails. */
192
- export const NoPreview: Story = {
193
- parameters: {
194
- docs: {
195
- description: {
196
- story:
197
- 'Renders the references view with no fetchAttachmentUrl, so screenshots degrade to download tiles rather than thumbnails.',
198
- },
199
- },
200
- },
201
- args: { fetchAttachmentUrl: undefined },
202
- };
203
-
204
- /** Nothing attached or linked. */
205
- export const Empty: Story = {
206
- parameters: {
207
- docs: {
208
- description: {
209
- story:
210
- "Renders the empty state with nothing attached or linked, showing 'No references or files yet'.",
211
- },
212
- },
213
- },
214
- args: { references: [], attachments: [], messages: [] },
215
- play: async ({ canvasElement }) => {
216
- await expect(canvasElement.textContent).toContain('No references or files yet');
217
- },
218
- };
219
-
220
- /**
221
- * With `onShowInChat` wired, a row that came from a reply gains a "three dots" menu
222
- * offering to reveal it in the conversation.
223
- *
224
- * A row filed against the ticket itself carries the same menu, but its reveal is
225
- * disabled and labelled "Filed on the ticket" — there's no message to jump to. That
226
- * distinction between a reply-borne row and a ticket-level one is the thing to see here.
227
- */
228
- export const ShowInChat: Story = {
229
- parameters: {
230
- docs: {
231
- description: {
232
- story:
233
- "Renders references with onShowInChat wired — a reply-borne row's menu offers an active 'Show in chat' that calls onShowInChat, while a ticket-level row carries the same menu with its reveal disabled ('Filed on the ticket').",
234
- },
235
- },
236
- },
237
- args: {
238
- messages: [
239
- {
240
- message_id: 'm-42',
241
- author_type: 'customer',
242
- author_id: 'acme-prod',
243
- body: 'Same failure on the reconcile job.',
244
- created: ago(30),
245
- referenced_interfaces: [{ interface_kind: 'job', interface_name: 'nightly-reconcile' }],
246
- },
247
- ],
248
- onShowInChat: fn(),
249
- },
250
- play: async ({ args, canvasElement }) => {
251
- const canvas = within(canvasElement);
252
- await expect(canvas.getByText('nightly-reconcile')).toBeInTheDocument();
253
-
254
- const rowFor = (label: string) =>
255
- Array.from(canvasElement.querySelectorAll<HTMLElement>('.reqore-entity-row')).find((row) =>
256
- row.textContent?.includes(label)
257
- );
258
-
259
- // the reply-borne reference offers the menu...
260
- const fromReply = rowFor('nightly-reconcile');
261
- const menuButton = fromReply?.querySelector<HTMLElement>(
262
- '.reqore-entity-row-actions .reqore-button:last-child'
263
- );
264
- await userEvent.click(menuButton!);
265
- const item = await waitFor(() => {
266
- const found = Array.from(document.querySelectorAll<HTMLElement>('.reqore-menu-item')).find(
267
- (el) => /Show in chat/.test(el.textContent || '')
268
- );
269
- if (!found) {
270
- throw new Error('Show in chat not offered');
271
- }
272
- return found;
273
- });
274
- await userEvent.click(item);
275
- await expect(args.onShowInChat).toHaveBeenCalledWith('m-42');
276
-
277
- // ...and a ticket-level reference carries the SAME menu, so the rows don't look
278
- // half-broken — but the reveal is disabled there, because it was filed on the
279
- // ticket and has no message to jump to.
280
- const ticketLevel = rowFor('order-sync:1.0');
281
- const ticketMenu = ticketLevel?.querySelector<HTMLElement>(
282
- '.reqore-entity-row-actions .reqore-button:last-child'
283
- );
284
- await expect(ticketMenu).toBeTruthy();
285
- await userEvent.click(ticketMenu!);
286
- const ticketEntry = await waitFor(() => {
287
- const entry = Array.from(document.querySelectorAll<HTMLElement>('.reqore-menu-item')).find(
288
- (el) => /Show in chat/.test(el.textContent || '')
289
- );
290
- if (!entry) {
291
- throw new Error('the menu should still offer the entry, disabled');
292
- }
293
- return entry;
294
- });
295
- // it is genuinely unclickable, not merely styled as such — and the handler stays
296
- // on its single call from the reply-borne row above
297
- await expect(getComputedStyle(ticketEntry).pointerEvents).toBe('none');
298
- await expect(ticketEntry.textContent).toContain('Filed on the ticket');
299
- await expect(args.onShowInChat).toHaveBeenCalledTimes(1);
300
- },
301
- };