@faststore/core 4.4.0 → 4.5.0-dev.0

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 (39) hide show
  1. package/.turbo/turbo-generate.log +4 -4
  2. package/.turbo/turbo-test.log +63 -50
  3. package/@generated/cached-operations.json +2 -0
  4. package/@generated/gql.ts +12 -0
  5. package/@generated/graphql.ts +73 -0
  6. package/@generated/persisted-documents.json +2 -0
  7. package/@generated/schema.graphql +59 -0
  8. package/CHANGELOG.md +10 -0
  9. package/package.json +6 -6
  10. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawer.tsx +5 -1
  11. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerBody.tsx +16 -6
  12. package/src/components/account/Layout/Layout.tsx +14 -5
  13. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotes.tsx +170 -0
  14. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountListQuotesTable.tsx +194 -0
  15. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountQuoteStatusBadge.tsx +15 -0
  16. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/MyAccountQuotesFilterSlider.tsx +162 -0
  17. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/section.module.scss +49 -0
  18. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesSelectedTags/MyAccountQuotesSelectedTags.tsx +125 -0
  19. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector.tsx +107 -0
  20. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/styles.module.scss +144 -0
  21. package/src/components/account/quotes/MyAccountListQuotes/index.ts +2 -0
  22. package/src/components/account/quotes/MyAccountListQuotes/quoteFilters.ts +89 -0
  23. package/src/components/account/quotes/MyAccountListQuotes/styles.module.scss +174 -0
  24. package/src/customizations/src/myAccount/extensions/quotes/after.tsx +5 -0
  25. package/src/customizations/src/myAccount/extensions/quotes/before.tsx +5 -0
  26. package/src/pages/pvt/account/quotes/index.tsx +264 -0
  27. package/src/sdk/account/getMyAccountRoutes.ts +7 -0
  28. package/src/utils/quoteStatus.ts +27 -0
  29. package/test/components/account/Layout.test.tsx +62 -0
  30. package/test/components/account/OrganizationDrawerBody.test.tsx +52 -0
  31. package/test/components/quotes/MyAccountListQuotes.test.tsx +232 -0
  32. package/test/components/quotes/MyAccountListQuotesTable.test.tsx +240 -0
  33. package/test/components/quotes/MyAccountQuoteStatusBadge.test.tsx +28 -0
  34. package/test/components/quotes/MyAccountQuotesFilterSlider.test.tsx +152 -0
  35. package/test/components/quotes/MyAccountQuotesSelectedTags.test.tsx +122 -0
  36. package/test/components/quotes/MyAccountQuotesStatusSelector.test.tsx +85 -0
  37. package/test/components/quotes/quoteFilters.test.ts +185 -0
  38. package/test/sdk/account/getMyAccountRoutes.test.ts +90 -0
  39. package/test/server/index.test.ts +2 -0
@@ -0,0 +1,125 @@
1
+ import { Button } from '@faststore/ui'
2
+ import { useSession } from 'src/sdk/session'
3
+ import { quoteStatusMap, type QuoteStatusKey } from 'src/utils/quoteStatus'
4
+ import { formatFilterDate } from '../quoteFilters'
5
+
6
+ type MyAccountQuotesSelectedTagsProps = Readonly<{
7
+ filters: {
8
+ status?: string[]
9
+ createdAtFrom?: string
10
+ createdAtTo?: string
11
+ expiresAtFrom?: string
12
+ expiresAtTo?: string
13
+ }
14
+ onClearAll: () => void
15
+ onRemoveFilter: (
16
+ key:
17
+ | 'status'
18
+ | 'createdAtFrom'
19
+ | 'createdAtTo'
20
+ | 'expiresAtFrom'
21
+ | 'expiresAtTo',
22
+ value: string
23
+ ) => void
24
+ }>
25
+
26
+ function formatDateRangeLabel(
27
+ from: string | undefined,
28
+ to: string | undefined,
29
+ locale: string
30
+ ) {
31
+ if (from && to) {
32
+ return `${formatFilterDate(from, locale)} to ${formatFilterDate(to, locale)}`
33
+ }
34
+ if (from) {
35
+ return `from ${formatFilterDate(from, locale)}`
36
+ }
37
+ return `to ${formatFilterDate(to!, locale)}`
38
+ }
39
+
40
+ function Tags({
41
+ filters,
42
+ onRemoveFilter,
43
+ }: Pick<MyAccountQuotesSelectedTagsProps, 'filters' | 'onRemoveFilter'>) {
44
+ const { locale } = useSession()
45
+ const { createdAtFrom, createdAtTo, expiresAtFrom, expiresAtTo, status } =
46
+ filters
47
+
48
+ const createdTag = (createdAtFrom || createdAtTo) && (
49
+ <div key="created-date-range" data-fs-list-orders-selected-tag>
50
+ <span data-fs-list-orders-selected-tag-label>
51
+ {`Created: ${formatDateRangeLabel(createdAtFrom, createdAtTo, locale)}`}
52
+ </span>
53
+ <button
54
+ data-fs-list-orders-selected-tag-clear
55
+ onClick={() => onRemoveFilter('createdAtFrom', createdAtFrom ?? '')}
56
+ >
57
+ &times;
58
+ </button>
59
+ </div>
60
+ )
61
+
62
+ const expiresTag = (expiresAtFrom || expiresAtTo) && (
63
+ <div key="expires-date-range" data-fs-list-orders-selected-tag>
64
+ <span data-fs-list-orders-selected-tag-label>
65
+ {`Expires: ${formatDateRangeLabel(expiresAtFrom, expiresAtTo, locale)}`}
66
+ </span>
67
+ <button
68
+ data-fs-list-orders-selected-tag-clear
69
+ onClick={() => onRemoveFilter('expiresAtFrom', expiresAtFrom ?? '')}
70
+ >
71
+ &times;
72
+ </button>
73
+ </div>
74
+ )
75
+
76
+ const statusTags = (status || []).map((value) => (
77
+ <div key={`status-${value}`} data-fs-list-orders-selected-tag>
78
+ <span>{quoteStatusMap[value as QuoteStatusKey]?.label ?? value}</span>
79
+ <button
80
+ data-fs-list-orders-selected-tag-clear
81
+ onClick={() => onRemoveFilter('status', value)}
82
+ >
83
+ &times;
84
+ </button>
85
+ </div>
86
+ ))
87
+
88
+ return (
89
+ <>
90
+ {createdTag}
91
+ {expiresTag}
92
+ {statusTags}
93
+ </>
94
+ )
95
+ }
96
+
97
+ function MyAccountQuotesSelectedTags({
98
+ filters,
99
+ onClearAll,
100
+ onRemoveFilter,
101
+ }: MyAccountQuotesSelectedTagsProps) {
102
+ const hasFilters = Object.entries(filters).some(([, values]) =>
103
+ Array.isArray(values) ? values.length > 0 : Boolean(values)
104
+ )
105
+
106
+ return (
107
+ <>
108
+ {hasFilters && (
109
+ <div data-fs-list-orders-selected-tags>
110
+ <Tags filters={filters} onRemoveFilter={onRemoveFilter} />
111
+ <Button
112
+ variant="tertiary"
113
+ size="small"
114
+ data-fs-list-orders-selected-tags-clear-all-button
115
+ onClick={onClearAll}
116
+ >
117
+ Clear All
118
+ </Button>
119
+ </div>
120
+ )}
121
+ </>
122
+ )
123
+ }
124
+
125
+ export default MyAccountQuotesSelectedTags
@@ -0,0 +1,107 @@
1
+ import { useEffect, useId, useRef, useState } from 'react'
2
+
3
+ import { quoteStatusMap } from 'src/utils/quoteStatus'
4
+ import styles from './styles.module.scss'
5
+
6
+ type MyAccountQuotesStatusSelectorProps = Readonly<{
7
+ value: string[]
8
+ onChange: (selected: string[]) => void
9
+ }>
10
+
11
+ const statusEntries = Object.entries(quoteStatusMap).map(([key, entry]) => ({
12
+ key,
13
+ label: entry.label,
14
+ variant: entry.variant,
15
+ }))
16
+
17
+ export default function MyAccountQuotesStatusSelector({
18
+ value,
19
+ onChange,
20
+ }: MyAccountQuotesStatusSelectorProps) {
21
+ const [open, setOpen] = useState(false)
22
+ const ref = useRef<HTMLDivElement>(null)
23
+ const labelId = useId()
24
+
25
+ useEffect(() => {
26
+ function handleClickOutside(e: MouseEvent) {
27
+ if (ref.current && !ref.current.contains(e.target as Node)) {
28
+ setOpen(false)
29
+ }
30
+ }
31
+ document.addEventListener('mousedown', handleClickOutside)
32
+ return () => document.removeEventListener('mousedown', handleClickOutside)
33
+ }, [])
34
+
35
+ const toggle = (key: string) => {
36
+ const next = value.includes(key)
37
+ ? value.filter((v) => v !== key)
38
+ : [...value, key]
39
+ onChange(next)
40
+ }
41
+
42
+ const remove = (key: string, e: React.MouseEvent) => {
43
+ e.stopPropagation()
44
+ onChange(value.filter((v) => v !== key))
45
+ }
46
+
47
+ return (
48
+ <div className={styles.wrapper} ref={ref} data-fs-quotes-status-selector>
49
+ <span id={labelId} className={styles.label}>
50
+ Status
51
+ </span>
52
+ <div data-fs-quotes-status-input-wrapper>
53
+ <div className={styles.input} data-open={open || undefined}>
54
+ {value.map((key) => {
55
+ const entry = statusEntries.find((e) => e.key === key)
56
+ return (
57
+ <span
58
+ key={key}
59
+ className={styles.chip}
60
+ data-variant={entry?.variant ?? 'neutral'}
61
+ >
62
+ {entry?.label ?? key}
63
+ <button
64
+ type="button"
65
+ className={styles.chipRemove}
66
+ onClick={(e) => remove(key, e)}
67
+ aria-label={`Remove ${entry?.label ?? key}`}
68
+ >
69
+ &times;
70
+ </button>
71
+ </span>
72
+ )
73
+ })}
74
+ <button
75
+ type="button"
76
+ className={styles.toggle}
77
+ aria-labelledby={labelId}
78
+ aria-expanded={open}
79
+ aria-controls="status-listbox"
80
+ onClick={() => setOpen((o) => !o)}
81
+ >
82
+ <span aria-hidden="true">▾</span>
83
+ </button>
84
+ </div>
85
+ {open && (
86
+ <div id="status-listbox" className={styles.dropdown} tabIndex={-1}>
87
+ {statusEntries.map(({ key, label }) => {
88
+ const id = `status-opt-${key}`
89
+ const checked = value.includes(key)
90
+ return (
91
+ <div key={key} className={styles.option}>
92
+ <input
93
+ type="checkbox"
94
+ id={id}
95
+ checked={checked}
96
+ onChange={() => toggle(key)}
97
+ />
98
+ <label htmlFor={id}>{label}</label>
99
+ </div>
100
+ )
101
+ })}
102
+ </div>
103
+ )}
104
+ </div>
105
+ </div>
106
+ )
107
+ }
@@ -0,0 +1,144 @@
1
+ @use "@faststore/ui/src/styles/base/utilities";
2
+
3
+ .wrapper {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: var(--fs-spacing-1);
7
+ min-width: 220px;
8
+
9
+ [data-fs-quotes-status-input-wrapper] {
10
+ position: relative;
11
+ }
12
+ }
13
+
14
+ .label {
15
+ font-size: var(--fs-text-size-2);
16
+ font-weight: var(--fs-text-weight-medium);
17
+ color: var(--fs-color-text);
18
+ }
19
+
20
+ .input {
21
+ display: flex;
22
+ flex-wrap: wrap;
23
+ gap: var(--fs-spacing-1);
24
+ align-items: center;
25
+ min-height: 2.5rem;
26
+ padding: var(--fs-spacing-1) var(--fs-spacing-2);
27
+ background-color: var(--fs-color-neutral-0);
28
+ border: 1px solid var(--fs-border-color);
29
+ border-radius: var(--fs-border-radius);
30
+
31
+ &[data-open] {
32
+ border-color: var(--fs-color-focus-ring);
33
+ box-shadow: 0 0 0 2px var(--fs-color-focus-ring-outline);
34
+ }
35
+ }
36
+
37
+ .chip {
38
+ display: inline-flex;
39
+ gap: var(--fs-spacing-0);
40
+ align-items: center;
41
+ padding: var(--fs-spacing-0) var(--fs-spacing-2);
42
+ font-size: var(--fs-text-size-1);
43
+ color: var(--fs-color-text);
44
+ background-color: var(--fs-color-neutral-2);
45
+ border-radius: var(--fs-border-radius-pill);
46
+
47
+ &[data-variant="success"] {
48
+ background-color: #e9fce3;
49
+ }
50
+
51
+ &[data-variant="warning"] {
52
+ background-color: #ffedcd;
53
+ }
54
+
55
+ &[data-variant="info"] {
56
+ background-color: #d0f0e8;
57
+ }
58
+
59
+ &[data-variant="danger"] {
60
+ background-color: #fce3e3;
61
+ }
62
+
63
+ &[data-variant="neutral"] {
64
+ background-color: #f5f5f5;
65
+ }
66
+ }
67
+
68
+ .toggle {
69
+ display: inline-flex;
70
+ flex-shrink: 0;
71
+ align-items: center;
72
+ justify-content: center;
73
+ padding: 0 var(--fs-spacing-1);
74
+ margin-left: auto;
75
+ color: var(--fs-color-text);
76
+ cursor: pointer;
77
+ background: none;
78
+ border: none;
79
+ outline: none;
80
+
81
+ &:focus-visible {
82
+ outline: 2px solid var(--fs-color-focus-ring);
83
+ }
84
+ }
85
+
86
+ .chipRemove {
87
+ display: inline-flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ padding: 0;
91
+ margin-left: var(--fs-spacing-0);
92
+ font-size: var(--fs-text-size-2);
93
+ line-height: 1;
94
+ color: var(--fs-color-text);
95
+ cursor: pointer;
96
+ background: none;
97
+ border: none;
98
+
99
+ &:hover {
100
+ opacity: 0.6;
101
+ }
102
+ }
103
+
104
+ .dropdown {
105
+ position: absolute;
106
+ top: 100%;
107
+ right: 0;
108
+ left: 0;
109
+ z-index: 100;
110
+ padding: var(--fs-spacing-1) 0;
111
+ margin: 0;
112
+ list-style: none;
113
+ background-color: var(--fs-color-neutral-0);
114
+ border: 1px solid var(--fs-border-color-light);
115
+ border-top: none;
116
+ border-radius: 0 0 var(--fs-border-radius) var(--fs-border-radius);
117
+ box-shadow: 0 4px 8px rgb(0 0 0 / 8%);
118
+ }
119
+
120
+ .option {
121
+ display: flex;
122
+ gap: var(--fs-spacing-2);
123
+ align-items: center;
124
+ padding: var(--fs-spacing-2) var(--fs-spacing-3);
125
+ cursor: pointer;
126
+
127
+ &:hover {
128
+ background-color: var(--fs-color-neutral-1);
129
+ }
130
+
131
+ input[type="checkbox"] {
132
+ flex-shrink: 0;
133
+ width: 1.125rem;
134
+ height: 1.125rem;
135
+ cursor: pointer;
136
+ accent-color: var(--fs-color-primary-bkg);
137
+ }
138
+
139
+ label {
140
+ font-size: var(--fs-text-size-body);
141
+ color: var(--fs-color-text);
142
+ cursor: pointer;
143
+ }
144
+ }
@@ -0,0 +1,2 @@
1
+ export { default as MyAccountListQuotes } from './MyAccountListQuotes'
2
+ export type { MyAccountListQuotesProps } from './MyAccountListQuotes'
@@ -0,0 +1,89 @@
1
+ import { quoteStatusMap } from 'src/utils/quoteStatus'
2
+
3
+ export type QuoteFilters = {
4
+ page: number
5
+ status: string[]
6
+ createdAtFrom: string
7
+ createdAtTo: string
8
+ expiresAtFrom: string
9
+ expiresAtTo: string
10
+ label: string
11
+ }
12
+
13
+ export type QuoteSelectedFacet = { key: string; value: string }
14
+
15
+ export function getSelectedFacets(filters: QuoteFilters): QuoteSelectedFacet[] {
16
+ const facets: QuoteSelectedFacet[] = []
17
+ if (filters.status.length > 0) {
18
+ filters.status.forEach((s) => facets.push({ key: 'status', value: s }))
19
+ }
20
+ return facets
21
+ }
22
+
23
+ export function getAllFacets(filters: QuoteFilters) {
24
+ return [
25
+ {
26
+ __typename: 'StoreFacetBoolean' as const,
27
+ key: 'status',
28
+ label: 'Status',
29
+ values: Object.entries(quoteStatusMap).map(([key, { label }]) => ({
30
+ label,
31
+ quantity: 0,
32
+ selected: false,
33
+ value: key,
34
+ })),
35
+ },
36
+ {
37
+ __typename: 'StoreFacetRange' as const,
38
+ key: 'createdAt',
39
+ label: 'Created Date',
40
+ from: filters.createdAtFrom,
41
+ to: filters.createdAtTo,
42
+ },
43
+ {
44
+ __typename: 'StoreFacetRange' as const,
45
+ key: 'expiresAt',
46
+ label: 'Expiry Date',
47
+ from: filters.expiresAtFrom,
48
+ to: filters.expiresAtTo,
49
+ },
50
+ ]
51
+ }
52
+
53
+ export function hasActiveFilters(filters: QuoteFilters): boolean {
54
+ return (
55
+ filters.status.length > 0 ||
56
+ Boolean(filters.createdAtFrom) ||
57
+ Boolean(filters.createdAtTo) ||
58
+ Boolean(filters.expiresAtFrom) ||
59
+ Boolean(filters.expiresAtTo) ||
60
+ Boolean(filters.label)
61
+ )
62
+ }
63
+
64
+ export function countActiveFilters(filters: QuoteFilters): number {
65
+ let count = 0
66
+ if (filters.status.length > 0) count++
67
+ if (filters.createdAtFrom || filters.createdAtTo) count++
68
+ if (filters.expiresAtFrom || filters.expiresAtTo) count++
69
+ return count
70
+ }
71
+
72
+ export function formatFilterDate(date: string, locale: string): string {
73
+ if (!date || !/^\d{4}-\d{2}-\d{2}$/.test(date)) return date
74
+ const [year, month, day] = date.split('-').map(Number)
75
+ if (!year || !month || !day) return date
76
+ const parsed = new Date(year, month - 1, day)
77
+ if (
78
+ parsed.getFullYear() !== year ||
79
+ parsed.getMonth() !== month - 1 ||
80
+ parsed.getDate() !== day
81
+ ) {
82
+ return date
83
+ }
84
+ return parsed.toLocaleDateString(locale, {
85
+ year: 'numeric',
86
+ month: '2-digit',
87
+ day: '2-digit',
88
+ })
89
+ }
@@ -0,0 +1,174 @@
1
+ @use "sass:meta";
2
+ @use "@faststore/ui/src/styles/base/utilities";
3
+
4
+ .page {
5
+ @include meta.load-css("../../components/StatusBadge/styles.scss");
6
+ @include meta.load-css("~@faststore/ui/src/components/atoms/Button/styles.scss");
7
+ @include meta.load-css("~@faststore/ui/src/components/molecules/SearchInputField/styles.scss");
8
+ @include meta.load-css("~@faststore/ui/src/components/organisms/EmptyState/styles.scss");
9
+
10
+ // --------------------------------------------------------
11
+ // Design Tokens
12
+ // --------------------------------------------------------
13
+
14
+ --fs-page-height: 100%;
15
+ --fs-quotes-list-border-color: var(--fs-border-color-light);
16
+ --fs-quotes-list-row-hover-bg: var(--fs-color-neutral-1);
17
+ --fs-quotes-list-id-size: var(--fs-text-size-base);
18
+ --fs-quotes-list-label-size: var(--fs-text-size-2);
19
+ --fs-quotes-list-label-color: var(--fs-color-neutral-6);
20
+ --fs-quotes-list-date-label-size: var(--fs-text-size-tiny);
21
+ --fs-quotes-list-date-label-color: var(--fs-color-neutral-6);
22
+ --fs-quotes-list-date-value-size: var(--fs-text-size-2);
23
+ --fs-quotes-list-relative-expiry-color: var(--fs-color-neutral-5);
24
+ --fs-quotes-list-total-size: var(--fs-text-size-2);
25
+ --fs-quotes-list-total-color: var(--fs-color-neutral-6);
26
+ --fs-quotes-list-filter-margin-bottom: var(--fs-spacing-3);
27
+
28
+ height: var(--fs-page-height);
29
+
30
+ // --------------------------------------------------------
31
+ // Controls bar (filter button + pagination)
32
+ // --------------------------------------------------------
33
+
34
+ [data-fs-list-quotes-controls] {
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: space-between;
38
+ margin-bottom: var(--fs-quotes-list-filter-margin-bottom);
39
+ }
40
+
41
+ [data-fs-list-quotes-search-filters] {
42
+ display: flex;
43
+ flex: 1;
44
+ gap: var(--fs-spacing-3);
45
+ align-items: center;
46
+ max-width: 55%;
47
+
48
+ [data-fs-search-input-field-wrapper] {
49
+ flex: 1;
50
+ }
51
+ }
52
+
53
+ [data-fs-filter-count] {
54
+ display: inline-flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ width: 1.25rem;
58
+ height: 1.25rem;
59
+ margin-left: var(--fs-spacing-1);
60
+ font-size: var(--fs-text-size-tiny);
61
+ font-weight: var(--fs-text-weight-bold);
62
+ line-height: 1;
63
+ color: var(--fs-color-primary-text);
64
+ background-color: var(--fs-color-primary-bkg);
65
+ border-radius: 50%;
66
+ }
67
+
68
+ // --------------------------------------------------------
69
+ // Quotes list
70
+ // --------------------------------------------------------
71
+
72
+ [data-fs-quotes-list] {
73
+ width: 100%;
74
+ }
75
+
76
+ [data-fs-quotes-list-row] {
77
+ display: flex;
78
+ gap: var(--fs-spacing-4);
79
+ align-items: flex-start;
80
+ padding: var(--fs-spacing-4) 0;
81
+ border-bottom: 1px solid var(--fs-quotes-list-border-color);
82
+ transition: background-color 0.2s ease;
83
+
84
+ &:last-child {
85
+ border-bottom: none;
86
+ }
87
+
88
+ &:hover {
89
+ background-color: var(--fs-quotes-list-row-hover-bg);
90
+ }
91
+ }
92
+
93
+ // Left column: ID + label
94
+ [data-fs-quotes-list-col-main] {
95
+ display: flex;
96
+ flex: 1 1 35%;
97
+ flex-direction: column;
98
+ gap: var(--fs-spacing-1);
99
+ min-width: 0;
100
+ }
101
+
102
+ [data-fs-quotes-list-id] {
103
+ overflow: hidden;
104
+ font-size: var(--fs-quotes-list-id-size);
105
+ font-weight: var(--fs-text-weight-bold);
106
+ text-overflow: ellipsis;
107
+ white-space: nowrap;
108
+ }
109
+
110
+ [data-fs-quotes-list-label] {
111
+ overflow: hidden;
112
+ font-size: var(--fs-quotes-list-label-size);
113
+ color: var(--fs-quotes-list-label-color);
114
+ text-overflow: ellipsis;
115
+ white-space: nowrap;
116
+ }
117
+
118
+ // Middle column: created by + dates
119
+ [data-fs-quotes-list-col-dates] {
120
+ display: flex;
121
+ flex: 1 1 40%;
122
+ flex-direction: column;
123
+ gap: var(--fs-spacing-2);
124
+ min-width: 0;
125
+ }
126
+
127
+ [data-fs-quotes-list-date-group] {
128
+ display: flex;
129
+ flex-direction: column;
130
+ gap: var(--fs-spacing-0);
131
+ }
132
+
133
+ [data-fs-quotes-list-date-label] {
134
+ font-size: var(--fs-quotes-list-date-label-size);
135
+ font-weight: var(--fs-text-weight-medium);
136
+ color: var(--fs-quotes-list-date-label-color);
137
+ }
138
+
139
+ [data-fs-quotes-list-date-value] {
140
+ font-size: var(--fs-quotes-list-date-value-size);
141
+ }
142
+
143
+ [data-fs-quotes-list-relative-expiry] {
144
+ color: var(--fs-quotes-list-relative-expiry-color);
145
+ }
146
+
147
+ // Right column: badge + total
148
+ [data-fs-quotes-list-col-status] {
149
+ display: flex;
150
+ flex: 0 0 auto;
151
+ flex-direction: column;
152
+ gap: var(--fs-spacing-2);
153
+ align-items: flex-end;
154
+ min-width: 120px;
155
+ }
156
+
157
+ [data-fs-quotes-list-total] {
158
+ font-size: var(--fs-quotes-list-total-size);
159
+ color: var(--fs-quotes-list-total-color);
160
+ text-align: right;
161
+
162
+ strong {
163
+ font-weight: var(--fs-text-weight-bold);
164
+ color: var(--fs-color-text);
165
+ }
166
+ }
167
+
168
+ // --------------------------------------------------------
169
+ // Pagination + selected tags (reuse from orders)
170
+ // --------------------------------------------------------
171
+
172
+ @include meta.load-css("../../orders/ListOrders/ListOrdersTable/styles.scss");
173
+ @include meta.load-css("../../orders/ListOrders/SelectedTags/styles.scss");
174
+ }
@@ -0,0 +1,5 @@
1
+ function After() {
2
+ return <></>
3
+ }
4
+
5
+ export default After
@@ -0,0 +1,5 @@
1
+ function Before() {
2
+ return <></>
3
+ }
4
+
5
+ export default Before