@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
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
import * as React from '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 { Plus, Trash2, ChevronRight, ChevronDown, Code, LayoutList } from 'lucide-react'
|
|
7
15
|
|
|
8
16
|
function cn(...classes: (string | undefined | null | false)[]) {
|
|
@@ -239,47 +247,57 @@ function JsonNode({ data, onChange, onDelete, readOnly, label, isRoot }: JsonNod
|
|
|
239
247
|
<div className="flex-1 flex gap-2 items-center flex-wrap">
|
|
240
248
|
|
|
241
249
|
{!readOnly && (
|
|
242
|
-
<
|
|
250
|
+
<Select
|
|
243
251
|
value={type}
|
|
244
|
-
|
|
245
|
-
className="text-xs border rounded px-1 py-0.5 bg-muted text-foreground focus-visible:ring-1 focus-visible:ring-ring"
|
|
252
|
+
onValueChange={(next) => handleTypeChange(next as JsonNodeType)}
|
|
246
253
|
>
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
+
<SelectTrigger size="sm" className="w-auto min-w-[6rem]">
|
|
255
|
+
<SelectValue />
|
|
256
|
+
</SelectTrigger>
|
|
257
|
+
<SelectContent>
|
|
258
|
+
<SelectItem value="string">String</SelectItem>
|
|
259
|
+
<SelectItem value="number">Number</SelectItem>
|
|
260
|
+
<SelectItem value="boolean">Boolean</SelectItem>
|
|
261
|
+
<SelectItem value="object">Object</SelectItem>
|
|
262
|
+
<SelectItem value="array">Array</SelectItem>
|
|
263
|
+
<SelectItem value="null">Null</SelectItem>
|
|
264
|
+
</SelectContent>
|
|
265
|
+
</Select>
|
|
254
266
|
)}
|
|
255
267
|
|
|
256
268
|
{type === 'string' && (
|
|
257
|
-
<
|
|
258
|
-
|
|
269
|
+
<Input
|
|
270
|
+
size="sm"
|
|
271
|
+
className="flex-1 min-w-0 sm:min-w-[120px]"
|
|
259
272
|
value={data}
|
|
260
273
|
onChange={e => onChange(e.target.value)}
|
|
261
274
|
disabled={readOnly}
|
|
262
275
|
/>
|
|
263
276
|
)}
|
|
264
277
|
{type === 'number' && (
|
|
265
|
-
<
|
|
278
|
+
<Input
|
|
266
279
|
type="number"
|
|
267
|
-
|
|
280
|
+
size="sm"
|
|
281
|
+
className="flex-1 w-full sm:w-[100px]"
|
|
268
282
|
value={data}
|
|
269
283
|
onChange={e => onChange(parseFloat(e.target.value) || 0)}
|
|
270
284
|
disabled={readOnly}
|
|
271
285
|
/>
|
|
272
286
|
)}
|
|
273
287
|
{type === 'boolean' && (
|
|
274
|
-
<
|
|
275
|
-
className="flex-1 w-full sm:w-[100px] text-sm border rounded px-2 py-0.5 focus-visible:outline-none focus-visible:border-ring"
|
|
288
|
+
<Select
|
|
276
289
|
value={String(data)}
|
|
277
|
-
|
|
290
|
+
onValueChange={(next) => onChange(next === 'true')}
|
|
278
291
|
disabled={readOnly}
|
|
279
292
|
>
|
|
280
|
-
<
|
|
281
|
-
|
|
282
|
-
|
|
293
|
+
<SelectTrigger size="sm" className="flex-1 w-full sm:w-[100px]">
|
|
294
|
+
<SelectValue />
|
|
295
|
+
</SelectTrigger>
|
|
296
|
+
<SelectContent>
|
|
297
|
+
<SelectItem value="true">true</SelectItem>
|
|
298
|
+
<SelectItem value="false">false</SelectItem>
|
|
299
|
+
</SelectContent>
|
|
300
|
+
</Select>
|
|
283
301
|
)}
|
|
284
302
|
{type === 'null' && <span className="text-xs text-muted-foreground">null</span>}
|
|
285
303
|
{isContainer && (
|
|
@@ -5,6 +5,20 @@ import { fireEvent, screen } from '@testing-library/react'
|
|
|
5
5
|
import { renderWithProviders } from '@open-mercato/shared/lib/testing/renderWithProviders'
|
|
6
6
|
import { FieldDefinitionsEditor, type FieldDefinition } from '../custom-fields/FieldDefinitionsEditor'
|
|
7
7
|
|
|
8
|
+
// Radix Select uses pointer capture / scrollIntoView APIs that jsdom doesn't implement.
|
|
9
|
+
// Polyfill them so tests can interact with Radix-based comboboxes.
|
|
10
|
+
if (typeof window !== 'undefined') {
|
|
11
|
+
if (!Element.prototype.hasPointerCapture) {
|
|
12
|
+
Element.prototype.hasPointerCapture = () => false
|
|
13
|
+
}
|
|
14
|
+
if (!Element.prototype.releasePointerCapture) {
|
|
15
|
+
Element.prototype.releasePointerCapture = () => undefined
|
|
16
|
+
}
|
|
17
|
+
if (!Element.prototype.scrollIntoView) {
|
|
18
|
+
Element.prototype.scrollIntoView = () => undefined
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
8
22
|
describe('FieldDefinitionsEditor', () => {
|
|
9
23
|
it('assigns a field to a fieldset without triggering a render-time parent update warning', () => {
|
|
10
24
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
@@ -36,15 +50,18 @@ describe('FieldDefinitionsEditor', () => {
|
|
|
36
50
|
/>,
|
|
37
51
|
)
|
|
38
52
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const optionLabels = Array.from(element.options).map((option) => option.text)
|
|
42
|
-
return optionLabels.includes('Unassigned') && optionLabels.includes('New fieldset')
|
|
53
|
+
const assignFieldsetTrigger = screen.getAllByRole('combobox').find((element) => {
|
|
54
|
+
return element.textContent?.trim() === 'Unassigned'
|
|
43
55
|
})
|
|
44
56
|
|
|
45
|
-
expect(
|
|
57
|
+
expect(assignFieldsetTrigger).toBeDefined()
|
|
58
|
+
|
|
59
|
+
fireEvent.pointerDown(assignFieldsetTrigger!, { button: 0, ctrlKey: false })
|
|
60
|
+
fireEvent.click(assignFieldsetTrigger!)
|
|
46
61
|
|
|
47
|
-
|
|
62
|
+
const option = screen.getByRole('option', { name: 'New fieldset' })
|
|
63
|
+
fireEvent.pointerDown(option)
|
|
64
|
+
fireEvent.click(option)
|
|
48
65
|
|
|
49
66
|
expect(handleDefinitionChange).toHaveBeenCalledWith(0, expect.objectContaining({
|
|
50
67
|
key: 'buying_role',
|
|
@@ -4,6 +4,7 @@ import { Search, GripVertical, X, ChevronRight } from 'lucide-react'
|
|
|
4
4
|
import { Button } from '../../primitives/button'
|
|
5
5
|
import { IconButton } from '../../primitives/icon-button'
|
|
6
6
|
import { Switch } from '../../primitives/switch'
|
|
7
|
+
import { Input } from '../../primitives/input'
|
|
7
8
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
8
9
|
import {
|
|
9
10
|
DndContext,
|
|
@@ -167,16 +168,14 @@ export function ColumnChooserSection({
|
|
|
167
168
|
return (
|
|
168
169
|
<div className="flex flex-col">
|
|
169
170
|
<div className="px-4 py-3 border-t">
|
|
170
|
-
<
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
/>
|
|
179
|
-
</div>
|
|
171
|
+
<Input
|
|
172
|
+
type="text"
|
|
173
|
+
size="sm"
|
|
174
|
+
leftIcon={<Search />}
|
|
175
|
+
placeholder={t('ui.columnChooser.search', 'Search columns...')}
|
|
176
|
+
value={searchQuery}
|
|
177
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
178
|
+
/>
|
|
180
179
|
</div>
|
|
181
180
|
|
|
182
181
|
<div>
|
|
@@ -4,6 +4,13 @@ import * as React from 'react'
|
|
|
4
4
|
import { Cog, GripVertical, Languages, Pencil, Plus, Trash2, X } from 'lucide-react'
|
|
5
5
|
import { Button } from '../../primitives/button'
|
|
6
6
|
import { IconButton } from '../../primitives/icon-button'
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectTrigger,
|
|
12
|
+
SelectValue,
|
|
13
|
+
} from '../../primitives/select'
|
|
7
14
|
import { CUSTOM_FIELD_KINDS } from '@open-mercato/shared/modules/entities/kinds'
|
|
8
15
|
import { FieldRegistry } from '../fields/registry'
|
|
9
16
|
import { slugify } from '@open-mercato/shared/lib/slugify'
|
|
@@ -289,18 +296,21 @@ export function FieldDefinitionsEditor({
|
|
|
289
296
|
<div className="rounded border bg-card p-3 space-y-3">
|
|
290
297
|
<div className="flex flex-wrap items-center gap-2">
|
|
291
298
|
<label className="text-xs font-medium text-muted-foreground">Fieldset</label>
|
|
292
|
-
<
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
onChange={(event) => handleActiveFieldsetChange(event.target.value)}
|
|
299
|
+
<Select
|
|
300
|
+
value={resolvedActiveFieldset || undefined}
|
|
301
|
+
onValueChange={(value) => handleActiveFieldsetChange(value ?? '')}
|
|
296
302
|
>
|
|
297
|
-
<
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
303
|
+
<SelectTrigger size="sm" className="w-auto min-w-[10rem]">
|
|
304
|
+
<SelectValue placeholder="Unassigned fields" />
|
|
305
|
+
</SelectTrigger>
|
|
306
|
+
<SelectContent>
|
|
307
|
+
{fieldsets.map((fs) => (
|
|
308
|
+
<SelectItem key={fs.code} value={fs.code}>
|
|
309
|
+
{fs.label || fs.code}
|
|
310
|
+
</SelectItem>
|
|
311
|
+
))}
|
|
312
|
+
</SelectContent>
|
|
313
|
+
</Select>
|
|
304
314
|
<Button
|
|
305
315
|
variant="outline"
|
|
306
316
|
size="sm"
|
|
@@ -339,22 +349,25 @@ export function FieldDefinitionsEditor({
|
|
|
339
349
|
</div>
|
|
340
350
|
<div>
|
|
341
351
|
<label className="text-xs">Icon</label>
|
|
342
|
-
<
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
onChange={(event) =>
|
|
352
|
+
<Select
|
|
353
|
+
value={activeFieldsetConfig.icon || undefined}
|
|
354
|
+
onValueChange={(value) =>
|
|
346
355
|
handleFieldsetPatch(activeFieldsetConfig.code, {
|
|
347
|
-
icon:
|
|
356
|
+
icon: value || undefined,
|
|
348
357
|
})
|
|
349
358
|
}
|
|
350
359
|
>
|
|
351
|
-
<
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
360
|
+
<SelectTrigger size="sm">
|
|
361
|
+
<SelectValue placeholder="Default" />
|
|
362
|
+
</SelectTrigger>
|
|
363
|
+
<SelectContent>
|
|
364
|
+
{FIELDSET_ICON_OPTIONS.map((option) => (
|
|
365
|
+
<SelectItem key={option.value} value={option.value}>
|
|
366
|
+
{option.label}
|
|
367
|
+
</SelectItem>
|
|
368
|
+
))}
|
|
369
|
+
</SelectContent>
|
|
370
|
+
</Select>
|
|
358
371
|
</div>
|
|
359
372
|
<div>
|
|
360
373
|
<label className="text-xs">Description</label>
|
|
@@ -756,17 +769,21 @@ const FieldDefinitionCard = React.memo(function FieldDefinitionCard({
|
|
|
756
769
|
</div>
|
|
757
770
|
<div className="md:col-span-6">
|
|
758
771
|
<label className="text-xs">Kind</label>
|
|
759
|
-
<
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
onChange={(event) => { apply({ kind: event.target.value }, true) }}
|
|
772
|
+
<Select
|
|
773
|
+
value={local.kind || undefined}
|
|
774
|
+
onValueChange={(value) => { apply({ kind: value ?? '' }, true) }}
|
|
763
775
|
>
|
|
764
|
-
{
|
|
765
|
-
<
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
776
|
+
<SelectTrigger className={error?.kind ? 'border-destructive' : undefined}>
|
|
777
|
+
<SelectValue />
|
|
778
|
+
</SelectTrigger>
|
|
779
|
+
<SelectContent>
|
|
780
|
+
{kindOptions.map((option) => (
|
|
781
|
+
<SelectItem key={option.value} value={option.value}>
|
|
782
|
+
{option.label}
|
|
783
|
+
</SelectItem>
|
|
784
|
+
))}
|
|
785
|
+
</SelectContent>
|
|
786
|
+
</Select>
|
|
770
787
|
{error?.kind ? <div className="text-xs text-red-600 mt-1">{error.kind}</div> : null}
|
|
771
788
|
</div>
|
|
772
789
|
</div>
|
|
@@ -775,18 +792,21 @@ const FieldDefinitionCard = React.memo(function FieldDefinitionCard({
|
|
|
775
792
|
<div className="mt-3 grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
776
793
|
<div>
|
|
777
794
|
<label className="text-xs">Assign to fieldset</label>
|
|
778
|
-
<
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
onChange={(event) => handleFieldsetSelect(event.target.value)}
|
|
795
|
+
<Select
|
|
796
|
+
value={currentFieldsetValue || undefined}
|
|
797
|
+
onValueChange={(value) => handleFieldsetSelect(value ?? '')}
|
|
782
798
|
>
|
|
783
|
-
<
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
799
|
+
<SelectTrigger>
|
|
800
|
+
<SelectValue placeholder="Unassigned" />
|
|
801
|
+
</SelectTrigger>
|
|
802
|
+
<SelectContent>
|
|
803
|
+
{(fieldsets || []).map((fs) => (
|
|
804
|
+
<SelectItem key={fs.code} value={fs.code}>
|
|
805
|
+
{fs.label || fs.code}
|
|
806
|
+
</SelectItem>
|
|
807
|
+
))}
|
|
808
|
+
</SelectContent>
|
|
809
|
+
</Select>
|
|
790
810
|
</div>
|
|
791
811
|
{currentFieldsetValue ? (
|
|
792
812
|
<div>
|
|
@@ -794,18 +814,21 @@ const FieldDefinitionCard = React.memo(function FieldDefinitionCard({
|
|
|
794
814
|
<label className="text-xs">Group</label>
|
|
795
815
|
</div>
|
|
796
816
|
<div className="flex items-center gap-2 mt-1">
|
|
797
|
-
<
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
onChange={(event) => handleGroupSelect(event.target.value)}
|
|
817
|
+
<Select
|
|
818
|
+
value={currentGroup?.code || undefined}
|
|
819
|
+
onValueChange={(value) => handleGroupSelect(value ?? '')}
|
|
801
820
|
>
|
|
802
|
-
<
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
821
|
+
<SelectTrigger className="flex-1">
|
|
822
|
+
<SelectValue placeholder="No group" />
|
|
823
|
+
</SelectTrigger>
|
|
824
|
+
<SelectContent>
|
|
825
|
+
{groupOptions.map((group) => (
|
|
826
|
+
<SelectItem key={group.code} value={group.code}>
|
|
827
|
+
{group.title || group.code}
|
|
828
|
+
</SelectItem>
|
|
829
|
+
))}
|
|
830
|
+
</SelectContent>
|
|
831
|
+
</Select>
|
|
809
832
|
<IconButton
|
|
810
833
|
variant="outline"
|
|
811
834
|
className="text-muted-foreground"
|
|
@@ -852,16 +875,19 @@ const FieldDefinitionCard = React.memo(function FieldDefinitionCard({
|
|
|
852
875
|
<>
|
|
853
876
|
<div>
|
|
854
877
|
<label className="text-xs">Editor</label>
|
|
855
|
-
<
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
onChange={(event) => { apply({ configJson: { ...(local.configJson || {}), editor: event.target.value || undefined } }, true) }}
|
|
878
|
+
<Select
|
|
879
|
+
value={typeof local.configJson?.editor === 'string' && local.configJson.editor ? local.configJson.editor : undefined}
|
|
880
|
+
onValueChange={(value) => { apply({ configJson: { ...(local.configJson || {}), editor: value || undefined } }, true) }}
|
|
859
881
|
>
|
|
860
|
-
<
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
<
|
|
864
|
-
|
|
882
|
+
<SelectTrigger>
|
|
883
|
+
<SelectValue placeholder="Default" />
|
|
884
|
+
</SelectTrigger>
|
|
885
|
+
<SelectContent>
|
|
886
|
+
<SelectItem value="markdown">Markdown (UIW)</SelectItem>
|
|
887
|
+
<SelectItem value="simpleMarkdown">Simple Markdown</SelectItem>
|
|
888
|
+
<SelectItem value="htmlRichText">HTML Rich Text</SelectItem>
|
|
889
|
+
</SelectContent>
|
|
890
|
+
</Select>
|
|
865
891
|
</div>
|
|
866
892
|
{local.kind === 'text' && (
|
|
867
893
|
<>
|
|
@@ -873,20 +899,23 @@ const FieldDefinitionCard = React.memo(function FieldDefinitionCard({
|
|
|
873
899
|
{!!local.configJson?.multi && (
|
|
874
900
|
<div className="md:col-span-2">
|
|
875
901
|
<label className="text-xs">Multi-select input style</label>
|
|
876
|
-
<
|
|
877
|
-
className="border rounded w-full px-2 py-1 text-sm"
|
|
902
|
+
<Select
|
|
878
903
|
value={local.configJson?.input === 'listbox' ? 'listbox' : 'default'}
|
|
879
|
-
|
|
880
|
-
const { value } = event.target
|
|
904
|
+
onValueChange={(value) => {
|
|
881
905
|
const nextConfig = { ...(local.configJson || {}) }
|
|
882
906
|
if (value === 'listbox') nextConfig.input = 'listbox'
|
|
883
907
|
else delete nextConfig.input
|
|
884
908
|
apply({ configJson: nextConfig }, true)
|
|
885
909
|
}}
|
|
886
910
|
>
|
|
887
|
-
<
|
|
888
|
-
|
|
889
|
-
|
|
911
|
+
<SelectTrigger>
|
|
912
|
+
<SelectValue />
|
|
913
|
+
</SelectTrigger>
|
|
914
|
+
<SelectContent>
|
|
915
|
+
<SelectItem value="default">Default</SelectItem>
|
|
916
|
+
<SelectItem value="listbox">Listbox (searchable)</SelectItem>
|
|
917
|
+
</SelectContent>
|
|
918
|
+
</Select>
|
|
890
919
|
</div>
|
|
891
920
|
)}
|
|
892
921
|
</>
|
|
@@ -1022,11 +1051,10 @@ const FieldDefinitionCard = React.memo(function FieldDefinitionCard({
|
|
|
1022
1051
|
{(Array.isArray(local.configJson?.validation) ? local.configJson!.validation : []).map((rule: any, index: number) => (
|
|
1023
1052
|
<div key={index} className="grid grid-cols-1 md:grid-cols-12 gap-2 items-center">
|
|
1024
1053
|
<div className="md:col-span-3">
|
|
1025
|
-
<
|
|
1026
|
-
className="border rounded w-full px-2 py-1 text-sm"
|
|
1054
|
+
<Select
|
|
1027
1055
|
value={rule?.rule || 'required'}
|
|
1028
|
-
|
|
1029
|
-
const nextRule =
|
|
1056
|
+
onValueChange={(value) => {
|
|
1057
|
+
const nextRule = value
|
|
1030
1058
|
apply((current) => {
|
|
1031
1059
|
const list = Array.isArray(current.configJson?.validation) ? [...current.configJson.validation] : []
|
|
1032
1060
|
const existing = (list[index] as any) || {}
|
|
@@ -1035,18 +1063,23 @@ const FieldDefinitionCard = React.memo(function FieldDefinitionCard({
|
|
|
1035
1063
|
}, true)
|
|
1036
1064
|
}}
|
|
1037
1065
|
>
|
|
1038
|
-
<
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
<
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1066
|
+
<SelectTrigger>
|
|
1067
|
+
<SelectValue />
|
|
1068
|
+
</SelectTrigger>
|
|
1069
|
+
<SelectContent>
|
|
1070
|
+
<SelectItem value="required">required</SelectItem>
|
|
1071
|
+
<SelectItem value="date">date</SelectItem>
|
|
1072
|
+
<SelectItem value="integer">integer</SelectItem>
|
|
1073
|
+
<SelectItem value="float">float</SelectItem>
|
|
1074
|
+
<SelectItem value="lt">lt</SelectItem>
|
|
1075
|
+
<SelectItem value="lte">lte</SelectItem>
|
|
1076
|
+
<SelectItem value="gt">gt</SelectItem>
|
|
1077
|
+
<SelectItem value="gte">gte</SelectItem>
|
|
1078
|
+
<SelectItem value="eq">eq</SelectItem>
|
|
1079
|
+
<SelectItem value="ne">ne</SelectItem>
|
|
1080
|
+
<SelectItem value="regex">regex</SelectItem>
|
|
1081
|
+
</SelectContent>
|
|
1082
|
+
</Select>
|
|
1050
1083
|
</div>
|
|
1051
1084
|
<div className="md:col-span-4">
|
|
1052
1085
|
<input
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
5
|
+
import {
|
|
6
|
+
Select,
|
|
7
|
+
SelectContent,
|
|
8
|
+
SelectItem,
|
|
9
|
+
SelectTrigger,
|
|
10
|
+
SelectValue,
|
|
11
|
+
} from '../../primitives/select'
|
|
5
12
|
import { DATE_RANGE_OPTIONS, type DateRangePreset } from './dateRanges'
|
|
6
13
|
|
|
7
14
|
export type DateRangeSelectProps = {
|
|
@@ -31,18 +38,18 @@ export function DateRangeSelect({
|
|
|
31
38
|
{label}
|
|
32
39
|
</label>
|
|
33
40
|
)}
|
|
34
|
-
<
|
|
35
|
-
id={id}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
</
|
|
41
|
+
<Select value={value} onValueChange={(next) => onChange(next as DateRangePreset)}>
|
|
42
|
+
<SelectTrigger id={id} size="sm">
|
|
43
|
+
<SelectValue />
|
|
44
|
+
</SelectTrigger>
|
|
45
|
+
<SelectContent>
|
|
46
|
+
{DATE_RANGE_OPTIONS.map((option) => (
|
|
47
|
+
<SelectItem key={option.value} value={option.value}>
|
|
48
|
+
{t(option.labelKey, option.value.replace(/_/g, ' '))}
|
|
49
|
+
</SelectItem>
|
|
50
|
+
))}
|
|
51
|
+
</SelectContent>
|
|
52
|
+
</Select>
|
|
46
53
|
</div>
|
|
47
54
|
)
|
|
48
55
|
}
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
5
|
+
import {
|
|
6
|
+
Select,
|
|
7
|
+
SelectContent,
|
|
8
|
+
SelectItem,
|
|
9
|
+
SelectTrigger,
|
|
10
|
+
SelectValue,
|
|
11
|
+
} from '../../primitives/select'
|
|
5
12
|
import { DATE_RANGE_OPTIONS, type DateRangePreset } from './dateRanges'
|
|
6
13
|
|
|
7
14
|
export type InlineDateRangeSelectProps = {
|
|
@@ -23,29 +30,18 @@ export function InlineDateRangeSelect({
|
|
|
23
30
|
: value.replace(/_/g, ' ')
|
|
24
31
|
|
|
25
32
|
return (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
title={displayLabel}
|
|
32
|
-
>
|
|
33
|
+
<Select value={value} onValueChange={(next) => onChange(next as DateRangePreset)}>
|
|
34
|
+
<SelectTrigger size="sm" className={className} title={displayLabel}>
|
|
35
|
+
<SelectValue />
|
|
36
|
+
</SelectTrigger>
|
|
37
|
+
<SelectContent>
|
|
33
38
|
{DATE_RANGE_OPTIONS.map((option) => (
|
|
34
|
-
<
|
|
39
|
+
<SelectItem key={option.value} value={option.value}>
|
|
35
40
|
{t(option.labelKey, option.value.replace(/_/g, ' '))}
|
|
36
|
-
</
|
|
41
|
+
</SelectItem>
|
|
37
42
|
))}
|
|
38
|
-
</
|
|
39
|
-
|
|
40
|
-
className="pointer-events-none absolute right-1.5 h-3 w-3 text-muted-foreground"
|
|
41
|
-
fill="none"
|
|
42
|
-
viewBox="0 0 24 24"
|
|
43
|
-
stroke="currentColor"
|
|
44
|
-
strokeWidth={2}
|
|
45
|
-
>
|
|
46
|
-
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
|
47
|
-
</svg>
|
|
48
|
-
</div>
|
|
43
|
+
</SelectContent>
|
|
44
|
+
</Select>
|
|
49
45
|
)
|
|
50
46
|
}
|
|
51
47
|
|