@open-mercato/core 0.4.6-develop-f7d3079656 → 0.4.6-develop-0861f05ea9
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/dist/modules/currencies/backend/exchange-rates/[id]/page.js +17 -154
- package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +3 -3
- package/dist/modules/currencies/backend/exchange-rates/create/page.js +14 -152
- package/dist/modules/currencies/backend/exchange-rates/create/page.js.map +2 -2
- package/dist/modules/currencies/lib/exchangeRateFormConfig.js +167 -0
- package/dist/modules/currencies/lib/exchangeRateFormConfig.js.map +7 -0
- package/dist/modules/customers/api/dashboard/widgets/utils.js +1 -34
- package/dist/modules/customers/api/dashboard/widgets/utils.js.map +2 -2
- package/dist/modules/customers/commands/activities.js +3 -8
- package/dist/modules/customers/commands/activities.js.map +2 -2
- package/dist/modules/customers/commands/comments.js +2 -8
- package/dist/modules/customers/commands/comments.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetScope.js +38 -0
- package/dist/modules/dashboards/lib/widgetScope.js.map +7 -0
- package/dist/modules/entities/lib/makeActivityRoute.js +265 -0
- package/dist/modules/entities/lib/makeActivityRoute.js.map +7 -0
- package/dist/modules/resources/api/activities.js +24 -232
- package/dist/modules/resources/api/activities.js.map +2 -2
- package/dist/modules/resources/commands/activities.js +3 -8
- package/dist/modules/resources/commands/activities.js.map +2 -2
- package/dist/modules/resources/commands/comments.js +2 -8
- package/dist/modules/resources/commands/comments.js.map +2 -2
- package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js +27 -182
- package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js.map +2 -2
- package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js +28 -183
- package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js.map +2 -2
- package/dist/modules/sales/api/order-line-statuses/route.js +15 -194
- package/dist/modules/sales/api/order-line-statuses/route.js.map +2 -2
- package/dist/modules/sales/api/order-lines/route.js +15 -281
- package/dist/modules/sales/api/order-lines/route.js.map +2 -2
- package/dist/modules/sales/api/order-statuses/route.js +15 -194
- package/dist/modules/sales/api/order-statuses/route.js.map +2 -2
- package/dist/modules/sales/api/payment-statuses/route.js +15 -194
- package/dist/modules/sales/api/payment-statuses/route.js.map +2 -2
- package/dist/modules/sales/api/quote-lines/route.js +15 -279
- package/dist/modules/sales/api/quote-lines/route.js.map +2 -2
- package/dist/modules/sales/api/shipment-statuses/route.js +15 -194
- package/dist/modules/sales/api/shipment-statuses/route.js.map +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js +3 -84
- package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/ProviderFieldInput.js +86 -0
- package/dist/modules/sales/components/ProviderFieldInput.js.map +7 -0
- package/dist/modules/sales/components/ShippingMethodsSettings.js +3 -82
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
- package/dist/modules/sales/lib/makeSalesLineRoute.js +308 -0
- package/dist/modules/sales/lib/makeSalesLineRoute.js.map +7 -0
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +206 -0
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +7 -0
- package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js +178 -0
- package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js.map +7 -0
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +1 -39
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +1 -39
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/shared.js +46 -0
- package/dist/modules/sales/widgets/dashboard/shared.js.map +7 -0
- package/dist/modules/staff/api/activities.js +24 -232
- package/dist/modules/staff/api/activities.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +14 -34
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +15 -34
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/commands/activities.js +3 -8
- package/dist/modules/staff/commands/activities.js.map +2 -2
- package/dist/modules/staff/commands/comments.js +2 -8
- package/dist/modules/staff/commands/comments.js.map +2 -2
- package/dist/modules/staff/lib/leaveRequestHelpers.js +41 -0
- package/dist/modules/staff/lib/leaveRequestHelpers.js.map +7 -0
- package/package.json +2 -2
- package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +20 -180
- package/src/modules/currencies/backend/exchange-rates/create/page.tsx +16 -175
- package/src/modules/currencies/lib/exchangeRateFormConfig.ts +200 -0
- package/src/modules/customers/api/dashboard/widgets/utils.ts +1 -53
- package/src/modules/customers/commands/activities.ts +2 -8
- package/src/modules/customers/commands/comments.ts +2 -8
- package/src/modules/dashboards/i18n/de.json +3 -0
- package/src/modules/dashboards/i18n/en.json +3 -0
- package/src/modules/dashboards/i18n/es.json +3 -0
- package/src/modules/dashboards/i18n/pl.json +3 -0
- package/src/modules/dashboards/lib/widgetScope.ts +53 -0
- package/src/modules/entities/lib/makeActivityRoute.ts +327 -0
- package/src/modules/resources/api/activities.ts +25 -269
- package/src/modules/resources/commands/activities.ts +2 -7
- package/src/modules/resources/commands/comments.ts +2 -8
- package/src/modules/sales/api/dashboard/widgets/new-orders/route.ts +29 -244
- package/src/modules/sales/api/dashboard/widgets/new-quotes/route.ts +30 -245
- package/src/modules/sales/api/order-line-statuses/route.ts +16 -209
- package/src/modules/sales/api/order-lines/route.ts +16 -300
- package/src/modules/sales/api/order-statuses/route.ts +16 -209
- package/src/modules/sales/api/payment-statuses/route.ts +16 -209
- package/src/modules/sales/api/quote-lines/route.ts +16 -298
- package/src/modules/sales/api/shipment-statuses/route.ts +16 -209
- package/src/modules/sales/components/PaymentMethodsSettings.tsx +3 -88
- package/src/modules/sales/components/ProviderFieldInput.tsx +85 -0
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +3 -86
- package/src/modules/sales/lib/makeSalesLineRoute.ts +345 -0
- package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +229 -0
- package/src/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.ts +247 -0
- package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +7 -50
- package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +7 -49
- package/src/modules/sales/widgets/dashboard/shared.ts +44 -0
- package/src/modules/staff/api/activities.ts +25 -269
- package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +15 -69
- package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +16 -65
- package/src/modules/staff/commands/activities.ts +2 -7
- package/src/modules/staff/commands/comments.ts +2 -8
- package/src/modules/staff/lib/leaveRequestHelpers.ts +78 -0
|
@@ -1,214 +1,21 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
|
|
3
|
-
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
4
|
-
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
5
|
-
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
6
|
-
import { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'
|
|
7
1
|
import { E } from '#generated/entities.ids.generated'
|
|
8
2
|
import * as F from '#generated/entities/dictionary_entry'
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const rawBodySchema = z.object({}).passthrough()
|
|
20
|
-
|
|
21
|
-
const listSchema = z
|
|
22
|
-
.object({
|
|
23
|
-
page: z.coerce.number().min(1).default(1),
|
|
24
|
-
pageSize: z.coerce.number().min(1).max(100).default(50),
|
|
25
|
-
search: z.string().optional(),
|
|
26
|
-
sortField: z.string().optional(),
|
|
27
|
-
sortDir: z.enum(['asc', 'desc']).optional(),
|
|
28
|
-
})
|
|
29
|
-
.passthrough()
|
|
30
|
-
|
|
31
|
-
const kind: SalesDictionaryKind = 'shipment-status'
|
|
32
|
-
const definition = getSalesDictionaryDefinition(kind)
|
|
33
|
-
|
|
34
|
-
export const metadata = {
|
|
35
|
-
GET: { requireAuth: true, requireFeatures: ['sales.settings.manage'] },
|
|
36
|
-
POST: { requireAuth: true, requireFeatures: ['sales.settings.manage'] },
|
|
37
|
-
PUT: { requireAuth: true, requireFeatures: ['sales.settings.manage'] },
|
|
38
|
-
DELETE: { requireAuth: true, requireFeatures: ['sales.settings.manage'] },
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const dictionaryItemSchema = z.object({
|
|
42
|
-
id: z.string().uuid(),
|
|
43
|
-
value: z.string(),
|
|
44
|
-
label: z.string().nullable(),
|
|
45
|
-
color: z.string().nullable(),
|
|
46
|
-
icon: z.string().nullable(),
|
|
47
|
-
organizationId: z.string().uuid().nullable(),
|
|
48
|
-
tenantId: z.string().uuid().nullable(),
|
|
49
|
-
createdAt: z.string(),
|
|
50
|
-
updatedAt: z.string(),
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
const dictionaryListResponseSchema = createPagedListResponseSchema(dictionaryItemSchema)
|
|
54
|
-
|
|
55
|
-
const normalizeId = (value: unknown): string | null => {
|
|
56
|
-
if (typeof value !== 'string') return null
|
|
57
|
-
const trimmed = value.trim()
|
|
58
|
-
return trimmed.length > 0 ? trimmed : null
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function resolveDictionaryContext(ctx: any): Promise<{ dictionaryId: string; organizationId: string | null }> {
|
|
62
|
-
if (!ctx.auth || !ctx.auth.tenantId) {
|
|
63
|
-
throw new CrudHttpError(401, { error: 'Tenant context is required.' })
|
|
64
|
-
}
|
|
65
|
-
const em = ctx.container.resolve('em') as EntityManager
|
|
66
|
-
const tenantId: string = ctx.auth.tenantId
|
|
67
|
-
const candidateOrgIds = new Set<string>()
|
|
68
|
-
const pushCandidate = (value: unknown) => {
|
|
69
|
-
const normalized = normalizeId(value)
|
|
70
|
-
if (normalized) candidateOrgIds.add(normalized)
|
|
71
|
-
}
|
|
72
|
-
pushCandidate(ctx.selectedOrganizationId)
|
|
73
|
-
pushCandidate(ctx.auth.orgId ?? null)
|
|
74
|
-
const scope = ctx.organizationScope
|
|
75
|
-
if (scope) {
|
|
76
|
-
if (Array.isArray(scope.filterIds)) {
|
|
77
|
-
for (const id of scope.filterIds) pushCandidate(id)
|
|
78
|
-
}
|
|
79
|
-
if (Array.isArray(scope.allowedIds)) {
|
|
80
|
-
for (const id of scope.allowedIds) pushCandidate(id)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
for (const orgId of candidateOrgIds) {
|
|
85
|
-
const dictionary = await ensureSalesDictionary({
|
|
86
|
-
em,
|
|
87
|
-
tenantId,
|
|
88
|
-
organizationId: orgId,
|
|
89
|
-
kind,
|
|
90
|
-
})
|
|
91
|
-
if (dictionary) {
|
|
92
|
-
return { dictionaryId: dictionary.id, organizationId: orgId }
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const fallback = await em.findOne(
|
|
97
|
-
Dictionary,
|
|
98
|
-
{
|
|
99
|
-
tenantId,
|
|
100
|
-
key: definition.key,
|
|
101
|
-
deletedAt: null,
|
|
102
|
-
},
|
|
103
|
-
{ orderBy: { createdAt: 'asc' } },
|
|
104
|
-
)
|
|
105
|
-
if (fallback) {
|
|
106
|
-
return { dictionaryId: fallback.id, organizationId: fallback.organizationId }
|
|
107
|
-
}
|
|
108
|
-
throw new CrudHttpError(400, { error: 'Organization context is required.' })
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const crud = makeCrudRoute({
|
|
112
|
-
metadata,
|
|
113
|
-
orm: {
|
|
114
|
-
entity: DictionaryEntry,
|
|
115
|
-
idField: 'id',
|
|
116
|
-
orgField: 'organizationId',
|
|
117
|
-
tenantField: 'tenantId',
|
|
118
|
-
softDeleteField: null,
|
|
3
|
+
import { makeStatusDictionaryRoute } from '../../lib/makeStatusDictionaryRoute'
|
|
4
|
+
|
|
5
|
+
const route = makeStatusDictionaryRoute({
|
|
6
|
+
kind: 'shipment-status',
|
|
7
|
+
entityId: E.dictionaries.dictionary_entry,
|
|
8
|
+
fieldConstants: F,
|
|
9
|
+
openApi: {
|
|
10
|
+
resourceName: 'Shipment status',
|
|
11
|
+
pluralName: 'Shipment statuses',
|
|
12
|
+
description: 'Manage the lifecycle states available for shipments.',
|
|
119
13
|
},
|
|
120
|
-
list: {
|
|
121
|
-
schema: listSchema,
|
|
122
|
-
entityId: E.dictionaries.dictionary_entry,
|
|
123
|
-
fields: [
|
|
124
|
-
F.id,
|
|
125
|
-
F.value,
|
|
126
|
-
F.label,
|
|
127
|
-
F.color,
|
|
128
|
-
F.icon,
|
|
129
|
-
F.organization_id,
|
|
130
|
-
F.tenant_id,
|
|
131
|
-
F.created_at,
|
|
132
|
-
F.updated_at,
|
|
133
|
-
],
|
|
134
|
-
sortFieldMap: {
|
|
135
|
-
id: F.id,
|
|
136
|
-
value: F.value,
|
|
137
|
-
label: F.label,
|
|
138
|
-
createdAt: F.created_at,
|
|
139
|
-
updatedAt: F.updated_at,
|
|
140
|
-
},
|
|
141
|
-
buildFilters: async (query, ctx) => {
|
|
142
|
-
const { dictionaryId } = await resolveDictionaryContext(ctx)
|
|
143
|
-
const filters: Record<string, unknown> = {
|
|
144
|
-
dictionary_id: dictionaryId,
|
|
145
|
-
}
|
|
146
|
-
if (query.search && query.search.trim().length > 0) {
|
|
147
|
-
const term = `%${escapeLikePattern(query.search.trim())}%`
|
|
148
|
-
filters.$or = [
|
|
149
|
-
{ [F.value]: { $ilike: term } },
|
|
150
|
-
{ [F.label]: { $ilike: term } },
|
|
151
|
-
]
|
|
152
|
-
}
|
|
153
|
-
return filters
|
|
154
|
-
},
|
|
155
|
-
transformItem: (item: any) => ({
|
|
156
|
-
id: item.id,
|
|
157
|
-
value: item.value,
|
|
158
|
-
label: item.label,
|
|
159
|
-
color: item.color ?? null,
|
|
160
|
-
icon: item.icon ?? null,
|
|
161
|
-
organizationId: item.organization_id ?? null,
|
|
162
|
-
tenantId: item.tenant_id ?? null,
|
|
163
|
-
createdAt: item.created_at,
|
|
164
|
-
updatedAt: item.updated_at,
|
|
165
|
-
}),
|
|
166
|
-
},
|
|
167
|
-
actions: {
|
|
168
|
-
create: {
|
|
169
|
-
commandId: `${definition.commandPrefix}.create`,
|
|
170
|
-
schema: rawBodySchema,
|
|
171
|
-
mapInput: async ({ raw, ctx }) => {
|
|
172
|
-
const { translate } = await resolveTranslations()
|
|
173
|
-
return parseScopedCommandInput(statusDictionaryCreateSchema, raw ?? {}, ctx, translate)
|
|
174
|
-
},
|
|
175
|
-
response: ({ result }) => ({ id: result?.entryId ?? null }),
|
|
176
|
-
status: 201,
|
|
177
|
-
},
|
|
178
|
-
update: {
|
|
179
|
-
commandId: `${definition.commandPrefix}.update`,
|
|
180
|
-
schema: rawBodySchema,
|
|
181
|
-
mapInput: async ({ raw, ctx }) => {
|
|
182
|
-
const { translate } = await resolveTranslations()
|
|
183
|
-
return parseScopedCommandInput(statusDictionaryUpdateSchema, raw ?? {}, ctx, translate)
|
|
184
|
-
},
|
|
185
|
-
response: () => ({ ok: true }),
|
|
186
|
-
},
|
|
187
|
-
delete: {
|
|
188
|
-
commandId: `${definition.commandPrefix}.delete`,
|
|
189
|
-
schema: rawBodySchema,
|
|
190
|
-
mapInput: async ({ parsed, ctx }) => {
|
|
191
|
-
const { translate } = await resolveTranslations()
|
|
192
|
-
const id = resolveCrudRecordId(parsed, ctx, translate)
|
|
193
|
-
return { id }
|
|
194
|
-
},
|
|
195
|
-
response: () => ({ ok: true }),
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
export const openApi = createSalesCrudOpenApi({
|
|
201
|
-
resourceName: 'Shipment status',
|
|
202
|
-
pluralName: 'Shipment statuses',
|
|
203
|
-
description: 'Manage the lifecycle states available for shipments.',
|
|
204
|
-
querySchema: listSchema,
|
|
205
|
-
listResponseSchema: dictionaryListResponseSchema,
|
|
206
|
-
create: { schema: statusDictionaryCreateSchema },
|
|
207
|
-
update: { schema: statusDictionaryUpdateSchema },
|
|
208
|
-
del: { schema: defaultDeleteRequestSchema },
|
|
209
14
|
})
|
|
210
15
|
|
|
211
|
-
export const
|
|
212
|
-
export const
|
|
213
|
-
export const
|
|
214
|
-
export const
|
|
16
|
+
export const metadata = route.metadata
|
|
17
|
+
export const openApi = route.openApi
|
|
18
|
+
export const GET = route.GET
|
|
19
|
+
export const POST = route.POST
|
|
20
|
+
export const PUT = route.PUT
|
|
21
|
+
export const DELETE = route.DELETE
|
|
@@ -12,10 +12,7 @@ import {
|
|
|
12
12
|
DialogHeader,
|
|
13
13
|
DialogTitle,
|
|
14
14
|
} from '@open-mercato/ui/primitives/dialog'
|
|
15
|
-
import { Input } from '@open-mercato/ui/primitives/input'
|
|
16
15
|
import { Label } from '@open-mercato/ui/primitives/label'
|
|
17
|
-
import { Switch } from '@open-mercato/ui/primitives/switch'
|
|
18
|
-
import { Textarea } from '@open-mercato/ui/primitives/textarea'
|
|
19
16
|
import { CrudForm, type CrudField, type CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'
|
|
20
17
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
21
18
|
import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
@@ -26,8 +23,9 @@ import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
|
26
23
|
import {
|
|
27
24
|
listPaymentProviders,
|
|
28
25
|
type PaymentProvider,
|
|
29
|
-
type ProviderSettingField,
|
|
30
26
|
} from '../lib/providers'
|
|
27
|
+
import { isRecord } from '@open-mercato/shared/lib/utils'
|
|
28
|
+
import { renderProviderFieldInput } from './ProviderFieldInput'
|
|
31
29
|
|
|
32
30
|
type PaymentMethodRow = {
|
|
33
31
|
id: string
|
|
@@ -68,89 +66,6 @@ const DEFAULT_FORM: PaymentFormValues = {
|
|
|
68
66
|
providerSettings: {},
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
72
|
-
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function renderFieldInput(opts: {
|
|
76
|
-
field: ProviderSettingField
|
|
77
|
-
value: unknown
|
|
78
|
-
onChange: (next: unknown) => void
|
|
79
|
-
}) {
|
|
80
|
-
const { field, value, onChange } = opts
|
|
81
|
-
const common = { id: field.key, 'data-provider-setting': field.key }
|
|
82
|
-
switch (field.type) {
|
|
83
|
-
case 'textarea':
|
|
84
|
-
return (
|
|
85
|
-
<Textarea
|
|
86
|
-
{...common}
|
|
87
|
-
value={typeof value === 'string' ? value : ''}
|
|
88
|
-
onChange={(evt) => onChange(evt.target.value)}
|
|
89
|
-
placeholder={field.placeholder}
|
|
90
|
-
/>
|
|
91
|
-
)
|
|
92
|
-
case 'number':
|
|
93
|
-
return (
|
|
94
|
-
<Input
|
|
95
|
-
{...common}
|
|
96
|
-
type="number"
|
|
97
|
-
value={typeof value === 'number' || typeof value === 'string' ? String(value) : ''}
|
|
98
|
-
onChange={(evt) => onChange(evt.target.value === '' ? '' : Number(evt.target.value))}
|
|
99
|
-
placeholder={field.placeholder}
|
|
100
|
-
/>
|
|
101
|
-
)
|
|
102
|
-
case 'boolean':
|
|
103
|
-
return (
|
|
104
|
-
<div className="flex items-center gap-2 py-1">
|
|
105
|
-
<Switch
|
|
106
|
-
id={field.key}
|
|
107
|
-
checked={Boolean(value)}
|
|
108
|
-
onCheckedChange={(checked) => onChange(checked)}
|
|
109
|
-
/>
|
|
110
|
-
<Label htmlFor={field.key}>{field.placeholder ?? ''}</Label>
|
|
111
|
-
</div>
|
|
112
|
-
)
|
|
113
|
-
case 'select':
|
|
114
|
-
return (
|
|
115
|
-
<select
|
|
116
|
-
{...common}
|
|
117
|
-
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
|
|
118
|
-
value={typeof value === 'string' ? value : ''}
|
|
119
|
-
onChange={(evt) => onChange(evt.target.value)}
|
|
120
|
-
>
|
|
121
|
-
<option value="">—</option>
|
|
122
|
-
{(field.options ?? []).map((opt) => (
|
|
123
|
-
<option key={opt.value} value={opt.value}>
|
|
124
|
-
{opt.label}
|
|
125
|
-
</option>
|
|
126
|
-
))}
|
|
127
|
-
</select>
|
|
128
|
-
)
|
|
129
|
-
case 'secret':
|
|
130
|
-
return (
|
|
131
|
-
<Input
|
|
132
|
-
{...common}
|
|
133
|
-
type="password"
|
|
134
|
-
value={typeof value === 'string' ? value : ''}
|
|
135
|
-
onChange={(evt) => onChange(evt.target.value)}
|
|
136
|
-
placeholder={field.placeholder}
|
|
137
|
-
/>
|
|
138
|
-
)
|
|
139
|
-
case 'url':
|
|
140
|
-
case 'text':
|
|
141
|
-
default:
|
|
142
|
-
return (
|
|
143
|
-
<Input
|
|
144
|
-
{...common}
|
|
145
|
-
type={field.type === 'url' ? 'url' : 'text'}
|
|
146
|
-
value={typeof value === 'string' ? value : ''}
|
|
147
|
-
onChange={(evt) => onChange(evt.target.value)}
|
|
148
|
-
placeholder={field.placeholder}
|
|
149
|
-
/>
|
|
150
|
-
)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
69
|
function createPaymentProviderSettingsRenderer(params: {
|
|
155
70
|
providers: PaymentProvider[]
|
|
156
71
|
selectPrompt: string
|
|
@@ -200,7 +115,7 @@ function createPaymentProviderSettingsRenderer(params: {
|
|
|
200
115
|
{field.description ? (
|
|
201
116
|
<p className="text-xs text-muted-foreground">{field.description}</p>
|
|
202
117
|
) : null}
|
|
203
|
-
{
|
|
118
|
+
{renderProviderFieldInput({
|
|
204
119
|
field,
|
|
205
120
|
value: fieldValue,
|
|
206
121
|
onChange: (next) => setValue({ ...settings, [field.key]: next }),
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Input } from '@open-mercato/ui/primitives/input'
|
|
2
|
+
import { Label } from '@open-mercato/ui/primitives/label'
|
|
3
|
+
import { Switch } from '@open-mercato/ui/primitives/switch'
|
|
4
|
+
import { Textarea } from '@open-mercato/ui/primitives/textarea'
|
|
5
|
+
import { isRecord } from '@open-mercato/shared/lib/utils'
|
|
6
|
+
import type { ProviderSettingField } from '../lib/providers'
|
|
7
|
+
|
|
8
|
+
export function renderProviderFieldInput(opts: {
|
|
9
|
+
field: ProviderSettingField
|
|
10
|
+
value: unknown
|
|
11
|
+
onChange: (next: unknown) => void
|
|
12
|
+
}) {
|
|
13
|
+
const { field, value, onChange } = opts
|
|
14
|
+
const common = { id: field.key, 'data-provider-setting': field.key }
|
|
15
|
+
switch (field.type) {
|
|
16
|
+
case 'textarea':
|
|
17
|
+
return (
|
|
18
|
+
<Textarea
|
|
19
|
+
{...common}
|
|
20
|
+
value={typeof value === 'string' ? value : ''}
|
|
21
|
+
onChange={(evt) => onChange(evt.target.value)}
|
|
22
|
+
placeholder={field.placeholder}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
case 'number':
|
|
26
|
+
return (
|
|
27
|
+
<Input
|
|
28
|
+
{...common}
|
|
29
|
+
type="number"
|
|
30
|
+
value={typeof value === 'number' || typeof value === 'string' ? String(value) : ''}
|
|
31
|
+
onChange={(evt) => onChange(evt.target.value === '' ? '' : Number(evt.target.value))}
|
|
32
|
+
placeholder={field.placeholder}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
case 'boolean':
|
|
36
|
+
return (
|
|
37
|
+
<div className="flex items-center gap-2 py-1">
|
|
38
|
+
<Switch
|
|
39
|
+
id={field.key}
|
|
40
|
+
checked={Boolean(value)}
|
|
41
|
+
onCheckedChange={(checked) => onChange(checked)}
|
|
42
|
+
/>
|
|
43
|
+
<Label htmlFor={field.key}>{field.placeholder ?? ''}</Label>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
case 'select':
|
|
47
|
+
return (
|
|
48
|
+
<select
|
|
49
|
+
{...common}
|
|
50
|
+
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
|
|
51
|
+
value={typeof value === 'string' ? value : ''}
|
|
52
|
+
onChange={(evt) => onChange(evt.target.value)}
|
|
53
|
+
>
|
|
54
|
+
<option value="">—</option>
|
|
55
|
+
{(field.options ?? []).map((opt) => (
|
|
56
|
+
<option key={opt.value} value={opt.value}>
|
|
57
|
+
{opt.label}
|
|
58
|
+
</option>
|
|
59
|
+
))}
|
|
60
|
+
</select>
|
|
61
|
+
)
|
|
62
|
+
case 'secret':
|
|
63
|
+
return (
|
|
64
|
+
<Input
|
|
65
|
+
{...common}
|
|
66
|
+
type="password"
|
|
67
|
+
value={typeof value === 'string' ? value : ''}
|
|
68
|
+
onChange={(evt) => onChange(evt.target.value)}
|
|
69
|
+
placeholder={field.placeholder}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
case 'url':
|
|
73
|
+
case 'text':
|
|
74
|
+
default:
|
|
75
|
+
return (
|
|
76
|
+
<Input
|
|
77
|
+
{...common}
|
|
78
|
+
type={field.type === 'url' ? 'url' : 'text'}
|
|
79
|
+
value={typeof value === 'string' ? value : ''}
|
|
80
|
+
onChange={(evt) => onChange(evt.target.value)}
|
|
81
|
+
placeholder={field.placeholder}
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
16
16
|
import { Label } from '@open-mercato/ui/primitives/label'
|
|
17
17
|
import { Switch } from '@open-mercato/ui/primitives/switch'
|
|
18
|
-
import { Textarea } from '@open-mercato/ui/primitives/textarea'
|
|
19
18
|
import { CrudForm, type CrudCustomFieldRenderProps, type CrudField } from '@open-mercato/ui/backend/CrudForm'
|
|
20
19
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
21
20
|
import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
@@ -25,9 +24,10 @@ import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
|
25
24
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
26
25
|
import {
|
|
27
26
|
listShippingProviders,
|
|
28
|
-
type ProviderSettingField,
|
|
29
27
|
type ShippingProvider,
|
|
30
28
|
} from '../lib/providers'
|
|
29
|
+
import { isRecord } from '@open-mercato/shared/lib/utils'
|
|
30
|
+
import { renderProviderFieldInput } from './ProviderFieldInput'
|
|
31
31
|
|
|
32
32
|
type ShippingMethodRow = {
|
|
33
33
|
id: string
|
|
@@ -83,89 +83,6 @@ const DEFAULT_FORM: ShippingFormValues = {
|
|
|
83
83
|
providerSettings: {},
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
87
|
-
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function renderFieldInput(opts: {
|
|
91
|
-
field: ProviderSettingField
|
|
92
|
-
value: unknown
|
|
93
|
-
onChange: (next: unknown) => void
|
|
94
|
-
}) {
|
|
95
|
-
const { field, value, onChange } = opts
|
|
96
|
-
const common = { id: field.key, 'data-provider-setting': field.key }
|
|
97
|
-
switch (field.type) {
|
|
98
|
-
case 'textarea':
|
|
99
|
-
return (
|
|
100
|
-
<Textarea
|
|
101
|
-
{...common}
|
|
102
|
-
value={typeof value === 'string' ? value : ''}
|
|
103
|
-
onChange={(evt) => onChange(evt.target.value)}
|
|
104
|
-
placeholder={field.placeholder}
|
|
105
|
-
/>
|
|
106
|
-
)
|
|
107
|
-
case 'number':
|
|
108
|
-
return (
|
|
109
|
-
<Input
|
|
110
|
-
{...common}
|
|
111
|
-
type="number"
|
|
112
|
-
value={typeof value === 'number' || typeof value === 'string' ? String(value) : ''}
|
|
113
|
-
onChange={(evt) => onChange(evt.target.value === '' ? '' : Number(evt.target.value))}
|
|
114
|
-
placeholder={field.placeholder}
|
|
115
|
-
/>
|
|
116
|
-
)
|
|
117
|
-
case 'boolean':
|
|
118
|
-
return (
|
|
119
|
-
<div className="flex items-center gap-2 py-1">
|
|
120
|
-
<Switch
|
|
121
|
-
id={field.key}
|
|
122
|
-
checked={Boolean(value)}
|
|
123
|
-
onCheckedChange={(checked) => onChange(checked)}
|
|
124
|
-
/>
|
|
125
|
-
<Label htmlFor={field.key}>{field.placeholder ?? ''}</Label>
|
|
126
|
-
</div>
|
|
127
|
-
)
|
|
128
|
-
case 'select':
|
|
129
|
-
return (
|
|
130
|
-
<select
|
|
131
|
-
{...common}
|
|
132
|
-
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
|
|
133
|
-
value={typeof value === 'string' ? value : ''}
|
|
134
|
-
onChange={(evt) => onChange(evt.target.value)}
|
|
135
|
-
>
|
|
136
|
-
<option value="">—</option>
|
|
137
|
-
{(field.options ?? []).map((opt) => (
|
|
138
|
-
<option key={opt.value} value={opt.value}>
|
|
139
|
-
{opt.label}
|
|
140
|
-
</option>
|
|
141
|
-
))}
|
|
142
|
-
</select>
|
|
143
|
-
)
|
|
144
|
-
case 'secret':
|
|
145
|
-
return (
|
|
146
|
-
<Input
|
|
147
|
-
{...common}
|
|
148
|
-
type="password"
|
|
149
|
-
value={typeof value === 'string' ? value : ''}
|
|
150
|
-
onChange={(evt) => onChange(evt.target.value)}
|
|
151
|
-
placeholder={field.placeholder}
|
|
152
|
-
/>
|
|
153
|
-
)
|
|
154
|
-
case 'url':
|
|
155
|
-
case 'text':
|
|
156
|
-
default:
|
|
157
|
-
return (
|
|
158
|
-
<Input
|
|
159
|
-
{...common}
|
|
160
|
-
type={field.type === 'url' ? 'url' : 'text'}
|
|
161
|
-
value={typeof value === 'string' ? value : ''}
|
|
162
|
-
onChange={(evt) => onChange(evt.target.value)}
|
|
163
|
-
placeholder={field.placeholder}
|
|
164
|
-
/>
|
|
165
|
-
)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
86
|
type RateRule = {
|
|
170
87
|
id?: string
|
|
171
88
|
name?: string
|
|
@@ -395,7 +312,7 @@ function createShippingProviderSettingsRenderer(params: {
|
|
|
395
312
|
{field.description ? (
|
|
396
313
|
<p className="text-xs text-muted-foreground">{field.description}</p>
|
|
397
314
|
) : null}
|
|
398
|
-
{
|
|
315
|
+
{renderProviderFieldInput({
|
|
399
316
|
field,
|
|
400
317
|
value: fieldValue,
|
|
401
318
|
onChange: (next) => setValue({ ...settings, [field.key]: next }),
|