@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"currentUser.js","sourceRoot":"","sources":["../../../src/stores/currentUser/currentUser.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,mCAAiC;AACjC,2CAAmF;
|
|
1
|
+
{"version":3,"file":"currentUser.js","sourceRoot":"","sources":["../../../src/stores/currentUser/currentUser.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,mCAAiC;AACjC,2CAAmF;AAkCtE,QAAA,gBAAgB,GAAG,IAAA,gBAAM,EAAoB,UAAC,GAAG,EAAE,GAAG,IAAK,OAAA,CAAC;IACvE,WAAW,EAAE,SAAS;IACtB,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE;;;;;oBACJ,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;;;;oBAKV,qBAAM,IAAA,aAAK,EAAe,EAAE,GAAG,EAAE,sBAAsB,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAA;;oBAAnF,QAAQ,GAAG,SAAwE,CAAC;;;;oBAEpF;;;;gFAI4D;oBAC5D,IAAI,IAAA,wBAAgB,EAAC,OAAK,CAAC,EAAE,CAAC;wBAC5B,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;oBAC1B,CAAC;yBAAM,CAAC;wBACN,GAAG,CAAC;4BACF,OAAO,EAAE,KAAK;4BACd,KAAK,EAAE,OAAc;4BACrB,WAAW,EAAE,SAAS;4BACtB,SAAS,EAAE,SAAS;yBACrB,CAAC,CAAC;oBACL,CAAC;oBAED,sBAAO,OAAO,CAAC,MAAM,CAAC,OAAK,CAAC,EAAC;;oBAG/B,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;wBACjB,GAAG,CAAC;4BACF,OAAO,EAAE,KAAK;4BACd,KAAK,EAAE,QAAQ,CAAC,KAAK;4BACrB,WAAW,EAAE,SAAS;4BACtB,SAAS,EAAE,QAAQ,CAAC,IAAI;yBACzB,CAAC,CAAC;wBAEH,sBAAO,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAC;oBACxC,CAAC;oBAED,GAAG,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;oBAE5F,sBAAO,QAAQ,CAAC,IAAI,EAAC;;;SACtB;IACD,gBAAgB,EAAE,UAAC,WAAW;QAC5B,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACf,CAAC;QAEO,IAAA,WAAW,GAAK,GAAG,EAAE,YAAV,CAAW;QAE9B,OAAO,WAAW,CAAC,IAAI,CAAC,UAAC,UAAU,YAAK,OAAA,MAAA,WAAW,CAAC,WAAW,0CAAE,QAAQ,CAAC,UAAU,CAAC,CAAA,EAAA,CAAC,CAAC;IACzF,CAAC;IACD,aAAa,EAAE,UAAC,OAAO;QACrB,GAAG,CAAC,UAAC,KAAK,IAAK,OAAA,CAAC,EAAE,WAAW,wBAAO,KAAK,CAAC,WAAW,KAAE,OAAO,SAAA,GAAE,EAAE,CAAC,EAApD,CAAoD,CAAC,CAAC;IACvE,CAAC;CACF,CAAC,EA1DsE,CA0DtE,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qoretechnologies/reqraft",
|
|
3
|
-
"version": "0.10.65
|
|
3
|
+
"version": "0.10.65",
|
|
4
4
|
"description": "ReQraft is a collection of React components and hooks that are used across Qore Technologies' products made using the ReQore component library from Qore.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"@chromatic-com/storybook": "^5.2.1",
|
|
58
58
|
"@netsells/storybook-mockdate": "^0.3.3",
|
|
59
59
|
"@qoretechnologies/qlip": "^0.1.3-beta.20260929120101",
|
|
60
|
-
"@qoretechnologies/reqore": "^0.
|
|
61
|
-
"@storybook/addon-docs": "^10.
|
|
62
|
-
"@storybook/addon-links": "^10.
|
|
63
|
-
"@storybook/addon-vitest": "^10.
|
|
64
|
-
"@storybook/react": "^10.
|
|
65
|
-
"@storybook/react-vite": "^10.
|
|
60
|
+
"@qoretechnologies/reqore": "^0.77.2",
|
|
61
|
+
"@storybook/addon-docs": "^10.6.3",
|
|
62
|
+
"@storybook/addon-links": "^10.6.3",
|
|
63
|
+
"@storybook/addon-vitest": "^10.6.3",
|
|
64
|
+
"@storybook/react": "^10.6.3",
|
|
65
|
+
"@storybook/react-vite": "^10.6.3",
|
|
66
66
|
"@testing-library/jest-dom": "^5.16.5",
|
|
67
67
|
"@testing-library/react": "^13.4.0",
|
|
68
68
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"chromatic": "^11.3.0",
|
|
81
81
|
"eslint": "^8.46.0",
|
|
82
82
|
"eslint-plugin-react": "^7.33.1",
|
|
83
|
-
"eslint-plugin-storybook": "10.
|
|
83
|
+
"eslint-plugin-storybook": "10.6.3",
|
|
84
84
|
"jsdom": "^29.1.1",
|
|
85
85
|
"mock-socket": "^9.3.1",
|
|
86
86
|
"playwright": "^1.60.0",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"react-dom": "^18.3.1",
|
|
90
90
|
"react-intersection-observer": "^9.4.2",
|
|
91
91
|
"react-test-renderer": "^18.2.0",
|
|
92
|
-
"storybook": "^10.
|
|
92
|
+
"storybook": "^10.6.3",
|
|
93
93
|
"storybook-addon-mock": "^6.0.1",
|
|
94
94
|
"ts-node": "^9.1.1",
|
|
95
95
|
"tsc-watch": "^7.2.0",
|
|
@@ -7264,11 +7264,9 @@ export const CompactAutoFocusNonFocusableFirstField: Story = {
|
|
|
7264
7264
|
},
|
|
7265
7265
|
{ timeout: 10000 }
|
|
7266
7266
|
);
|
|
7267
|
-
// On the control itself, which is not a text box: that is the whole case.
|
|
7268
|
-
// is a native checkbox input.
|
|
7267
|
+
// On the control itself, which is not a text box: that is the whole case.
|
|
7269
7268
|
const active = document.activeElement as HTMLElement;
|
|
7270
|
-
expect(
|
|
7271
|
-
expect((active as HTMLInputElement).type).toBe('checkbox');
|
|
7269
|
+
expect(['INPUT', 'TEXTAREA']).not.toContain(active.tagName);
|
|
7272
7270
|
// It did not settle for the focusable text field further down instead.
|
|
7273
7271
|
expect(document.querySelector('[data-field="name"]')?.contains(active)).toBe(false);
|
|
7274
7272
|
},
|
|
@@ -7332,10 +7330,9 @@ export const CompactAutoFocusPickOneFirstQuestion: Story = {
|
|
|
7332
7330
|
{ timeout: 10000 }
|
|
7333
7331
|
);
|
|
7334
7332
|
const active = document.activeElement as HTMLElement;
|
|
7335
|
-
// The control the row actually offers: a choice, not a text box
|
|
7336
|
-
|
|
7337
|
-
expect(active.
|
|
7338
|
-
expect((active as HTMLInputElement).type).toBe('checkbox');
|
|
7333
|
+
// The control the row actually offers: a choice, not a text box.
|
|
7334
|
+
expect(['INPUT', 'TEXTAREA']).not.toContain(active.tagName);
|
|
7335
|
+
expect(active.getAttribute('tabindex')).toBe('0');
|
|
7339
7336
|
},
|
|
7340
7337
|
};
|
|
7341
7338
|
|
|
@@ -147,15 +147,12 @@ export const CompactEmpty: Story = {
|
|
|
147
147
|
const canvas = within(canvasElement);
|
|
148
148
|
|
|
149
149
|
// Should show the input field but no tags
|
|
150
|
+
await expect(canvas.queryByRole('button', { name: /add item/i })).not.toBeInTheDocument();
|
|
150
151
|
await expect(document.querySelectorAll('.array-auto-compact-tag').length).toBe(0);
|
|
151
152
|
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
const confirmBtn = canvasElement.querySelector<HTMLButtonElement>(
|
|
155
|
-
'.array-auto-compact-confirm'
|
|
156
|
-
);
|
|
153
|
+
// Confirm button should be disabled when input is empty
|
|
154
|
+
const confirmBtn = document.querySelector('.array-auto-compact-confirm') as HTMLButtonElement;
|
|
157
155
|
await expect(confirmBtn).toBeInTheDocument();
|
|
158
|
-
await expect(canvas.getAllByRole('button', { name: /add item/i })).toEqual([confirmBtn]);
|
|
159
156
|
await expect(confirmBtn).toBeDisabled();
|
|
160
157
|
},
|
|
161
158
|
};
|
|
@@ -5,10 +5,6 @@ import { useState } from 'react';
|
|
|
5
5
|
import { StoryMeta } from '../../../../types';
|
|
6
6
|
import { BooleanFormField } from './Boolean';
|
|
7
7
|
|
|
8
|
-
/** The switch as a user clicks it: since reqore 0.78 it is a native input under its drawn track. */
|
|
9
|
-
const switchTrack = (input: HTMLElement): HTMLElement =>
|
|
10
|
-
(input.closest('.reqore-checkbox') as HTMLElement | null) ?? input.parentElement!;
|
|
11
|
-
|
|
12
8
|
const meta = {
|
|
13
9
|
component: BooleanFormField,
|
|
14
10
|
title: 'Components/Form/Boolean',
|
|
@@ -52,7 +48,7 @@ export const Checked: Story = {
|
|
|
52
48
|
await expect(canvas.getByText('Yes')).toBeInTheDocument();
|
|
53
49
|
await expect(canvas.getByText('No')).toBeInTheDocument();
|
|
54
50
|
|
|
55
|
-
await userEvent.click(
|
|
51
|
+
await userEvent.click(canvas.getByLabelText('Boolean'));
|
|
56
52
|
await expect(args.onChange).toHaveBeenLastCalledWith(false);
|
|
57
53
|
},
|
|
58
54
|
};
|
|
@@ -74,7 +70,7 @@ export const Unchecked: Story = {
|
|
|
74
70
|
|
|
75
71
|
await expect(canvas.getByText('No')).toBeInTheDocument();
|
|
76
72
|
|
|
77
|
-
await userEvent.click(
|
|
73
|
+
await userEvent.click(canvas.getByLabelText('Boolean'));
|
|
78
74
|
await expect(args.onChange).toHaveBeenLastCalledWith(true);
|
|
79
75
|
},
|
|
80
76
|
};
|
|
@@ -108,7 +104,7 @@ export const UnsetSelectsYes: Story = {
|
|
|
108
104
|
async play({ canvasElement, args }) {
|
|
109
105
|
const canvas = within(canvasElement);
|
|
110
106
|
|
|
111
|
-
await userEvent.click(
|
|
107
|
+
await userEvent.click(canvas.getByLabelText('Boolean'));
|
|
112
108
|
await expect(args.onChange).toHaveBeenLastCalledWith(true);
|
|
113
109
|
},
|
|
114
110
|
};
|
|
@@ -35,13 +35,7 @@ export const Default: Story = {
|
|
|
35
35
|
args: {
|
|
36
36
|
'aria-label': 'MarkdownEditor',
|
|
37
37
|
},
|
|
38
|
-
// A desktop: the preview is drawn beside the editor only above reqore's tablet width (1200px), and the
|
|
39
|
-
// story run's default viewport is 1200px wide. Reqore answers media queries in a story run since 0.78.
|
|
40
|
-
globals: { viewport: { value: 'desktop1440' } },
|
|
41
38
|
parameters: {
|
|
42
|
-
viewport: {
|
|
43
|
-
options: { desktop1440: { name: 'Desktop', styles: { width: '1440px', height: '900px' } } },
|
|
44
|
-
},
|
|
45
39
|
docs: {
|
|
46
40
|
description: {
|
|
47
41
|
story:
|
|
@@ -53,10 +53,7 @@ export const Default: Story = {
|
|
|
53
53
|
async play({ canvasElement, args }) {
|
|
54
54
|
const canvas = within(canvasElement);
|
|
55
55
|
const java = canvas.getByLabelText('Java');
|
|
56
|
-
|
|
57
|
-
await userEvent.click(
|
|
58
|
-
(java.closest('.reqore-checkbox') as HTMLElement | null) ?? java.parentElement!
|
|
59
|
-
);
|
|
56
|
+
await userEvent.click(java);
|
|
60
57
|
await expect(args.onChange).toHaveBeenLastCalledWith('Java');
|
|
61
58
|
},
|
|
62
59
|
};
|
|
@@ -903,10 +903,6 @@ const longExampleTemplates = {
|
|
|
903
903
|
};
|
|
904
904
|
|
|
905
905
|
export const LongExampleValueTruncatedWithModal: StoryObj<typeof meta> = {
|
|
906
|
-
/* A field in a narrow column, as a form's field is: the picker is as wide as the field (since reqore 0.78 it
|
|
907
|
-
follows the field's width as it is laid out), and the preview is cut at that width. Across a whole
|
|
908
|
-
desktop page the field is wider than the preview itself, and nothing has to be cut. */
|
|
909
|
-
decorators: [(Story) => <div style={{ width: 480 }}>{Story()}</div>],
|
|
910
906
|
args: {
|
|
911
907
|
component: LongStringField,
|
|
912
908
|
value: 'Some string',
|
|
@@ -255,59 +255,3 @@ export const KindImageFailsToLoad: Story = {
|
|
|
255
255
|
await expect(canvasElement.querySelector('.reqore-tag .reqore-icon')).toBeTruthy();
|
|
256
256
|
},
|
|
257
257
|
};
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* A viewer that can open SOME references: the IDE under a support grant, or any viewer
|
|
261
|
-
* after an interface moved group or was deleted since it was cited. `isInterfaceLinkable`
|
|
262
|
-
* keeps the refused chips in place — they are still what was cited — but without the
|
|
263
|
-
* click, with an unlink glyph, and with `unlinkableReason` in the tooltip.
|
|
264
|
-
*/
|
|
265
|
-
export const MixedLinkability: Story = {
|
|
266
|
-
parameters: {
|
|
267
|
-
docs: {
|
|
268
|
-
description: {
|
|
269
|
-
story:
|
|
270
|
-
'Renders four chips of which two can be opened: the refused ones keep their place and look but carry an unlink glyph and no click, with the reason in their tooltip (qorus#693).',
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
args: {
|
|
275
|
-
onInterfaceClick: fn(),
|
|
276
|
-
isInterfaceLinkable: (reference: IInterfaceReference) => reference.interface_kind !== 'workflow',
|
|
277
|
-
unlinkableReason: (reference: IInterfaceReference) =>
|
|
278
|
-
reference.interface_kind === 'workflow' ? 'not covered by the support grant' : undefined,
|
|
279
|
-
},
|
|
280
|
-
async play({ canvasElement, args }) {
|
|
281
|
-
const canvas = within(canvasElement);
|
|
282
|
-
await userEvent.click(canvas.getByText('order-sync:1.2'));
|
|
283
|
-
await expect(args.onInterfaceClick).not.toHaveBeenCalled();
|
|
284
|
-
await userEvent.click(canvas.getByText('notification-svc'));
|
|
285
|
-
await waitFor(() =>
|
|
286
|
-
expect(args.onInterfaceClick).toHaveBeenCalledWith(
|
|
287
|
-
expect.objectContaining({ interface_kind: 'service', interface_name: 'notification-svc' })
|
|
288
|
-
)
|
|
289
|
-
);
|
|
290
|
-
await expect(args.onInterfaceClick).toHaveBeenCalledTimes(1);
|
|
291
|
-
},
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
/** The same mixed chips at phone width: the glyph is what tells the two apart on a
|
|
295
|
-
* screen with no hover, so it has to survive the wrap. */
|
|
296
|
-
export const MixedLinkabilityMobile: Story = {
|
|
297
|
-
parameters: {
|
|
298
|
-
qlip: { viewport: { width: 390, height: 844 } },
|
|
299
|
-
docs: {
|
|
300
|
-
description: {
|
|
301
|
-
story:
|
|
302
|
-
'Renders the mixed linkable and unlinkable chips at 390px: they wrap, and the refused ones still carry the unlink glyph, which is the only cue on a screen without hover.',
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
args: MixedLinkability.args,
|
|
307
|
-
async play({ canvasElement }) {
|
|
308
|
-
const canvas = within(canvasElement);
|
|
309
|
-
await expect(canvas.getByText('order-sync:1.2')).toBeInTheDocument();
|
|
310
|
-
await expect(canvas.getByText('nightly-cleanup')).toBeInTheDocument();
|
|
311
|
-
},
|
|
312
|
-
};
|
|
313
|
-
|
|
@@ -4,7 +4,6 @@ import { ComponentProps } from 'react';
|
|
|
4
4
|
import {
|
|
5
5
|
defaultInterfaceIcon,
|
|
6
6
|
IInterfaceReference,
|
|
7
|
-
interfaceReferenceKey,
|
|
8
7
|
toInterfaceKindMark,
|
|
9
8
|
TResolveInterfaceIcon,
|
|
10
9
|
} from './meta';
|
|
@@ -23,18 +22,6 @@ export interface IInterfaceReferenceTagsProps {
|
|
|
23
22
|
/** Open a referenced interface; chips are static (informational) when omitted —
|
|
24
23
|
* e.g. the staff view, which can't reach the customer's instance. */
|
|
25
24
|
onInterfaceClick?: (reference: IInterfaceReference) => void;
|
|
26
|
-
/**
|
|
27
|
-
* Whether a reference can be opened right now; consulted only when `onInterfaceClick`
|
|
28
|
-
* is given, and every reference is linkable when it is omitted. A chip it refuses
|
|
29
|
-
* renders without the click and with an unlink glyph, and its tooltip carries
|
|
30
|
-
* `unlinkableReason` when there is one. A support reader can open only what the
|
|
31
|
-
* customer's grant covers, and an interface that moved group or was deleted since it
|
|
32
|
-
* was cited is a reference with nowhere to go — the chip still shows what was cited.
|
|
33
|
-
*/
|
|
34
|
-
isInterfaceLinkable?: (reference: IInterfaceReference) => boolean;
|
|
35
|
-
/** Why a reference is not linkable, for the chip's tooltip ("not covered by the
|
|
36
|
-
* grant", "no longer on the instance"). Consulted only for refused references. */
|
|
37
|
-
unlinkableReason?: (reference: IInterfaceReference) => string | undefined;
|
|
38
25
|
/**
|
|
39
26
|
* Detach a reference. Each chip grows an "×" when given — for the composer,
|
|
40
27
|
* where the chips are the references you're about to send and so are still
|
|
@@ -81,8 +68,6 @@ export const InterfaceReferenceTags = ({
|
|
|
81
68
|
label,
|
|
82
69
|
resolveInterfaceIcon,
|
|
83
70
|
onInterfaceClick,
|
|
84
|
-
isInterfaceLinkable,
|
|
85
|
-
unlinkableReason,
|
|
86
71
|
onRemove,
|
|
87
72
|
size = 'small',
|
|
88
73
|
intent,
|
|
@@ -115,30 +100,25 @@ export const InterfaceReferenceTags = ({
|
|
|
115
100
|
// The chosen image (see `preferKindImage`) once it has loaded; the kind's
|
|
116
101
|
// font icon until then, and if it fails.
|
|
117
102
|
const loaded = isLoaded(image);
|
|
118
|
-
// Linkable: a click handler is wired AND the consumer does not refuse this one.
|
|
119
|
-
// A refused chip keeps its look and says why, instead of opening nothing.
|
|
120
|
-
const linkable = !!onInterfaceClick && (isInterfaceLinkable?.(reference) ?? true);
|
|
121
|
-
const reason = onInterfaceClick && !linkable ? unlinkableReason?.(reference) : undefined;
|
|
122
|
-
const described = `${reference.interface_kind}: ${reference.interface_name}`;
|
|
123
103
|
return (
|
|
124
104
|
<ReqoreTag
|
|
125
|
-
key={
|
|
105
|
+
key={
|
|
106
|
+
reference.reference_id ??
|
|
107
|
+
`${reference.interface_kind}:${reference.interface_name}`
|
|
108
|
+
}
|
|
126
109
|
icon={icon}
|
|
127
110
|
leftIconProps={loaded ? { image } : undefined}
|
|
128
|
-
rightIcon={onInterfaceClick && !linkable ? 'LinkUnlinkM' : undefined}
|
|
129
111
|
labelKey={reference.interface_kind}
|
|
130
112
|
label={reference.interface_name}
|
|
131
113
|
size={size}
|
|
132
114
|
intent={intent}
|
|
133
115
|
customTheme={customTheme}
|
|
134
116
|
tooltip={
|
|
135
|
-
|
|
117
|
+
onInterfaceClick
|
|
136
118
|
? `Open ${reference.interface_kind} ${reference.interface_name}`
|
|
137
|
-
:
|
|
138
|
-
? `${described} — ${reason}`
|
|
139
|
-
: described
|
|
119
|
+
: `${reference.interface_kind}: ${reference.interface_name}`
|
|
140
120
|
}
|
|
141
|
-
onClick={
|
|
121
|
+
onClick={onInterfaceClick ? () => onInterfaceClick(reference) : undefined}
|
|
142
122
|
onRemoveClick={onRemove ? () => onRemove(reference) : undefined}
|
|
143
123
|
removeTooltip={`Remove ${reference.interface_kind} ${reference.interface_name}`}
|
|
144
124
|
/>
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
IInterfaceReference,
|
|
17
17
|
toInterfaceKindMark,
|
|
18
18
|
TResolveInterfaceIcon,
|
|
19
|
-
normalizeInterfaceKind,
|
|
20
19
|
} from './meta';
|
|
21
20
|
import { useLoadedImages } from './useLoadedImages';
|
|
22
21
|
|
|
@@ -343,9 +342,7 @@ export const ReferencePicker = ({
|
|
|
343
342
|
() =>
|
|
344
343
|
new Set(
|
|
345
344
|
(picked ?? [])
|
|
346
|
-
.filter(
|
|
347
|
-
(reference) => normalizeInterfaceKind(reference.interface_kind) === normalizeInterfaceKind(kind)
|
|
348
|
-
)
|
|
345
|
+
.filter((reference) => reference.interface_kind === kind)
|
|
349
346
|
.map((reference) => reference.interface_name)
|
|
350
347
|
),
|
|
351
348
|
[picked, kind]
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export * from './meta';
|
|
2
|
-
export * from './accessMeta';
|
|
3
2
|
export * from './ticketModel';
|
|
4
3
|
export * from './attachmentAccess';
|
|
5
4
|
export * from './AttachmentChips';
|
|
@@ -9,4 +8,3 @@ export * from './InterfaceReferenceTags';
|
|
|
9
8
|
export * from './ReferencePicker';
|
|
10
9
|
export * from './fileToAttachment';
|
|
11
10
|
export * from './TicketReplyBox';
|
|
12
|
-
export * from './WorkspaceAccessStatus';
|
|
@@ -13,39 +13,7 @@ import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
|
|
|
13
13
|
export type TTicketStatus = 'new' | 'open' | 'awaiting_customer' | 'resolved' | 'closed';
|
|
14
14
|
export type TTicketPriority = 'low' | 'normal' | 'high' | 'urgent';
|
|
15
15
|
export type TTicketCategory = 'bug' | 'question' | 'billing' | 'feature' | 'other';
|
|
16
|
-
export type TTicketAuditAction =
|
|
17
|
-
| 'replied'
|
|
18
|
-
| 'noted'
|
|
19
|
-
| 'status_changed'
|
|
20
|
-
| 'assigned'
|
|
21
|
-
| 'updated'
|
|
22
|
-
// staff reads (qorus#693 Layer 0): a ticket, its thread or its attachment list was opened,
|
|
23
|
-
// or a file was downloaded — coalesced server-side per staff member and window
|
|
24
|
-
| 'viewed'
|
|
25
|
-
| 'attachment_downloaded'
|
|
26
|
-
// delegated workspace access (qorus#693 Layers 1–3): the customer's grant lifecycle, a
|
|
27
|
-
// staff read of the workspace under it, and a read-only IDE session launched from it
|
|
28
|
-
| 'workspace_viewed'
|
|
29
|
-
| 'access_requested'
|
|
30
|
-
| 'access_granted'
|
|
31
|
-
| 'access_extended'
|
|
32
|
-
| 'access_revoked'
|
|
33
|
-
| 'access_expired'
|
|
34
|
-
| 'session_launched';
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The `system_event` a system message in a ticket thread may carry (qorus#693): the
|
|
38
|
-
* delegated-access lifecycle, written by the control plane as a thread message so the
|
|
39
|
-
* customer and the staff member see the same record in the conversation. A thread
|
|
40
|
-
* renders a known event with its own mark; an unknown one falls back to a plain line.
|
|
41
|
-
*/
|
|
42
|
-
export type TTicketSystemEvent =
|
|
43
|
-
| 'support_access.requested'
|
|
44
|
-
| 'support_access.granted'
|
|
45
|
-
| 'support_access.extended'
|
|
46
|
-
| 'support_access.first_used'
|
|
47
|
-
| 'support_access.revoked'
|
|
48
|
-
| 'support_access.expired';
|
|
16
|
+
export type TTicketAuditAction = 'replied' | 'noted' | 'status_changed' | 'assigned' | 'updated';
|
|
49
17
|
|
|
50
18
|
/** A message author. Staff identities are masked from the customer by the backend. */
|
|
51
19
|
export type TTicketMessageAuthor = 'customer' | 'staff' | 'system';
|
|
@@ -100,72 +68,8 @@ export const TICKET_AUDIT_ACTION_META: Record<TTicketAuditAction, ITicketEnumMet
|
|
|
100
68
|
status_changed: { label: 'Status changed', intent: 'warning', icon: 'ExchangeLine' },
|
|
101
69
|
assigned: { label: 'Assigned', intent: 'pending', icon: 'UserFollowLine' },
|
|
102
70
|
updated: { label: 'Updated', intent: 'muted', icon: 'Edit2Line' },
|
|
103
|
-
viewed: { label: 'Viewed', intent: 'muted', icon: 'EyeLine' },
|
|
104
|
-
attachment_downloaded: { label: 'Attachment downloaded', intent: 'muted', icon: 'Download2Line' },
|
|
105
|
-
workspace_viewed: { label: 'Workspace viewed', intent: 'info', icon: 'Search2Line' },
|
|
106
|
-
access_requested: { label: 'Access requested', intent: 'pending', icon: 'ShieldUserLine' },
|
|
107
|
-
access_granted: { label: 'Access granted', intent: 'success', icon: 'ShieldCheckLine' },
|
|
108
|
-
access_extended: { label: 'Access extended', intent: 'info', icon: 'TimerLine' },
|
|
109
|
-
access_revoked: { label: 'Access revoked', intent: 'danger', icon: 'ShieldCrossLine' },
|
|
110
|
-
access_expired: { label: 'Access expired', intent: 'muted', icon: 'HourglassLine' },
|
|
111
|
-
session_launched: { label: 'IDE session launched', intent: 'info', icon: 'ExternalLinkLine' },
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
/** The mark for an audit action the server writes but this vocabulary has not caught up
|
|
115
|
-
* with: a muted, generic row that still names the action. It exists because the staff
|
|
116
|
-
* audit tab used to index `TICKET_AUDIT_ACTION_META` directly, and one unknown action
|
|
117
|
-
* took the whole tab down (qorus#693). */
|
|
118
|
-
export const UNKNOWN_TICKET_AUDIT_ACTION_META: ITicketEnumMeta = {
|
|
119
|
-
label: 'Activity',
|
|
120
|
-
intent: 'muted',
|
|
121
|
-
icon: 'HistoryLine',
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
/** A wire value humanised for reading: `attachment_downloaded` → "Attachment downloaded". */
|
|
125
|
-
export const humaniseWireValue = (value: string): string => {
|
|
126
|
-
const words = value.replace(/[._-]+/g, ' ').trim();
|
|
127
|
-
return words ? words.charAt(0).toUpperCase() + words.slice(1) : value;
|
|
128
71
|
};
|
|
129
72
|
|
|
130
|
-
/**
|
|
131
|
-
* The mark for an audit action, whatever the server sent. A known action gets its
|
|
132
|
-
* entry; an unknown one gets the muted generic entry with the action's own wire value
|
|
133
|
-
* humanised as the label, so a new server action renders as a readable row instead
|
|
134
|
-
* of failing the surface that lists it.
|
|
135
|
-
*/
|
|
136
|
-
export const ticketAuditActionMeta = (action: string): ITicketEnumMeta =>
|
|
137
|
-
(TICKET_AUDIT_ACTION_META as Record<string, ITicketEnumMeta>)[action] ?? {
|
|
138
|
-
...UNKNOWN_TICKET_AUDIT_ACTION_META,
|
|
139
|
-
label: humaniseWireValue(action),
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/** How each delegated-access thread event renders: the mark on its system line or card. */
|
|
143
|
-
export const TICKET_SYSTEM_EVENT_META: Record<TTicketSystemEvent, ITicketEnumMeta> = {
|
|
144
|
-
'support_access.requested': {
|
|
145
|
-
label: 'Workspace access requested',
|
|
146
|
-
intent: 'info',
|
|
147
|
-
icon: 'ShieldUserLine',
|
|
148
|
-
},
|
|
149
|
-
'support_access.granted': { label: 'Workspace access granted', intent: 'success', icon: 'ShieldCheckLine' },
|
|
150
|
-
'support_access.extended': { label: 'Workspace access extended', intent: 'info', icon: 'TimerLine' },
|
|
151
|
-
'support_access.first_used': { label: 'Workspace opened by support', intent: 'info', icon: 'EyeLine' },
|
|
152
|
-
'support_access.revoked': { label: 'Workspace access revoked', intent: 'danger', icon: 'ShieldCrossLine' },
|
|
153
|
-
'support_access.expired': { label: 'Workspace access expired', intent: 'muted', icon: 'HourglassLine' },
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
/** The generic mark for a system message with no event, or an event this vocabulary
|
|
157
|
-
* does not know: the plain informational line every system message used to be. */
|
|
158
|
-
export const DEFAULT_TICKET_SYSTEM_EVENT_META: ITicketEnumMeta = {
|
|
159
|
-
label: 'System',
|
|
160
|
-
intent: 'muted',
|
|
161
|
-
icon: 'InformationLine',
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
/** The mark for a system message's `system_event`, with the generic fallback. */
|
|
165
|
-
export const ticketSystemEventMeta = (event?: string): ITicketEnumMeta =>
|
|
166
|
-
(event && (TICKET_SYSTEM_EVENT_META as Record<string, ITicketEnumMeta>)[event]) ||
|
|
167
|
-
DEFAULT_TICKET_SYSTEM_EVENT_META;
|
|
168
|
-
|
|
169
73
|
/** The status label for a given viewer — the customer sees "Awaiting your reply"
|
|
170
74
|
* where staff see "Awaiting customer"; all other values are audience-neutral. */
|
|
171
75
|
export const ticketStatusLabel = (
|
|
@@ -200,28 +104,12 @@ export interface IAttachmentUpload {
|
|
|
200
104
|
* `{kind, name}` snapshot (the interface itself lives on the instance, not the
|
|
201
105
|
* SaaS plane). Attaches to a ticket (at creation) or a message (in a reply). */
|
|
202
106
|
export interface IInterfaceReference {
|
|
203
|
-
/**
|
|
204
|
-
* The interface's kind. On READ this is the normalized Qorus `TypeMap` key the server
|
|
205
|
-
* stores (`fsm`, never `qog`; `step`, never `workflow-step`), so two surfaces compare
|
|
206
|
-
* equal; a writer may still send the spelling it has, and the server normalizes it.
|
|
207
|
-
* Compare kinds through `normalizeInterfaceKind()` rather than by string.
|
|
208
|
-
*/
|
|
107
|
+
/** interface type: workflow / service / job / fsm / qog / connection / ... */
|
|
209
108
|
interface_kind: string;
|
|
210
109
|
/** interface name (may include a version) */
|
|
211
110
|
interface_name: string;
|
|
212
111
|
/** server-assigned reference UUID (present on read, absent when submitting) */
|
|
213
112
|
reference_id?: string;
|
|
214
|
-
/**
|
|
215
|
-
* The interface's id on the subscriber's instance at the time it was referenced, when
|
|
216
|
-
* the writer knew it (the IDE's picker does). A viewer that can reach the instance
|
|
217
|
-
* prefers it over the name, which may have been renamed or versioned since.
|
|
218
|
-
*/
|
|
219
|
-
interface_id?: number;
|
|
220
|
-
/** The group the interface lived in when referenced — set by the server on read. */
|
|
221
|
-
groupid?: number;
|
|
222
|
-
/** That group's portable namespace — set by the server on read; absent for the host's
|
|
223
|
-
* DEFAULT group. What a support reader needs to find the interface after a move. */
|
|
224
|
-
group_namespace?: number;
|
|
225
113
|
/** For an app-triggered interface (a qog): the triggering app's name. A viewer
|
|
226
114
|
* with the app catalogue (the customer's IDE) resolves it to the app's logo the
|
|
227
115
|
* same way the qogs list does — see `resolveInterfaceImage` on the tags. */
|
|
@@ -232,47 +120,6 @@ export interface IInterfaceReference {
|
|
|
232
120
|
logo?: string;
|
|
233
121
|
}
|
|
234
122
|
|
|
235
|
-
// Kind spellings a writer may still send, and the normalized `TypeMap` key the server
|
|
236
|
-
// stores for each. The IDE's menus and older references say `qog`; the engine's type
|
|
237
|
-
// is `fsm`. A `workflow-step` is a `step`.
|
|
238
|
-
const INTERFACE_KIND_ALIASES: Record<string, string> = {
|
|
239
|
-
qog: 'fsm',
|
|
240
|
-
'workflow-step': 'step',
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* The normalized `TypeMap` key for a kind, however a writer spelled it: `qog` → `fsm`,
|
|
245
|
-
* `workflow-step` → `step`; anything else is returned trimmed and lower-cased. Every
|
|
246
|
-
* comparison of two references' kinds goes through this, so a reference written as a
|
|
247
|
-
* `qog` and one read back as an `fsm` are the same interface.
|
|
248
|
-
*/
|
|
249
|
-
export const normalizeInterfaceKind = (kind: string): string => {
|
|
250
|
-
const key = kind.trim().toLowerCase();
|
|
251
|
-
return INTERFACE_KIND_ALIASES[key] ?? key;
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* A reference's identity for keys and dedupe: its normalized kind and its name. Not the
|
|
256
|
-
* `reference_id` (absent when submitting) and not the `interface_id` (absent on older
|
|
257
|
-
* references), so the same interface cited twice is one key whatever each copy carries.
|
|
258
|
-
*/
|
|
259
|
-
export const interfaceReferenceKey = (reference: IInterfaceReference): string =>
|
|
260
|
-
`${normalizeInterfaceKind(reference.interface_kind)}:${reference.interface_name}`;
|
|
261
|
-
|
|
262
|
-
/** The references with duplicates dropped — first occurrence wins, order kept —
|
|
263
|
-
* where two references are the same interface by `interfaceReferenceKey()`. */
|
|
264
|
-
export const dedupeInterfaceReferences = <T extends IInterfaceReference>(references: T[]): T[] => {
|
|
265
|
-
const seen = new Set<string>();
|
|
266
|
-
return references.filter((reference) => {
|
|
267
|
-
const key = interfaceReferenceKey(reference);
|
|
268
|
-
if (seen.has(key)) {
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
seen.add(key);
|
|
272
|
-
return true;
|
|
273
|
-
});
|
|
274
|
-
};
|
|
275
|
-
|
|
276
123
|
// Built-in per-kind icons for referenced interfaces, kept in step with the
|
|
277
124
|
// qorus-ide interface vocabulary. Used when a consumer doesn't pass its own
|
|
278
125
|
// `resolveInterfaceIcon`, so the staff view and the Qonsole renderer show the
|
|
@@ -352,7 +199,8 @@ export const defaultInterfaceKindLabel = (kind: string): string => {
|
|
|
352
199
|
return known;
|
|
353
200
|
}
|
|
354
201
|
|
|
355
|
-
const
|
|
202
|
+
const words = kind.replace(/[-_]+/g, ' ').trim();
|
|
203
|
+
const sentenceCase = words.charAt(0).toUpperCase() + words.slice(1);
|
|
356
204
|
|
|
357
205
|
return sentenceCase.endsWith('s') ? sentenceCase : `${sentenceCase}s`;
|
|
358
206
|
};
|
|
@@ -61,16 +61,6 @@ export interface ITicketMessage {
|
|
|
61
61
|
attachments?: ITicketAttachment[];
|
|
62
62
|
/** interfaces referenced in this message, as `{kind, name}` snapshots */
|
|
63
63
|
referenced_interfaces?: IInterfaceReference[];
|
|
64
|
-
/**
|
|
65
|
-
* For a `system` message: the event it records (qorus#693), e.g.
|
|
66
|
-
* `support_access.requested`. A thread renders a known event with its own mark, and the
|
|
67
|
-
* `support_access.requested` event as a card that can carry the customer's Grant action;
|
|
68
|
-
* absent on the plain system lines (status changes, assignments).
|
|
69
|
-
*/
|
|
70
|
-
system_event?: string;
|
|
71
|
-
/** For a `system` message: the event's structured payload (a grant id, an expiry, the
|
|
72
|
-
* requesting staff member's reason), as the server wrote it. */
|
|
73
|
-
system_detail?: Record<string, unknown>;
|
|
74
64
|
}
|
|
75
65
|
|
|
76
66
|
/**
|
|
@@ -313,43 +313,3 @@ export const ShowInChat: Story = {
|
|
|
313
313
|
await expect(args.onShowInChat).toHaveBeenCalledTimes(1);
|
|
314
314
|
},
|
|
315
315
|
};
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* A viewer that can open some references but not others: a refused row offers no Open
|
|
319
|
-
* action and says why in its metadata line, next to the kind — in the row, so it reads on
|
|
320
|
-
* a touch screen, where a tooltip never shows.
|
|
321
|
-
*/
|
|
322
|
-
export const MixedLinkability: Story = {
|
|
323
|
-
parameters: {
|
|
324
|
-
docs: {
|
|
325
|
-
description: {
|
|
326
|
-
story:
|
|
327
|
-
'Renders the linked-interface rows with two refusals: those rows have no Open action and carry the reason in their metadata line after the kind, visible without hover (qorus#693).',
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
args: {
|
|
332
|
-
attachments: [],
|
|
333
|
-
onInterfaceClick: fn(),
|
|
334
|
-
isInterfaceLinkable: (reference: IInterfaceReference) => reference.interface_kind !== 'workflow',
|
|
335
|
-
unlinkableReason: (reference: IInterfaceReference) =>
|
|
336
|
-
reference.interface_kind === 'workflow' ? 'moved to another group' : undefined,
|
|
337
|
-
},
|
|
338
|
-
play: async ({ canvasElement, args }) => {
|
|
339
|
-
const canvas = within(canvasElement);
|
|
340
|
-
await expect(canvas.getByText('Linked interfaces')).toBeInTheDocument();
|
|
341
|
-
// the reason is in the row's text, not only a tooltip
|
|
342
|
-
await expect(canvas.getAllByText('workflow · moved to another group')).toHaveLength(2);
|
|
343
|
-
// the refused rows have no Open action; the three others do
|
|
344
|
-
await waitFor(() =>
|
|
345
|
-
expect(canvasElement.querySelectorAll('.reqore-entity-row-actions button')).toHaveLength(3)
|
|
346
|
-
);
|
|
347
|
-
await userEvent.click(canvasElement.querySelectorAll('.reqore-entity-row-actions button')[0]);
|
|
348
|
-
await waitFor(() =>
|
|
349
|
-
expect(args.onInterfaceClick).toHaveBeenCalledWith(
|
|
350
|
-
expect.objectContaining({ interface_kind: 'connection' })
|
|
351
|
-
)
|
|
352
|
-
);
|
|
353
|
-
},
|
|
354
|
-
};
|
|
355
|
-
|