@qoretechnologies/reqraft 0.10.25 → 0.10.26
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 +52 -23
- package/design/COMPACT_ENGINE_REDESIGN.md +156 -0
- package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +353 -0
- package/dist/components/FocusedEditing/index.d.ts +9 -1
- package/dist/components/FocusedEditing/index.d.ts.map +1 -1
- package/dist/components/FocusedEditing/index.js +2 -2
- package/dist/components/FocusedEditing/index.js.map +1 -1
- package/dist/components/composer/Composer.d.ts +108 -0
- package/dist/components/composer/Composer.d.ts.map +1 -0
- package/dist/components/composer/Composer.js +238 -0
- package/dist/components/composer/Composer.js.map +1 -0
- package/dist/components/composer/helpers.d.ts +21 -0
- package/dist/components/composer/helpers.d.ts.map +1 -0
- package/dist/components/composer/helpers.js +75 -0
- package/dist/components/composer/helpers.js.map +1 -0
- package/dist/components/composer/index.d.ts +3 -0
- package/dist/components/composer/index.d.ts.map +1 -0
- package/dist/components/composer/index.js +19 -0
- package/dist/components/composer/index.js.map +1 -0
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +307 -110
- package/dist/components/form/engine/CompactRow.js.map +1 -1
- package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
- package/dist/components/form/engine/CompactToolbar.js +123 -106
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +107 -11
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +618 -179
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +9 -0
- package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowContext.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts +7 -3
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +88 -51
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
- package/dist/components/form/engine/optionActions.d.ts +28 -0
- package/dist/components/form/engine/optionActions.d.ts.map +1 -0
- package/dist/components/form/engine/optionActions.js +18 -0
- package/dist/components/form/engine/optionActions.js.map +1 -0
- package/dist/components/form/engine/readFirst.d.ts +63 -2
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +121 -6
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +35 -0
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -0
- package/dist/components/form/engine/rendererTypes.js +63 -0
- package/dist/components/form/engine/rendererTypes.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js +94 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.js +80 -0
- package/dist/components/form/engine/variants/VariantCards.js.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts +7 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.js +138 -0
- package/dist/components/form/engine/variants/VariantFocus.js.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.js +73 -0
- package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts +13 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.js +139 -0
- package/dist/components/form/engine/variants/focusDemo.js.map +1 -0
- package/dist/components/form/engine/variants/variantModel.d.ts +70 -0
- package/dist/components/form/engine/variants/variantModel.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantModel.js +133 -0
- package/dist/components/form/engine/variants/variantModel.js.map +1 -0
- package/dist/components/form/engine/variants/variantParts.d.ts +79 -0
- package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantParts.js +191 -0
- package/dist/components/form/engine/variants/variantParts.js.map +1 -0
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +13 -2
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.js +4 -1
- package/dist/components/form/expressions/useExpressions.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +5 -3
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +2 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +8 -26
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts +3 -0
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +42 -8
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/binary/Binary.d.ts +15 -0
- package/dist/components/form/fields/binary/Binary.d.ts.map +1 -0
- package/dist/components/form/fields/binary/Binary.js +74 -0
- package/dist/components/form/fields/binary/Binary.js.map +1 -0
- package/dist/components/form/fields/boolean/Boolean.js +1 -1
- package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +5 -4
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +29 -12
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +8 -2
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +52 -16
- package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +2 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/imageLightbox/ImageLightbox.d.ts +21 -0
- package/dist/components/imageLightbox/ImageLightbox.d.ts.map +1 -0
- package/dist/components/imageLightbox/ImageLightbox.js +169 -0
- package/dist/components/imageLightbox/ImageLightbox.js.map +1 -0
- package/dist/components/imageLightbox/index.d.ts +2 -0
- package/dist/components/imageLightbox/index.d.ts.map +1 -0
- package/dist/components/imageLightbox/index.js +18 -0
- package/dist/components/imageLightbox/index.js.map +1 -0
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts +33 -0
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -0
- package/dist/components/searchFilterBar/SearchFilterBar.js +76 -0
- package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -0
- package/dist/components/searchFilterBar/index.d.ts +2 -0
- package/dist/components/searchFilterBar/index.d.ts.map +1 -0
- package/dist/components/searchFilterBar/index.js +18 -0
- package/dist/components/searchFilterBar/index.js.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts +10 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.js +122 -0
- package/dist/components/supportTicket/AttachmentChips.js.map +1 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +43 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.js +31 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -0
- package/dist/components/supportTicket/ReferencePicker.d.ts +87 -0
- package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -0
- package/dist/components/supportTicket/ReferencePicker.js +295 -0
- package/dist/components/supportTicket/ReferencePicker.js.map +1 -0
- package/dist/components/supportTicket/TicketHeader.d.ts +86 -0
- package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketHeader.js +149 -0
- package/dist/components/supportTicket/TicketHeader.js.map +1 -0
- package/dist/components/supportTicket/TicketMetaTags.d.ts +32 -0
- package/dist/components/supportTicket/TicketMetaTags.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketMetaTags.js +21 -0
- package/dist/components/supportTicket/TicketMetaTags.js.map +1 -0
- package/dist/components/supportTicket/TicketReplyBox.d.ts +63 -0
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketReplyBox.js +115 -0
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -0
- package/dist/components/supportTicket/attachmentAccess.d.ts +37 -0
- package/dist/components/supportTicket/attachmentAccess.d.ts.map +1 -0
- package/dist/components/supportTicket/attachmentAccess.js +132 -0
- package/dist/components/supportTicket/attachmentAccess.js.map +1 -0
- package/dist/components/supportTicket/fileToAttachment.d.ts +9 -0
- package/dist/components/supportTicket/fileToAttachment.d.ts.map +1 -0
- package/dist/components/supportTicket/fileToAttachment.js +28 -0
- package/dist/components/supportTicket/fileToAttachment.js.map +1 -0
- package/dist/components/supportTicket/index.d.ts +11 -0
- package/dist/components/supportTicket/index.d.ts.map +1 -0
- package/dist/components/supportTicket/index.js +27 -0
- package/dist/components/supportTicket/index.js.map +1 -0
- package/dist/components/supportTicket/meta.d.ts +71 -0
- package/dist/components/supportTicket/meta.d.ts.map +1 -0
- package/dist/components/supportTicket/meta.js +115 -0
- package/dist/components/supportTicket/meta.js.map +1 -0
- package/dist/components/supportTicket/ticketModel.d.ts +102 -0
- package/dist/components/supportTicket/ticketModel.d.ts.map +1 -0
- package/dist/components/supportTicket/ticketModel.js +3 -0
- package/dist/components/supportTicket/ticketModel.js.map +1 -0
- package/dist/components/ticketReferences/TicketReferences.d.ts +52 -0
- package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -0
- package/dist/components/ticketReferences/TicketReferences.js +492 -0
- package/dist/components/ticketReferences/TicketReferences.js.map +1 -0
- package/dist/components/ticketReferences/index.d.ts +3 -0
- package/dist/components/ticketReferences/index.d.ts.map +1 -0
- package/dist/components/ticketReferences/index.js +6 -0
- package/dist/components/ticketReferences/index.js.map +1 -0
- package/dist/components/ticketThread/TicketThread.d.ts +57 -0
- package/dist/components/ticketThread/TicketThread.d.ts.map +1 -0
- package/dist/components/ticketThread/TicketThread.js +299 -0
- package/dist/components/ticketThread/TicketThread.js.map +1 -0
- package/dist/components/ticketThread/index.d.ts +3 -0
- package/dist/components/ticketThread/index.d.ts.map +1 -0
- package/dist/components/ticketThread/index.js +7 -0
- package/dist/components/ticketThread/index.js.map +1 -0
- package/dist/helpers/common.d.ts +5 -0
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +20 -1
- package/dist/helpers/common.js.map +1 -1
- package/dist/helpers/scrollbar.d.ts +2 -0
- package/dist/helpers/scrollbar.d.ts.map +1 -0
- package/dist/helpers/scrollbar.js +25 -0
- package/dist/helpers/scrollbar.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/StorageProvider.d.ts +13 -0
- package/dist/providers/StorageProvider.d.ts.map +1 -1
- package/dist/providers/StorageProvider.js +29 -5
- package/dist/providers/StorageProvider.js.map +1 -1
- package/package.json +6 -5
- package/src/components/FocusedEditing/index.tsx +17 -2
- package/src/components/composer/Composer.stories.tsx +238 -0
- package/src/components/composer/Composer.tsx +545 -0
- package/src/components/composer/helpers.ts +74 -0
- package/src/components/composer/index.ts +2 -0
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +74 -2
- package/src/components/form/engine/CompactRow.tsx +561 -283
- package/src/components/form/engine/CompactToolbar.tsx +216 -182
- package/src/components/form/engine/FormEngine.stories.tsx +1736 -162
- package/src/components/form/engine/FormEngine.tsx +886 -170
- package/src/components/form/engine/FormEngineRemote.stories.tsx +377 -0
- package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +8 -0
- package/src/components/form/engine/compactRowContext.ts +14 -0
- package/src/components/form/engine/compactRowStyles.ts +273 -146
- package/src/components/form/engine/compactToolbarContext.ts +1 -0
- package/src/components/form/engine/optionActions.ts +40 -0
- package/src/components/form/engine/readFirst.ts +169 -10
- package/src/components/form/engine/rendererTypes.ts +55 -0
- package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +159 -0
- package/src/components/form/engine/variants/VariantCalmTable.tsx +242 -0
- package/src/components/form/engine/variants/VariantCards.tsx +212 -0
- package/src/components/form/engine/variants/VariantFocus.tsx +382 -0
- package/src/components/form/engine/variants/VariantMinimal.tsx +170 -0
- package/src/components/form/engine/variants/focusDemo.ts +145 -0
- package/src/components/form/engine/variants/variantModel.ts +216 -0
- package/src/components/form/engine/variants/variantParts.tsx +313 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +84 -2
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +213 -19
- package/src/components/form/expressions/builder/index.tsx +12 -1
- package/src/components/form/expressions/useExpressions.ts +4 -1
- package/src/components/form/fields/Field.stories.tsx +40 -0
- package/src/components/form/fields/Field.tsx +10 -1
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -36
- package/src/components/form/fields/array/ArrayAutoField.stories.tsx +64 -0
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +146 -2
- package/src/components/form/fields/auto/AutoFormField.tsx +42 -1
- package/src/components/form/fields/binary/Binary.stories.tsx +122 -0
- package/src/components/form/fields/binary/Binary.tsx +81 -0
- package/src/components/form/fields/boolean/Boolean.stories.tsx +58 -0
- package/src/components/form/fields/boolean/Boolean.tsx +1 -1
- package/src/components/form/fields/byte-size/ByteSize.stories.tsx +24 -0
- package/src/components/form/fields/color/Color.stories.tsx +30 -0
- package/src/components/form/fields/cron/Cron.stories.tsx +8 -0
- package/src/components/form/fields/date/Date.stories.tsx +22 -0
- package/src/components/form/fields/file/File.stories.tsx +48 -0
- package/src/components/form/fields/long-string/LongString.stories.tsx +24 -0
- package/src/components/form/fields/markdown/Markdown.stories.tsx +8 -0
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +14 -10
- package/src/components/form/fields/number/Number.stories.tsx +31 -0
- package/src/components/form/fields/object/Object.stories.tsx +88 -0
- package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +24 -0
- package/src/components/form/fields/rich-text/RichText.stories.tsx +23 -0
- package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +72 -0
- package/src/components/form/fields/select/Select.stories.tsx +108 -0
- package/src/components/form/fields/select/Select.tsx +38 -12
- package/src/components/form/fields/select/SelectCollection.tsx +78 -17
- package/src/components/form/fields/string/String.stories.tsx +16 -0
- package/src/components/form/fields/template/TemplateField.stories.tsx +160 -0
- package/src/components/form/fields/url/Url.stories.tsx +24 -0
- package/src/components/form/index.tsx +2 -0
- package/src/components/imageLightbox/ImageLightbox.stories.tsx +238 -0
- package/src/components/imageLightbox/ImageLightbox.tsx +236 -0
- package/src/components/imageLightbox/index.ts +1 -0
- package/src/components/log/Log.stories.tsx +96 -0
- package/src/components/menu/Menu.stories.tsx +54 -0
- package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +83 -37
- package/src/components/searchFilterBar/SearchFilterBar.tsx +131 -0
- package/src/components/searchFilterBar/index.ts +1 -0
- package/src/components/smartEditor/SmartEditor.stories.tsx +32 -0
- package/src/components/supportTicket/AttachmentChips.stories.tsx +171 -0
- package/src/components/supportTicket/AttachmentChips.tsx +147 -0
- package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +171 -0
- package/src/components/supportTicket/InterfaceReferenceTags.tsx +96 -0
- package/src/components/supportTicket/ReferencePicker.stories.tsx +519 -0
- package/src/components/supportTicket/ReferencePicker.tsx +563 -0
- package/src/components/supportTicket/TicketConversation.stories.tsx +121 -0
- package/src/components/supportTicket/TicketHeader.stories.tsx +183 -0
- package/src/components/supportTicket/TicketHeader.tsx +247 -0
- package/src/components/supportTicket/TicketMetaTags.stories.tsx +103 -0
- package/src/components/supportTicket/TicketMetaTags.tsx +85 -0
- package/src/components/supportTicket/TicketReplyBox.stories.tsx +351 -0
- package/src/components/supportTicket/TicketReplyBox.tsx +146 -0
- package/src/components/supportTicket/attachmentAccess.ts +97 -0
- package/src/components/supportTicket/fileToAttachment.ts +24 -0
- package/src/components/supportTicket/index.ts +10 -0
- package/src/components/supportTicket/meta.ts +185 -0
- package/src/components/supportTicket/ticketModel.ts +131 -0
- package/src/components/ticketReferences/TicketReferences.stories.tsx +315 -0
- package/src/components/ticketReferences/TicketReferences.tsx +886 -0
- package/src/components/ticketReferences/index.ts +2 -0
- package/src/components/ticketThread/TicketThread.stories.tsx +828 -0
- package/src/components/ticketThread/TicketThread.tsx +704 -0
- package/src/components/ticketThread/index.ts +6 -0
- package/src/helpers/common.ts +19 -0
- package/src/helpers/scrollbar.ts +32 -0
- package/src/hooks/useFetch/useFetch.stories.tsx +30 -0
- package/src/hooks/useStorage/useStorage.stories.tsx +82 -0
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +127 -1
- package/src/index.tsx +8 -1
- package/src/providers/StorageProvider.tsx +30 -4
- package/src/stores/currentUser/currentUser.stories.tsx +49 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { ReqoreButton, ReqoreDropdown } from '@qoretechnologies/reqore';
|
|
2
|
+
import { StoryObj } from '@storybook/react-vite';
|
|
3
|
+
import { expect, within } from 'storybook/test';
|
|
4
|
+
import { StoryMeta } from '../../types';
|
|
5
|
+
import { TicketMetaTags } from './TicketMetaTags';
|
|
6
|
+
import {
|
|
7
|
+
TicketAvatar,
|
|
8
|
+
TicketHeader,
|
|
9
|
+
TicketHeaderText,
|
|
10
|
+
TicketPersonValue,
|
|
11
|
+
ticketInitials,
|
|
12
|
+
} from './TicketHeader';
|
|
13
|
+
|
|
14
|
+
const meta = {
|
|
15
|
+
component: TicketHeader,
|
|
16
|
+
title: 'Components/TicketHeader',
|
|
17
|
+
args: {
|
|
18
|
+
subject: 'Workflow deploy fails at the mapper step',
|
|
19
|
+
reference: '#1001',
|
|
20
|
+
},
|
|
21
|
+
} as StoryMeta<typeof TicketHeader>;
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj<typeof meta>;
|
|
25
|
+
|
|
26
|
+
const person = (name: string) => (
|
|
27
|
+
<TicketPersonValue>
|
|
28
|
+
<TicketAvatar>{ticketInitials(name)}</TicketAvatar>
|
|
29
|
+
<TicketHeaderText>{name}</TicketHeaderText>
|
|
30
|
+
</TicketPersonValue>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* What a customer sees: the same rail, every value read-only. The properties are the
|
|
35
|
+
* caller's — this is the shorter of the two lists a real surface passes.
|
|
36
|
+
*/
|
|
37
|
+
export const Customer: Story = {
|
|
38
|
+
parameters: {
|
|
39
|
+
docs: {
|
|
40
|
+
description: {
|
|
41
|
+
story:
|
|
42
|
+
"Renders the customer's header rail — subject, reference, and a short list of read-only property rows, with the status shown from the customer's viewpoint as 'Awaiting your reply'.",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
args: {
|
|
47
|
+
properties: [
|
|
48
|
+
{
|
|
49
|
+
label: 'Status',
|
|
50
|
+
value: (
|
|
51
|
+
<TicketMetaTags
|
|
52
|
+
ticket={{ status: 'awaiting_customer', priority: 'high', category: 'bug' }}
|
|
53
|
+
viewerRole='customer'
|
|
54
|
+
/>
|
|
55
|
+
),
|
|
56
|
+
},
|
|
57
|
+
{ label: 'Opened by', value: person('ide-demo@local.dev') },
|
|
58
|
+
{ label: 'References', value: <TicketHeaderText muted>None</TicketHeaderText> },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
async play({ canvasElement }) {
|
|
62
|
+
const canvas = within(canvasElement);
|
|
63
|
+
await expect(canvas.getByText('Workflow deploy fails at the mapper step')).toBeInTheDocument();
|
|
64
|
+
await expect(canvas.getByText('#1001')).toBeInTheDocument();
|
|
65
|
+
await expect(canvas.getByText('Awaiting your reply')).toBeInTheDocument();
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* What staff see: the same frame with pressable values. The row is as tall as a
|
|
71
|
+
* control whether or not it holds one, so a rail that mixes text and dropdowns still
|
|
72
|
+
* reads as a table — that invariant is what the alignment test below pins.
|
|
73
|
+
*/
|
|
74
|
+
export const Staff: Story = {
|
|
75
|
+
parameters: {
|
|
76
|
+
docs: {
|
|
77
|
+
description: {
|
|
78
|
+
story:
|
|
79
|
+
'Renders the staff header rail — the same frame with pressable values (a status dropdown, assignee and customer people, a references button) alongside plain text.',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
args: {
|
|
84
|
+
properties: [
|
|
85
|
+
{
|
|
86
|
+
label: 'Status',
|
|
87
|
+
value: (
|
|
88
|
+
<ReqoreDropdown
|
|
89
|
+
size='small'
|
|
90
|
+
minimal
|
|
91
|
+
flat
|
|
92
|
+
label='Open'
|
|
93
|
+
showCaret={false}
|
|
94
|
+
rightIcon='ArrowDownSLine'
|
|
95
|
+
items={[{ label: 'Open', selected: true }, { label: 'Resolved' }]}
|
|
96
|
+
/>
|
|
97
|
+
),
|
|
98
|
+
},
|
|
99
|
+
{ label: 'Assignee', value: person('nick.m@acme.io') },
|
|
100
|
+
{ label: 'Customer', value: person('ide-demo@local.dev') },
|
|
101
|
+
{
|
|
102
|
+
label: 'References',
|
|
103
|
+
value: (
|
|
104
|
+
<ReqoreButton size='small' minimal flat icon='GitBranchLine'>
|
|
105
|
+
5 references
|
|
106
|
+
</ReqoreButton>
|
|
107
|
+
),
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
async play({ canvasElement }) {
|
|
112
|
+
const canvas = within(canvasElement);
|
|
113
|
+
await expect(canvas.getByText('nick.m@acme.io')).toBeInTheDocument();
|
|
114
|
+
await expect(canvas.getByText('NM')).toBeInTheDocument();
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The rail's two layout invariants, measured rather than eyeballed — both have
|
|
120
|
+
* regressed by hand more than once.
|
|
121
|
+
*
|
|
122
|
+
* 1. Every value starts on the same vertical edge, whether it is a control (which
|
|
123
|
+
* insets its own content by its horizontal padding) or bare text (which does not).
|
|
124
|
+
* 2. Every row is the same height, whether it holds a 32px control or a line of text.
|
|
125
|
+
*/
|
|
126
|
+
export const RowsAlign: Story = {
|
|
127
|
+
parameters: {
|
|
128
|
+
docs: {
|
|
129
|
+
description: {
|
|
130
|
+
story:
|
|
131
|
+
"Renders the staff rail again to measure its two layout invariants — every value's content starts on the same vertical edge, and every row is the same height, whether the row holds a control or bare text.",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
args: Staff.args,
|
|
136
|
+
async play({ canvasElement }) {
|
|
137
|
+
const rows = Array.from(
|
|
138
|
+
canvasElement.querySelectorAll<HTMLElement>('.reqore-description-list-row')
|
|
139
|
+
);
|
|
140
|
+
await expect(rows.length).toBe(4);
|
|
141
|
+
|
|
142
|
+
/* Where the value's CONTENT starts, not where its box does. The inset is padding
|
|
143
|
+
and it lands on the value itself — the button on a control row, the person span
|
|
144
|
+
on a text row — so measure that child's padded edge. */
|
|
145
|
+
const contentLeft = (row: HTMLElement) => {
|
|
146
|
+
const column = row.querySelector<HTMLElement>('.reqore-description-list-content')!;
|
|
147
|
+
const value = (column.firstElementChild as HTMLElement | null) ?? column;
|
|
148
|
+
return Math.round(
|
|
149
|
+
value.getBoundingClientRect().left + parseFloat(getComputedStyle(value).paddingLeft)
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const lefts = rows.map(contentLeft);
|
|
154
|
+
await expect(new Set(lefts).size, `value content starts at: ${lefts.join(', ')}`).toBe(1);
|
|
155
|
+
|
|
156
|
+
const heights = rows.map((row) => Math.round(row.getBoundingClientRect().height));
|
|
157
|
+
await expect(new Set(heights).size, `row heights: ${heights.join(', ')}`).toBe(1);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** `when: false` drops a row, so one list can serve both viewer roles. */
|
|
162
|
+
export const HiddenRow: Story = {
|
|
163
|
+
parameters: {
|
|
164
|
+
docs: {
|
|
165
|
+
description: {
|
|
166
|
+
story:
|
|
167
|
+
'Renders a property list where one row is flagged when: false, showing that the flagged Assignee row drops out so a single list can serve both viewer roles.',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
args: {
|
|
172
|
+
properties: [
|
|
173
|
+
{ label: 'Status', value: <TicketHeaderText>Open</TicketHeaderText> },
|
|
174
|
+
{ label: 'Assignee', value: <TicketHeaderText>nick.m@acme.io</TicketHeaderText>, when: false },
|
|
175
|
+
{ label: 'Customer', value: <TicketHeaderText>ide-demo@local.dev</TicketHeaderText> },
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
async play({ canvasElement }) {
|
|
179
|
+
const canvas = within(canvasElement);
|
|
180
|
+
await expect(canvas.queryByText('Assignee')).not.toBeInTheDocument();
|
|
181
|
+
await expect(canvas.getByText('Customer')).toBeInTheDocument();
|
|
182
|
+
},
|
|
183
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReqoreControlGroup,
|
|
3
|
+
ReqoreDescriptionList,
|
|
4
|
+
ReqoreSpan,
|
|
5
|
+
} from '@qoretechnologies/reqore';
|
|
6
|
+
import { TReqoreBadge } from '@qoretechnologies/reqore/dist/components/Button';
|
|
7
|
+
import { TReqoreEffectColor } from '@qoretechnologies/reqore/dist/components/Effect';
|
|
8
|
+
import { SIZE_TO_PX } from '@qoretechnologies/reqore/dist/constants/sizes';
|
|
9
|
+
import { ReactNode } from 'react';
|
|
10
|
+
import styled from 'styled-components';
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* The header every ticket surface puts above its tabs: the subject, the ticket's
|
|
14
|
+
* number, and a rail of labelled properties.
|
|
15
|
+
*
|
|
16
|
+
* Shared because it had already drifted. The staff drawer's header was redesigned
|
|
17
|
+
* into this definition-list rail while the customer's kept a heading, a tag row and
|
|
18
|
+
* a muted meta line — the same information, two layouts, and no way to change one
|
|
19
|
+
* without hand-copying it into the other. What each surface shows differs (staff
|
|
20
|
+
* edit the assignee; a customer reads who opened the ticket), so the rows are the
|
|
21
|
+
* caller's; the frame, the rhythm and the alignment rules are not.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** How wide the label column is. Fits "References" — the longest label in use. */
|
|
25
|
+
const LABEL_WIDTH = '84px';
|
|
26
|
+
/** Everything in the rail runs at one scale. */
|
|
27
|
+
const RAIL_SIZE = 'small' as const;
|
|
28
|
+
/** Breathing room above and below each row's control — the rail's vertical rhythm. */
|
|
29
|
+
const ROW_PADDING = 4;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The height a tab item needs to sit on the rail's rhythm.
|
|
33
|
+
*
|
|
34
|
+
* Exported because the tab strip below the header has to match it and there is no
|
|
35
|
+
* Reqore prop for a tab's height: `size` sets it, and `padded` governs the items'
|
|
36
|
+
* horizontal edges, not the strip's. The height must go on the ITEM, never on the
|
|
37
|
+
* list — both of a tab's own surfaces are keyed to the item's box (the active marker
|
|
38
|
+
* is an inset shadow on its bottom edge, the hover wash fills it), so padding the
|
|
39
|
+
* list leaves the item floating in a taller strip: the marker lifts off the hairline
|
|
40
|
+
* and reads as a doubled line, and the wash reads as a floating box.
|
|
41
|
+
*
|
|
42
|
+
* This belongs in Reqore as a tab-height option rather than as a CSS rule each
|
|
43
|
+
* consumer re-applies. Until it exists, `TicketTabsFrame` is the single copy.
|
|
44
|
+
*/
|
|
45
|
+
export const TICKET_RAIL_ROW_HEIGHT = SIZE_TO_PX[RAIL_SIZE] + ROW_PADDING * 2;
|
|
46
|
+
|
|
47
|
+
/** The ticket number, set in mono so it reads as an identifier, not a heading. Exported
|
|
48
|
+
* so the staff drawer (which puts the number in the drawer's own title, not the rail)
|
|
49
|
+
* renders it identically rather than re-declaring the same span. */
|
|
50
|
+
export const TicketMonoId = styled.span`
|
|
51
|
+
font-family: ui-monospace, 'SF Mono', 'Menlo', monospace;
|
|
52
|
+
font-size: 11.5px;
|
|
53
|
+
color: rgba(255, 255, 255, 0.45);
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A person: initials disc, then the name, on one baseline.
|
|
58
|
+
*
|
|
59
|
+
* One wrapper for every person in the rail — the assignee, the customer, the author.
|
|
60
|
+
* They were visibly different before: one went through a nested control group inside
|
|
61
|
+
* a button (its own gap and alignment) while another used a plain flex row, so the
|
|
62
|
+
* two drifted apart every time either was touched. The only difference left is that
|
|
63
|
+
* some of them are pressable.
|
|
64
|
+
*/
|
|
65
|
+
export const TicketPersonValue = styled.span`
|
|
66
|
+
display: inline-flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: 6px;
|
|
69
|
+
min-width: 0;
|
|
70
|
+
|
|
71
|
+
/* the name yields before the disc does */
|
|
72
|
+
> span:last-child {
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
text-overflow: ellipsis;
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
/** Initials disc for a person. */
|
|
80
|
+
export const TicketAvatar = styled.span`
|
|
81
|
+
width: 18px;
|
|
82
|
+
height: 18px;
|
|
83
|
+
border-radius: 999px;
|
|
84
|
+
flex: none;
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
font-size: 8px;
|
|
89
|
+
font-weight: 650;
|
|
90
|
+
color: #fff;
|
|
91
|
+
background: linear-gradient(150deg, #8a4fe6, #2c1140);
|
|
92
|
+
border: 1px solid rgba(255, 255, 255, 0.14);
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Up to two initials from a handle ("nick.m@acme.io" → "NM"). Falls back to "?" so a
|
|
97
|
+
* handle made entirely of separators still renders a disc rather than an empty circle.
|
|
98
|
+
*/
|
|
99
|
+
export const ticketInitials = (name: string): string =>
|
|
100
|
+
name
|
|
101
|
+
.split(/[\s._@-]+/)
|
|
102
|
+
.filter(Boolean)
|
|
103
|
+
.slice(0, 2)
|
|
104
|
+
.map((part) => part[0]?.toUpperCase() ?? '')
|
|
105
|
+
.join('') || '?';
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Wraps a ticket surface's tab strip so it joins the header rail's rhythm.
|
|
109
|
+
*
|
|
110
|
+
* See `TICKET_RAIL_ROW_HEIGHT` for why the height goes on the item and not the list.
|
|
111
|
+
*/
|
|
112
|
+
export const TicketTabsFrame = styled.div`
|
|
113
|
+
display: flex;
|
|
114
|
+
flex-direction: column;
|
|
115
|
+
flex: 1;
|
|
116
|
+
min-height: 0;
|
|
117
|
+
|
|
118
|
+
.reqore-tabs-list {
|
|
119
|
+
padding-top: 0;
|
|
120
|
+
padding-bottom: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.reqore-tabs-list-item {
|
|
124
|
+
min-height: ${TICKET_RAIL_ROW_HEIGHT}px;
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
|
|
128
|
+
/*
|
|
129
|
+
* Centre each tab's icon + label + count.
|
|
130
|
+
*
|
|
131
|
+
* With `fill`, every tab is far wider than its content, and ReqoreButton defaults
|
|
132
|
+
* `textAlign` to 'left' for non-square buttons — which also hands the leading icon a
|
|
133
|
+
* `margin-right: auto`, pinning the group against the left edge of its tab. Setting
|
|
134
|
+
* both alignments centres the group as a unit, so the strip reads as evenly spaced
|
|
135
|
+
* segments rather than left-huddled labels.
|
|
136
|
+
*/
|
|
137
|
+
export const TICKET_TAB_ALIGN = {
|
|
138
|
+
textAlign: 'center',
|
|
139
|
+
iconsAlign: 'center',
|
|
140
|
+
} as const;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* A tab's count pill. The active tab's carries the accent; the rest stay neutral, so
|
|
144
|
+
* the strip reads as one selected item rather than several competing chips. Returns
|
|
145
|
+
* `undefined` for an empty count — a tab with nothing in it shows no badge at all.
|
|
146
|
+
*/
|
|
147
|
+
export const ticketTabBadge = (
|
|
148
|
+
count: number,
|
|
149
|
+
isActive: boolean,
|
|
150
|
+
accent?: { color: TReqoreEffectColor; glow: TReqoreEffectColor }
|
|
151
|
+
): TReqoreBadge | undefined => {
|
|
152
|
+
if (!count) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
if (!isActive || !accent) {
|
|
156
|
+
return count;
|
|
157
|
+
}
|
|
158
|
+
return { label: count, color: accent.glow, effect: { color: accent.color } };
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export interface ITicketHeaderProperty {
|
|
162
|
+
label: string;
|
|
163
|
+
/** plain text, a chip row, or a control — the row aligns all three the same way */
|
|
164
|
+
value: ReactNode;
|
|
165
|
+
/** Drop the row. Lets a caller keep one list and vary it by viewer role, rather
|
|
166
|
+
* than building a different array per branch. Defaults to shown. */
|
|
167
|
+
when?: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface ITicketHeaderProps {
|
|
171
|
+
/** Omit when the surrounding dialog already carries the subject — the staff drawer
|
|
172
|
+
* puts it in the drawer's own label, the customer's modal puts it here. */
|
|
173
|
+
subject?: string;
|
|
174
|
+
/** the ticket's number, e.g. `#1001` */
|
|
175
|
+
reference?: string;
|
|
176
|
+
properties: ITicketHeaderProperty[];
|
|
177
|
+
/** padding around the rail, matching the surface's own pane padding */
|
|
178
|
+
padding?: number;
|
|
179
|
+
className?: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const TicketHeader = ({
|
|
183
|
+
subject,
|
|
184
|
+
reference,
|
|
185
|
+
properties,
|
|
186
|
+
padding = 14,
|
|
187
|
+
className,
|
|
188
|
+
}: ITicketHeaderProps) => (
|
|
189
|
+
<ReqoreDescriptionList
|
|
190
|
+
/* The rail IS a description list — Reqore's, not a private copy. `contentSize`
|
|
191
|
+
is what makes it tabular with controls in it: it reserves a control's height on
|
|
192
|
+
every row and insets the values that aren't controls, so a dropdown row and a
|
|
193
|
+
plain-text row share both a height and a left edge. */
|
|
194
|
+
contentSize={RAIL_SIZE}
|
|
195
|
+
size={RAIL_SIZE}
|
|
196
|
+
/* Labels are reference furniture, quieter and smaller than the values they name —
|
|
197
|
+
and "References" has to fit LABEL_WIDTH on one line at this scale. */
|
|
198
|
+
labelTextSize='tiny'
|
|
199
|
+
/* The rail's rhythm, which the tab strip below is measured against — see
|
|
200
|
+
TICKET_RAIL_ROW_HEIGHT. Reqore's comfortable default would make the rows 52px
|
|
201
|
+
and the strip would no longer line up with them. */
|
|
202
|
+
rowPadding='tiny'
|
|
203
|
+
labelWidth={LABEL_WIDTH}
|
|
204
|
+
uppercaseLabels
|
|
205
|
+
flat
|
|
206
|
+
transparent
|
|
207
|
+
padded={false}
|
|
208
|
+
contentStyle={{ padding: `${padding - 4}px ${padding}px ${padding - 6}px` }}
|
|
209
|
+
className={`${className || ''} ticket-header`}
|
|
210
|
+
label={
|
|
211
|
+
subject ? (
|
|
212
|
+
/* The label is the panel's heading, which `padded={false}` leaves flush against
|
|
213
|
+
the edge — `contentStyle` only pads the rows. Pad the subject here so it lines
|
|
214
|
+
up with the values below it, using the same horizontal `padding` as the rows. */
|
|
215
|
+
<div style={{ padding: `${padding}px ${padding}px ${padding - 8}px`, minWidth: 0 }}>
|
|
216
|
+
<ReqoreControlGroup verticalAlign='center' gapSize='small'>
|
|
217
|
+
<span>{subject}</span>
|
|
218
|
+
{reference ? <TicketMonoId>{reference}</TicketMonoId> : null}
|
|
219
|
+
</ReqoreControlGroup>
|
|
220
|
+
</div>
|
|
221
|
+
) : undefined
|
|
222
|
+
}
|
|
223
|
+
items={properties
|
|
224
|
+
.filter((property) => property.when !== false)
|
|
225
|
+
.map((property) => ({
|
|
226
|
+
key: property.label,
|
|
227
|
+
label: property.label,
|
|
228
|
+
content: property.value,
|
|
229
|
+
}))}
|
|
230
|
+
/>
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
/** A rail value that is plain text rather than a control — sized to match the rest. */
|
|
234
|
+
export const TicketHeaderText = ({
|
|
235
|
+
children,
|
|
236
|
+
muted,
|
|
237
|
+
tooltip,
|
|
238
|
+
}: {
|
|
239
|
+
children: ReactNode;
|
|
240
|
+
/** for an absent value ("Unassigned", "None") */
|
|
241
|
+
muted?: boolean;
|
|
242
|
+
tooltip?: string;
|
|
243
|
+
}) => (
|
|
244
|
+
<ReqoreSpan size={RAIL_SIZE} tooltip={tooltip} effect={muted ? { opacity: 0.5 } : undefined}>
|
|
245
|
+
{children}
|
|
246
|
+
</ReqoreSpan>
|
|
247
|
+
);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { expect, within } from 'storybook/test';
|
|
3
|
+
import { StoryMeta } from '../../types';
|
|
4
|
+
import { TTicketCategory, TTicketPriority, TTicketStatus } from './meta';
|
|
5
|
+
import { TicketMetaTags } from './TicketMetaTags';
|
|
6
|
+
|
|
7
|
+
const ticket = (
|
|
8
|
+
status: TTicketStatus,
|
|
9
|
+
priority: TTicketPriority,
|
|
10
|
+
category: TTicketCategory
|
|
11
|
+
): { status: TTicketStatus; priority: TTicketPriority; category: TTicketCategory } => ({
|
|
12
|
+
status,
|
|
13
|
+
priority,
|
|
14
|
+
category,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
component: TicketMetaTags,
|
|
19
|
+
title: 'Components/TicketMetaTags',
|
|
20
|
+
args: {
|
|
21
|
+
ticket: ticket('open', 'high', 'bug'),
|
|
22
|
+
},
|
|
23
|
+
} as StoryMeta<typeof TicketMetaTags>;
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
type Story = StoryObj<typeof meta>;
|
|
27
|
+
|
|
28
|
+
/** The status / priority / category badge row — each value's label, intent and
|
|
29
|
+
* icon come from the shared `supportTicket/meta` vocabularies. */
|
|
30
|
+
export const Default: Story = {
|
|
31
|
+
parameters: {
|
|
32
|
+
docs: {
|
|
33
|
+
description: {
|
|
34
|
+
story:
|
|
35
|
+
"Renders the status, priority, and category badge row, each tag's label, intent, and icon drawn from the shared supportTicket/meta vocabularies.",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
async play({ canvasElement }) {
|
|
40
|
+
const canvas = within(canvasElement);
|
|
41
|
+
await expect(canvas.getByText('Open')).toBeInTheDocument();
|
|
42
|
+
await expect(canvas.getByText('High')).toBeInTheDocument();
|
|
43
|
+
await expect(canvas.getByText('Bug')).toBeInTheDocument();
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The `awaiting_customer` status is the one value whose label is audience-specific.
|
|
49
|
+
* With no `viewerRole` (the staff / neutral default) it reads "Awaiting customer".
|
|
50
|
+
*/
|
|
51
|
+
export const StaffAwaitingLabel: Story = {
|
|
52
|
+
parameters: {
|
|
53
|
+
docs: {
|
|
54
|
+
description: {
|
|
55
|
+
story:
|
|
56
|
+
"Renders the awaiting_customer status with no viewerRole (the staff / neutral default), where it reads 'Awaiting customer'.",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
args: { ticket: ticket('awaiting_customer', 'normal', 'question') },
|
|
61
|
+
async play({ canvasElement }) {
|
|
62
|
+
const canvas = within(canvasElement);
|
|
63
|
+
await expect(canvas.getByText('Awaiting customer')).toBeInTheDocument();
|
|
64
|
+
await expect(canvas.queryByText('Awaiting your reply')).not.toBeInTheDocument();
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** The customer viewpoint re-labels the same status as "Awaiting your reply". */
|
|
69
|
+
export const CustomerAwaitingLabel: Story = {
|
|
70
|
+
parameters: {
|
|
71
|
+
docs: {
|
|
72
|
+
description: {
|
|
73
|
+
story:
|
|
74
|
+
"Renders the same awaiting_customer status from the customer viewpoint, where it is re-labelled 'Awaiting your reply'.",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
args: { viewerRole: 'customer', ticket: ticket('awaiting_customer', 'normal', 'question') },
|
|
79
|
+
async play({ canvasElement }) {
|
|
80
|
+
const canvas = within(canvasElement);
|
|
81
|
+
await expect(canvas.getByText('Awaiting your reply')).toBeInTheDocument();
|
|
82
|
+
await expect(canvas.queryByText('Awaiting customer')).not.toBeInTheDocument();
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/** `hideCategory` drops the category tag — for dense list rows. */
|
|
87
|
+
export const HideCategory: Story = {
|
|
88
|
+
parameters: {
|
|
89
|
+
docs: {
|
|
90
|
+
description: {
|
|
91
|
+
story:
|
|
92
|
+
'Renders the badge row with hideCategory set, dropping the category tag for dense list rows while status and priority remain.',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
args: { hideCategory: true },
|
|
97
|
+
async play({ canvasElement }) {
|
|
98
|
+
const canvas = within(canvasElement);
|
|
99
|
+
await expect(canvas.getByText('Open')).toBeInTheDocument();
|
|
100
|
+
await expect(canvas.getByText('High')).toBeInTheDocument();
|
|
101
|
+
await expect(canvas.queryByText('Bug')).not.toBeInTheDocument();
|
|
102
|
+
},
|
|
103
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ReqoreControlGroup, ReqoreTag } from '@qoretechnologies/reqore';
|
|
2
|
+
import { TSizes } from '@qoretechnologies/reqore/dist/constants/sizes';
|
|
3
|
+
import {
|
|
4
|
+
TICKET_CATEGORY_META,
|
|
5
|
+
TICKET_PRIORITY_META,
|
|
6
|
+
TICKET_STATUS_META,
|
|
7
|
+
ticketStatusLabel,
|
|
8
|
+
TTicketCategory,
|
|
9
|
+
TTicketPriority,
|
|
10
|
+
TTicketStatus,
|
|
11
|
+
TTicketViewerRole,
|
|
12
|
+
} from './meta';
|
|
13
|
+
|
|
14
|
+
export interface ITicketMetaTagsProps {
|
|
15
|
+
/** The three enum values to badge — a full ticket satisfies this structurally. */
|
|
16
|
+
ticket: {
|
|
17
|
+
status: TTicketStatus;
|
|
18
|
+
priority: TTicketPriority;
|
|
19
|
+
category: TTicketCategory;
|
|
20
|
+
};
|
|
21
|
+
/** Picks audience-specific labels (e.g. "Awaiting your reply" vs "Awaiting
|
|
22
|
+
* customer"). Omit for the staff / neutral wording. */
|
|
23
|
+
viewerRole?: TTicketViewerRole;
|
|
24
|
+
size?: TSizes;
|
|
25
|
+
/** Hide the category tag (e.g. in a dense list row). */
|
|
26
|
+
hideCategory?: boolean;
|
|
27
|
+
/** Reqore's subtle tag style — the intent colour as text on a faint wash,
|
|
28
|
+
* instead of a solid intent fill. Defaults to `true`; pass `false` for the
|
|
29
|
+
* old solid chips. */
|
|
30
|
+
minimal?: boolean;
|
|
31
|
+
/** Whether the tags are borderless (Reqore's `flat`). Defaults to `false` here,
|
|
32
|
+
* so tags carry a border — a shade lighter than the fill — which keeps the
|
|
33
|
+
* `minimal` tags legible. Pass `true` for no border. */
|
|
34
|
+
flat?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The status / priority / category badge row for a ticket — the single place the
|
|
39
|
+
* three enum vocabularies (`../supportTicket/meta`) turn into Reqore tags, so the
|
|
40
|
+
* customer list, the staff queue, the detail header and the Qonsole card all read
|
|
41
|
+
* identically. Each tag's label, intent and icon come from the shared meta maps.
|
|
42
|
+
*/
|
|
43
|
+
export const TicketMetaTags = ({
|
|
44
|
+
ticket,
|
|
45
|
+
viewerRole,
|
|
46
|
+
size = 'small',
|
|
47
|
+
hideCategory,
|
|
48
|
+
minimal = true,
|
|
49
|
+
flat = false,
|
|
50
|
+
}: ITicketMetaTagsProps) => {
|
|
51
|
+
const status = TICKET_STATUS_META[ticket.status];
|
|
52
|
+
const priority = TICKET_PRIORITY_META[ticket.priority];
|
|
53
|
+
const category = TICKET_CATEGORY_META[ticket.category];
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<ReqoreControlGroup size={size} wrap>
|
|
57
|
+
<ReqoreTag
|
|
58
|
+
icon={status.icon}
|
|
59
|
+
intent={status.intent}
|
|
60
|
+
label={ticketStatusLabel(ticket.status, viewerRole)}
|
|
61
|
+
size={size}
|
|
62
|
+
minimal={minimal}
|
|
63
|
+
flat={flat}
|
|
64
|
+
effect={{ weight: 'bold' }}
|
|
65
|
+
/>
|
|
66
|
+
<ReqoreTag
|
|
67
|
+
icon={priority.icon}
|
|
68
|
+
intent={priority.intent}
|
|
69
|
+
label={priority.label}
|
|
70
|
+
size={size}
|
|
71
|
+
minimal={minimal}
|
|
72
|
+
flat={flat}
|
|
73
|
+
/>
|
|
74
|
+
{!hideCategory && (
|
|
75
|
+
<ReqoreTag
|
|
76
|
+
icon={category.icon}
|
|
77
|
+
label={category.label}
|
|
78
|
+
size={size}
|
|
79
|
+
minimal={minimal}
|
|
80
|
+
flat={flat}
|
|
81
|
+
/>
|
|
82
|
+
)}
|
|
83
|
+
</ReqoreControlGroup>
|
|
84
|
+
);
|
|
85
|
+
};
|