@open-mercato/ui 0.5.1-develop.2953.6647bb2c43 → 0.5.1-develop.2964.d5ac4a6ebb
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +8 -0
- package/dist/backend/CrudForm.js +57 -29
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +32 -14
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/FilterOverlay.js +23 -17
- package/dist/backend/FilterOverlay.js.map +2 -2
- package/dist/backend/JsonBuilder.js +32 -18
- package/dist/backend/JsonBuilder.js.map +2 -2
- package/dist/backend/columns/ColumnChooserPanel.js +12 -13
- package/dist/backend/columns/ColumnChooserPanel.js.map +2 -2
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js +71 -62
- package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +2 -2
- package/dist/backend/date-range/DateRangeSelect.js +11 -10
- package/dist/backend/date-range/DateRangeSelect.js.map +2 -2
- package/dist/backend/date-range/InlineDateRangeSelect.js +10 -22
- package/dist/backend/date-range/InlineDateRangeSelect.js.map +2 -2
- package/dist/backend/detail/ActivitiesSection.js +20 -12
- package/dist/backend/detail/ActivitiesSection.js.map +2 -2
- package/dist/backend/detail/AddressEditor.js +24 -7
- package/dist/backend/detail/AddressEditor.js.map +2 -2
- package/dist/backend/detail/InlineEditors.js +12 -6
- package/dist/backend/detail/InlineEditors.js.map +2 -2
- package/dist/backend/detail/NotesSection.js +20 -14
- package/dist/backend/detail/NotesSection.js.map +2 -2
- package/dist/backend/filters/AdvancedFilterBuilder.js +52 -24
- package/dist/backend/filters/AdvancedFilterBuilder.js.map +2 -2
- package/dist/backend/injection/InjectedField.js +12 -7
- package/dist/backend/injection/InjectedField.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/inputs/EventSelect.js +22 -6
- package/dist/backend/inputs/EventSelect.js.map +2 -2
- package/dist/backend/inputs/PhoneNumberField.js +2 -2
- package/dist/backend/inputs/PhoneNumberField.js.map +2 -2
- package/dist/backend/inputs/TimeInput.js +9 -10
- package/dist/backend/inputs/TimeInput.js.map +2 -2
- package/dist/backend/messages/message-compose-form-groups.js +12 -7
- package/dist/backend/messages/message-compose-form-groups.js.map +2 -2
- package/dist/backend/messages/useMessageCompose.js +7 -1
- package/dist/backend/messages/useMessageCompose.js.map +2 -2
- package/dist/frontend/LanguageSwitcher.js +19 -14
- package/dist/frontend/LanguageSwitcher.js.map +2 -2
- package/dist/index.js +5 -0
- package/dist/index.js.map +2 -2
- package/dist/primitives/checkbox-field.js +17 -5
- package/dist/primitives/checkbox-field.js.map +2 -2
- package/dist/primitives/input.js +71 -14
- package/dist/primitives/input.js.map +2 -2
- package/dist/primitives/radio-field.js +74 -0
- package/dist/primitives/radio-field.js.map +7 -0
- package/dist/primitives/radio.js +37 -0
- package/dist/primitives/radio.js.map +7 -0
- package/dist/primitives/select.js +155 -0
- package/dist/primitives/select.js.map +7 -0
- package/dist/primitives/switch-field.js +76 -0
- package/dist/primitives/switch-field.js.map +7 -0
- package/dist/primitives/switch.js +17 -3
- package/dist/primitives/switch.js.map +2 -2
- package/dist/primitives/textarea.js +48 -12
- package/dist/primitives/textarea.js.map +2 -2
- package/dist/primitives/tooltip.js +44 -15
- package/dist/primitives/tooltip.js.map +2 -2
- package/package.json +5 -3
- package/src/backend/CrudForm.tsx +104 -37
- package/src/backend/DataTable.tsx +38 -20
- package/src/backend/FilterOverlay.tsx +35 -21
- package/src/backend/JsonBuilder.tsx +38 -20
- package/src/backend/__tests__/FieldDefinitionsEditor.test.tsx +23 -6
- package/src/backend/columns/ColumnChooserPanel.tsx +9 -10
- package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +120 -87
- package/src/backend/date-range/DateRangeSelect.tsx +19 -12
- package/src/backend/date-range/InlineDateRangeSelect.tsx +16 -20
- package/src/backend/detail/ActivitiesSection.tsx +35 -23
- package/src/backend/detail/AddressEditor.tsx +30 -16
- package/src/backend/detail/InlineEditors.tsx +21 -11
- package/src/backend/detail/NotesSection.tsx +35 -25
- package/src/backend/filters/AdvancedFilterBuilder.tsx +60 -34
- package/src/backend/injection/InjectedField.tsx +21 -12
- package/src/backend/inputs/ComboboxInput.tsx +4 -0
- package/src/backend/inputs/EventSelect.tsx +30 -17
- package/src/backend/inputs/PhoneNumberField.tsx +2 -2
- package/src/backend/inputs/TimeInput.tsx +9 -10
- package/src/backend/messages/message-compose-form-groups.tsx +21 -12
- package/src/backend/messages/useMessageCompose.ts +20 -1
- package/src/frontend/LanguageSwitcher.tsx +20 -17
- package/src/index.ts +5 -0
- package/src/primitives/checkbox-field.tsx +10 -2
- package/src/primitives/input.tsx +73 -12
- package/src/primitives/radio-field.tsx +92 -0
- package/src/primitives/radio.tsx +42 -0
- package/src/primitives/select.tsx +200 -0
- package/src/primitives/switch-field.tsx +100 -0
- package/src/primitives/switch.tsx +17 -4
- package/src/primitives/textarea.tsx +67 -11
- package/src/primitives/tooltip.tsx +68 -24
|
@@ -4,6 +4,13 @@ import * as React from 'react'
|
|
|
4
4
|
import Link from 'next/link'
|
|
5
5
|
import { ArrowUpRightSquare, Pencil, Plus, Trash2 } from 'lucide-react'
|
|
6
6
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectTrigger,
|
|
12
|
+
SelectValue,
|
|
13
|
+
} from '@open-mercato/ui/primitives/select'
|
|
7
14
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
8
15
|
import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
9
16
|
import { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'
|
|
@@ -322,17 +329,21 @@ function ActivityForm({
|
|
|
322
329
|
const currentValue =
|
|
323
330
|
typeof value === 'string' && value.length ? value : normalizedEntityOptions[0]?.id ?? ''
|
|
324
331
|
return (
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
onChange={(event) => setValue(event.target.value)}
|
|
332
|
+
<Select
|
|
333
|
+
value={currentValue || undefined}
|
|
334
|
+
onValueChange={(next) => setValue(next ?? '')}
|
|
329
335
|
>
|
|
330
|
-
|
|
331
|
-
<
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
+
<SelectTrigger>
|
|
337
|
+
<SelectValue />
|
|
338
|
+
</SelectTrigger>
|
|
339
|
+
<SelectContent>
|
|
340
|
+
{normalizedEntityOptions.map((option) => (
|
|
341
|
+
<SelectItem key={option.id} value={option.id}>
|
|
342
|
+
{option.label}
|
|
343
|
+
</SelectItem>
|
|
344
|
+
))}
|
|
345
|
+
</SelectContent>
|
|
346
|
+
</Select>
|
|
336
347
|
)
|
|
337
348
|
},
|
|
338
349
|
} as CrudField)
|
|
@@ -347,20 +358,21 @@ function ActivityForm({
|
|
|
347
358
|
component: ({ value, setValue }) => {
|
|
348
359
|
const currentValue = typeof value === 'string' ? value : ''
|
|
349
360
|
return (
|
|
350
|
-
<
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
onChange={(event) => setValue(event.target.value)}
|
|
361
|
+
<Select
|
|
362
|
+
value={currentValue || undefined}
|
|
363
|
+
onValueChange={(next) => setValue(next ?? '')}
|
|
354
364
|
>
|
|
355
|
-
<
|
|
356
|
-
{translate('fields.dealPlaceholder', 'No linked deal')}
|
|
357
|
-
</
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
{option.
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
365
|
+
<SelectTrigger>
|
|
366
|
+
<SelectValue placeholder={translate('fields.dealPlaceholder', 'No linked deal')} />
|
|
367
|
+
</SelectTrigger>
|
|
368
|
+
<SelectContent>
|
|
369
|
+
{normalizedDealOptions.map((option) => (
|
|
370
|
+
<SelectItem key={option.id} value={option.id}>
|
|
371
|
+
{option.label}
|
|
372
|
+
</SelectItem>
|
|
373
|
+
))}
|
|
374
|
+
</SelectContent>
|
|
375
|
+
</Select>
|
|
364
376
|
)
|
|
365
377
|
},
|
|
366
378
|
} as CrudField)
|
|
@@ -6,6 +6,13 @@ import { usePathname, useSearchParams } from 'next/navigation'
|
|
|
6
6
|
import { Plus, Settings } from 'lucide-react'
|
|
7
7
|
import { Button } from '../../primitives/button'
|
|
8
8
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
9
|
+
import {
|
|
10
|
+
Select,
|
|
11
|
+
SelectContent,
|
|
12
|
+
SelectItem,
|
|
13
|
+
SelectTrigger,
|
|
14
|
+
SelectValue,
|
|
15
|
+
} from '@open-mercato/ui/primitives/select'
|
|
9
16
|
import {
|
|
10
17
|
Dialog,
|
|
11
18
|
DialogContent,
|
|
@@ -232,24 +239,31 @@ export function AddressEditor<C = unknown>({
|
|
|
232
239
|
aria-invalid={errors.name ? 'true' : undefined}
|
|
233
240
|
/>
|
|
234
241
|
<div className="flex gap-2">
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
onChange={(evt) => update('purpose', evt.target.value)}
|
|
242
|
+
<Select
|
|
243
|
+
value={current.purpose || undefined}
|
|
244
|
+
onValueChange={(next) => update('purpose', next ?? '')}
|
|
239
245
|
disabled={disabled}
|
|
240
|
-
aria-invalid={errors.purpose ? 'true' : undefined}
|
|
241
246
|
>
|
|
242
|
-
<
|
|
243
|
-
{
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
<SelectTrigger
|
|
248
|
+
className={errors.purpose ? 'border-destructive' : undefined}
|
|
249
|
+
aria-invalid={errors.purpose ? 'true' : undefined}
|
|
250
|
+
>
|
|
251
|
+
<SelectValue
|
|
252
|
+
placeholder={
|
|
253
|
+
addressTypesLoading
|
|
254
|
+
? label('types.loading', 'Loading…')
|
|
255
|
+
: label('types.placeholder', 'Address type')
|
|
256
|
+
}
|
|
257
|
+
/>
|
|
258
|
+
</SelectTrigger>
|
|
259
|
+
<SelectContent>
|
|
260
|
+
{addressTypes.map((entry) => (
|
|
261
|
+
<SelectItem key={entry.value} value={entry.value}>
|
|
262
|
+
{entry.label}
|
|
263
|
+
</SelectItem>
|
|
264
|
+
))}
|
|
265
|
+
</SelectContent>
|
|
266
|
+
</Select>
|
|
253
267
|
{addressTypesAdapter?.create ? (
|
|
254
268
|
<Dialog open={typeDialogOpen} onOpenChange={setTypeDialogOpen}>
|
|
255
269
|
<DialogTrigger asChild>
|
|
@@ -6,6 +6,13 @@ import { FileCode, Loader2, Mail, Pencil, Phone, X } from 'lucide-react'
|
|
|
6
6
|
import type { PluggableList } from 'unified'
|
|
7
7
|
import { PhoneNumberField } from '@open-mercato/ui/backend/inputs/PhoneNumberField'
|
|
8
8
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
9
|
+
import {
|
|
10
|
+
Select,
|
|
11
|
+
SelectContent,
|
|
12
|
+
SelectItem,
|
|
13
|
+
SelectTrigger,
|
|
14
|
+
SelectValue,
|
|
15
|
+
} from '@open-mercato/ui/primitives/select'
|
|
9
16
|
import { Textarea } from '@open-mercato/ui/primitives/textarea'
|
|
10
17
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
11
18
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
@@ -826,18 +833,21 @@ export function InlineSelectEditor({
|
|
|
826
833
|
{renderEditor ? (
|
|
827
834
|
renderEditor({ value: draft, onChange: setDraft })
|
|
828
835
|
) : (
|
|
829
|
-
<
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
onChange={(event) => setDraft(event.target.value)}
|
|
836
|
+
<Select
|
|
837
|
+
value={draft || undefined}
|
|
838
|
+
onValueChange={(next) => setDraft(next ?? '')}
|
|
833
839
|
>
|
|
834
|
-
<
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
840
|
+
<SelectTrigger>
|
|
841
|
+
<SelectValue placeholder={t('ui.detail.inline.select.placeholder', 'Not set')} />
|
|
842
|
+
</SelectTrigger>
|
|
843
|
+
<SelectContent>
|
|
844
|
+
{options.map((option) => (
|
|
845
|
+
<SelectItem key={option.value} value={option.value}>
|
|
846
|
+
{option.label}
|
|
847
|
+
</SelectItem>
|
|
848
|
+
))}
|
|
849
|
+
</SelectContent>
|
|
850
|
+
</Select>
|
|
841
851
|
)}
|
|
842
852
|
<div className="flex items-center gap-2">
|
|
843
853
|
<Button type="button" size="sm" onClick={() => void handleSave()} disabled={saving}>
|
|
@@ -8,6 +8,13 @@ import type { IconOption } from '@open-mercato/core/modules/dictionaries/compone
|
|
|
8
8
|
import { ArrowUpRightSquare, FileCode, Loader2, Palette, Pencil, Plus, Trash2 } from 'lucide-react'
|
|
9
9
|
import { formatRelativeTime } from '@open-mercato/shared/lib/time'
|
|
10
10
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
11
|
+
import {
|
|
12
|
+
Select,
|
|
13
|
+
SelectContent,
|
|
14
|
+
SelectItem,
|
|
15
|
+
SelectTrigger,
|
|
16
|
+
SelectValue,
|
|
17
|
+
} from '@open-mercato/ui/primitives/select'
|
|
11
18
|
import { flash } from '../FlashMessages'
|
|
12
19
|
import { SwitchableMarkdownInput } from '../inputs/SwitchableMarkdownInput'
|
|
13
20
|
import { ErrorMessage } from './ErrorMessage'
|
|
@@ -1013,19 +1020,22 @@ function NotesSectionImpl<C = unknown>({
|
|
|
1013
1020
|
>
|
|
1014
1021
|
{label('fields.entity', 'Assign to customer')}
|
|
1015
1022
|
</label>
|
|
1016
|
-
<
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
value={selectedEntityId}
|
|
1020
|
-
onChange={(event) => setSelectedEntityId(event.target.value)}
|
|
1023
|
+
<Select
|
|
1024
|
+
value={selectedEntityId || undefined}
|
|
1025
|
+
onValueChange={(next) => setSelectedEntityId(next ?? '')}
|
|
1021
1026
|
disabled={isSubmitting || isLoading || !normalizedEntityOptions.length}
|
|
1022
1027
|
>
|
|
1023
|
-
|
|
1024
|
-
<
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1028
|
+
<SelectTrigger id="note-entity-select">
|
|
1029
|
+
<SelectValue />
|
|
1030
|
+
</SelectTrigger>
|
|
1031
|
+
<SelectContent>
|
|
1032
|
+
{normalizedEntityOptions.map((option) => (
|
|
1033
|
+
<SelectItem key={option.id} value={option.id}>
|
|
1034
|
+
{option.label}
|
|
1035
|
+
</SelectItem>
|
|
1036
|
+
))}
|
|
1037
|
+
</SelectContent>
|
|
1038
|
+
</Select>
|
|
1029
1039
|
</div>
|
|
1030
1040
|
) : null}
|
|
1031
1041
|
{normalizedDealOptions.length ? (
|
|
@@ -1036,22 +1046,22 @@ function NotesSectionImpl<C = unknown>({
|
|
|
1036
1046
|
>
|
|
1037
1047
|
{label('fields.deal', 'Link to deal (optional)')}
|
|
1038
1048
|
</label>
|
|
1039
|
-
<
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
value={selectedDealId}
|
|
1043
|
-
onChange={(event) => setSelectedDealId(event.target.value)}
|
|
1049
|
+
<Select
|
|
1050
|
+
value={selectedDealId || undefined}
|
|
1051
|
+
onValueChange={(next) => setSelectedDealId(next ?? '')}
|
|
1044
1052
|
disabled={isSubmitting || isLoading}
|
|
1045
1053
|
>
|
|
1046
|
-
<
|
|
1047
|
-
{label('fields.dealPlaceholder', 'No linked deal')}
|
|
1048
|
-
</
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
{option.
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1054
|
+
<SelectTrigger id="note-deal-select">
|
|
1055
|
+
<SelectValue placeholder={label('fields.dealPlaceholder', 'No linked deal')} />
|
|
1056
|
+
</SelectTrigger>
|
|
1057
|
+
<SelectContent>
|
|
1058
|
+
{normalizedDealOptions.map((option) => (
|
|
1059
|
+
<SelectItem key={option.id} value={option.id}>
|
|
1060
|
+
{option.label}
|
|
1061
|
+
</SelectItem>
|
|
1062
|
+
))}
|
|
1063
|
+
</SelectContent>
|
|
1064
|
+
</Select>
|
|
1055
1065
|
</div>
|
|
1056
1066
|
) : null}
|
|
1057
1067
|
</div>
|
|
@@ -3,6 +3,14 @@ import * as React from 'react'
|
|
|
3
3
|
import { ChevronDown, Plus, Trash2, X } from 'lucide-react'
|
|
4
4
|
import { Button } from '../../primitives/button'
|
|
5
5
|
import { IconButton } from '../../primitives/icon-button'
|
|
6
|
+
import { Input } from '../../primitives/input'
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectTrigger,
|
|
12
|
+
SelectValue,
|
|
13
|
+
} from '../../primitives/select'
|
|
6
14
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
7
15
|
import type {
|
|
8
16
|
AdvancedFilterState,
|
|
@@ -114,30 +122,41 @@ function ConditionRow({
|
|
|
114
122
|
)}
|
|
115
123
|
</div>
|
|
116
124
|
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
onChange={(e) => handleFieldChange(e.target.value)}
|
|
121
|
-
aria-label={t('ui.advancedFilter.selectField', 'Select field')}
|
|
125
|
+
<Select
|
|
126
|
+
value={condition.field || undefined}
|
|
127
|
+
onValueChange={(next) => handleFieldChange(next ?? '')}
|
|
122
128
|
>
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
<SelectTrigger
|
|
130
|
+
className="min-w-[140px]"
|
|
131
|
+
aria-label={t('ui.advancedFilter.selectField', 'Select field')}
|
|
132
|
+
>
|
|
133
|
+
<SelectValue placeholder={t('ui.advancedFilter.selectFieldPlaceholder', 'Select field...')} />
|
|
134
|
+
</SelectTrigger>
|
|
135
|
+
<SelectContent>
|
|
136
|
+
{fields.map((f) => (
|
|
137
|
+
<SelectItem key={f.key} value={f.key}>{f.label}</SelectItem>
|
|
138
|
+
))}
|
|
139
|
+
</SelectContent>
|
|
140
|
+
</Select>
|
|
128
141
|
|
|
129
|
-
<
|
|
130
|
-
className="rounded border bg-background px-2 py-1.5 text-sm min-w-[120px]"
|
|
142
|
+
<Select
|
|
131
143
|
value={condition.operator}
|
|
132
|
-
|
|
133
|
-
aria-label={t('ui.advancedFilter.selectOperator', 'Select operator')}
|
|
144
|
+
onValueChange={(next) => onUpdate(condition.id, { operator: next as FilterOperator, value: '' })}
|
|
134
145
|
>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
146
|
+
<SelectTrigger
|
|
147
|
+
className="min-w-[120px]"
|
|
148
|
+
aria-label={t('ui.advancedFilter.selectOperator', 'Select operator')}
|
|
149
|
+
>
|
|
150
|
+
<SelectValue />
|
|
151
|
+
</SelectTrigger>
|
|
152
|
+
<SelectContent>
|
|
153
|
+
{operators.map((op) => (
|
|
154
|
+
<SelectItem key={op} value={op}>
|
|
155
|
+
{t(`ui.advancedFilter.operator.${op}`, OPERATOR_LABELS[op])}
|
|
156
|
+
</SelectItem>
|
|
157
|
+
))}
|
|
158
|
+
</SelectContent>
|
|
159
|
+
</Select>
|
|
141
160
|
|
|
142
161
|
{!valueless ? (
|
|
143
162
|
<ValueInput
|
|
@@ -180,17 +199,22 @@ function ValueInput({
|
|
|
180
199
|
|
|
181
200
|
if (fieldType === 'select' && fieldDef?.options) {
|
|
182
201
|
return (
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
onChange={(e) => onUpdate(condition.id, { value: e.target.value })}
|
|
187
|
-
aria-label={t('ui.advancedFilter.selectValue', 'Select value')}
|
|
202
|
+
<Select
|
|
203
|
+
value={typeof value === 'string' && value ? value : undefined}
|
|
204
|
+
onValueChange={(next) => onUpdate(condition.id, { value: next ?? '' })}
|
|
188
205
|
>
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
<SelectTrigger
|
|
207
|
+
className="min-w-[140px]"
|
|
208
|
+
aria-label={t('ui.advancedFilter.selectValue', 'Select value')}
|
|
209
|
+
>
|
|
210
|
+
<SelectValue placeholder={t('ui.advancedFilter.selectValuePlaceholder', 'Select...')} />
|
|
211
|
+
</SelectTrigger>
|
|
212
|
+
<SelectContent>
|
|
213
|
+
{fieldDef.options.map((opt) => (
|
|
214
|
+
<SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>
|
|
215
|
+
))}
|
|
216
|
+
</SelectContent>
|
|
217
|
+
</Select>
|
|
194
218
|
)
|
|
195
219
|
}
|
|
196
220
|
|
|
@@ -208,9 +232,10 @@ function ValueInput({
|
|
|
208
232
|
|
|
209
233
|
if (fieldType === 'number') {
|
|
210
234
|
return (
|
|
211
|
-
<
|
|
235
|
+
<Input
|
|
212
236
|
type="number"
|
|
213
|
-
|
|
237
|
+
size="sm"
|
|
238
|
+
className="w-[120px]"
|
|
214
239
|
value={typeof value === 'number' ? value : typeof value === 'string' ? value : ''}
|
|
215
240
|
onChange={(e) => onUpdate(condition.id, { value: e.target.value })}
|
|
216
241
|
placeholder={t('ui.advancedFilter.numberPlaceholder', 'Value')}
|
|
@@ -220,9 +245,10 @@ function ValueInput({
|
|
|
220
245
|
}
|
|
221
246
|
|
|
222
247
|
return (
|
|
223
|
-
<
|
|
248
|
+
<Input
|
|
224
249
|
type="text"
|
|
225
|
-
|
|
250
|
+
size="sm"
|
|
251
|
+
className="min-w-[140px]"
|
|
226
252
|
value={typeof value === 'string' ? value : ''}
|
|
227
253
|
onChange={(e) => onUpdate(condition.id, { value: e.target.value })}
|
|
228
254
|
placeholder={t('ui.advancedFilter.textPlaceholder', 'Value...')}
|
|
@@ -8,6 +8,13 @@ import { Input } from '../../primitives/input'
|
|
|
8
8
|
import { Checkbox } from '../../primitives/checkbox'
|
|
9
9
|
import { Textarea } from '../../primitives/textarea'
|
|
10
10
|
import { Label } from '../../primitives/label'
|
|
11
|
+
import {
|
|
12
|
+
Select,
|
|
13
|
+
SelectContent,
|
|
14
|
+
SelectItem,
|
|
15
|
+
SelectTrigger,
|
|
16
|
+
SelectValue,
|
|
17
|
+
} from '../../primitives/select'
|
|
11
18
|
import { Spinner } from '../../primitives/spinner'
|
|
12
19
|
|
|
13
20
|
type InjectedFieldProps = {
|
|
@@ -53,20 +60,22 @@ function SelectField({
|
|
|
53
60
|
return (
|
|
54
61
|
<div className="space-y-2" data-crud-field-id={field.id}>
|
|
55
62
|
<Label htmlFor={field.id}>{label}</Label>
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
value={typeof value === 'string' ? value : ''}
|
|
63
|
+
<Select
|
|
64
|
+
value={typeof value === 'string' && value ? value : undefined}
|
|
65
|
+
onValueChange={(next) => onChange(field.id, next || undefined)}
|
|
60
66
|
disabled={disabled || (options.length === 0 && !field.options?.length)}
|
|
61
|
-
onChange={(event) => onChange(field.id, event.target.value || undefined)}
|
|
62
67
|
>
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
<SelectTrigger id={field.id}>
|
|
69
|
+
<SelectValue placeholder={t('ui.filters.select.placeholder', 'Select...')} />
|
|
70
|
+
</SelectTrigger>
|
|
71
|
+
<SelectContent>
|
|
72
|
+
{options.map((option) => (
|
|
73
|
+
<SelectItem key={option.value} value={option.value}>
|
|
74
|
+
{option.labelKey ? t(option.labelKey, option.label) : option.label}
|
|
75
|
+
</SelectItem>
|
|
76
|
+
))}
|
|
77
|
+
</SelectContent>
|
|
78
|
+
</Select>
|
|
70
79
|
{optionsError ? (
|
|
71
80
|
<div className="text-xs text-muted-foreground">{t('ui.forms.optionsUnavailable', 'Options unavailable')}</div>
|
|
72
81
|
) : null}
|
|
@@ -208,6 +208,10 @@ export function ComboboxInput({
|
|
|
208
208
|
|
|
209
209
|
return (
|
|
210
210
|
<div className="relative w-full">
|
|
211
|
+
{/* Use raw <input> here instead of the DS Input primitive: ComboboxInput's
|
|
212
|
+
focus / suggestions-popup interplay relies on the trigger being a plain
|
|
213
|
+
input element. The DS wrapper introduces a <div> that desyncs autocomplete
|
|
214
|
+
on this specific surface. Keeps the rest of the form on Input primitive. */}
|
|
211
215
|
<input
|
|
212
216
|
ref={inputRef}
|
|
213
217
|
type="text"
|
|
@@ -4,6 +4,15 @@ import * as React from 'react'
|
|
|
4
4
|
import { useMemo } from 'react'
|
|
5
5
|
import { useQuery } from '@tanstack/react-query'
|
|
6
6
|
import { apiCall } from '../utils/apiCall'
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectGroup,
|
|
11
|
+
SelectItem,
|
|
12
|
+
SelectLabel,
|
|
13
|
+
SelectTrigger,
|
|
14
|
+
SelectValue,
|
|
15
|
+
} from '../../primitives/select'
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* Event definition returned by the API
|
|
@@ -101,25 +110,29 @@ export function EventSelect({
|
|
|
101
110
|
const isEmpty = !isLoading && filteredEvents.length === 0
|
|
102
111
|
|
|
103
112
|
return (
|
|
104
|
-
<
|
|
105
|
-
value={value}
|
|
106
|
-
|
|
107
|
-
className={`h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className || ''}`}
|
|
113
|
+
<Select
|
|
114
|
+
value={value || undefined}
|
|
115
|
+
onValueChange={(next) => onChange(next ?? '')}
|
|
108
116
|
disabled={disabled || isLoading}
|
|
109
117
|
>
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
</
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
<SelectTrigger size="lg" className={className}>
|
|
119
|
+
<SelectValue
|
|
120
|
+
placeholder={isLoading ? 'Loading...' : isEmpty ? 'No events available' : placeholder}
|
|
121
|
+
/>
|
|
122
|
+
</SelectTrigger>
|
|
123
|
+
<SelectContent>
|
|
124
|
+
{Object.entries(eventsByModule).map(([module, moduleEvents]) => (
|
|
125
|
+
<SelectGroup key={module}>
|
|
126
|
+
<SelectLabel>{formatModuleName(module)}</SelectLabel>
|
|
127
|
+
{moduleEvents.map(event => (
|
|
128
|
+
<SelectItem key={event.id} value={event.id}>
|
|
129
|
+
{event.label}
|
|
130
|
+
</SelectItem>
|
|
131
|
+
))}
|
|
132
|
+
</SelectGroup>
|
|
133
|
+
))}
|
|
134
|
+
</SelectContent>
|
|
135
|
+
</Select>
|
|
123
136
|
)
|
|
124
137
|
}
|
|
125
138
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { extractPhoneDigits, validatePhoneNumber } from '@open-mercato/shared/lib/phone'
|
|
5
|
+
import { Input } from '../../primitives/input'
|
|
5
6
|
|
|
6
7
|
export type PhoneDuplicateMatch = {
|
|
7
8
|
id: string
|
|
@@ -136,9 +137,8 @@ export function PhoneNumberField({
|
|
|
136
137
|
|
|
137
138
|
return (
|
|
138
139
|
<div className="space-y-2">
|
|
139
|
-
<
|
|
140
|
+
<Input
|
|
140
141
|
type="tel"
|
|
141
|
-
className="w-full h-9 rounded border px-2 text-sm"
|
|
142
142
|
value={local}
|
|
143
143
|
onChange={handleChange}
|
|
144
144
|
onBlur={handleBlur}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
5
5
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
6
|
+
import { Input } from '../../primitives/input'
|
|
6
7
|
|
|
7
8
|
export type TimeInputProps = {
|
|
8
9
|
value?: string | null
|
|
@@ -107,16 +108,12 @@ export function TimeInput({
|
|
|
107
108
|
[disabled, emitChange, hour, minuteStep]
|
|
108
109
|
)
|
|
109
110
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',
|
|
113
|
-
disabled && 'bg-muted text-muted-foreground cursor-not-allowed',
|
|
114
|
-
'disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed'
|
|
115
|
-
)
|
|
111
|
+
const wrapperClass = 'w-14'
|
|
112
|
+
const innerInputClass = 'text-center tabular-nums'
|
|
116
113
|
|
|
117
114
|
return (
|
|
118
115
|
<div className={cn('flex items-center gap-1', className)}>
|
|
119
|
-
<
|
|
116
|
+
<Input
|
|
120
117
|
type="number"
|
|
121
118
|
min={0}
|
|
122
119
|
max={23}
|
|
@@ -126,10 +123,11 @@ export function TimeInput({
|
|
|
126
123
|
disabled={disabled}
|
|
127
124
|
aria-label={hourLabel}
|
|
128
125
|
data-crud-focus-target=""
|
|
129
|
-
className={
|
|
126
|
+
className={wrapperClass}
|
|
127
|
+
inputClassName={innerInputClass}
|
|
130
128
|
/>
|
|
131
129
|
<span className="text-sm font-medium select-none">:</span>
|
|
132
|
-
<
|
|
130
|
+
<Input
|
|
133
131
|
type="number"
|
|
134
132
|
min={0}
|
|
135
133
|
max={59}
|
|
@@ -139,7 +137,8 @@ export function TimeInput({
|
|
|
139
137
|
onKeyDown={handleMinuteKeyDown}
|
|
140
138
|
disabled={disabled}
|
|
141
139
|
aria-label={minuteLabel}
|
|
142
|
-
className={
|
|
140
|
+
className={wrapperClass}
|
|
141
|
+
inputClassName={innerInputClass}
|
|
143
142
|
/>
|
|
144
143
|
</div>
|
|
145
144
|
)
|