Add multi_value technical inputs and FA AI prompts.
Support duplicatable list fields in form builder and value UIs, and guide fill prompts to put leftover specs in سایر ویژگیها. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
0bbc5bbc12
commit
592540cc7e
@@ -28,7 +28,8 @@ export function valuesToFormState(
|
||||
|
||||
if (form) {
|
||||
for (const field of form.fields) {
|
||||
state[field.key] = field.type === 'multi_select' ? [] : ''
|
||||
state[field.key] =
|
||||
field.type === 'multi_select' || field.type === 'multi_value' ? [] : ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +44,19 @@ export function valuesToFormState(
|
||||
export function formStateToPayload(values: ProductTechnicalFormValues) {
|
||||
return {
|
||||
values: Object.entries(values)
|
||||
.filter(([, value]) => {
|
||||
if (Array.isArray(value)) return value.length > 0
|
||||
return value !== ''
|
||||
.map(([fieldKey, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
return {
|
||||
fieldKey,
|
||||
value: value.map((item) => String(item).trim()).filter(Boolean),
|
||||
}
|
||||
}
|
||||
return { fieldKey, value }
|
||||
})
|
||||
.map(([fieldKey, value]) => ({ fieldKey, value })),
|
||||
.filter((entry) => {
|
||||
if (Array.isArray(entry.value)) return entry.value.length > 0
|
||||
return entry.value !== ''
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,12 @@ import type { UserProductListItem, UserProductStatus } from '../types/userProduc
|
||||
|
||||
export type UserProductCondition = 'new' | 'stock' | 'needs_repair' | 'scrap'
|
||||
export type UserProductPriceCurrency = 'IRT' | 'USD' | 'EUR' | 'AED'
|
||||
export type TechnicalFieldType = 'text' | 'textarea' | 'select' | 'multi_select'
|
||||
export type TechnicalFieldType =
|
||||
| 'text'
|
||||
| 'textarea'
|
||||
| 'select'
|
||||
| 'multi_select'
|
||||
| 'multi_value'
|
||||
|
||||
export interface UserProductCategoryOption {
|
||||
id: string
|
||||
@@ -109,6 +114,44 @@ function businessPath(suffix = '') {
|
||||
return `/businesses/${businessId}/user-products${suffix}`
|
||||
}
|
||||
|
||||
/** Decode free-text list values stored as JSON in text_value. */
|
||||
export function decodeMultiValueText(raw: string | null | undefined): string[] {
|
||||
if (raw == null) return []
|
||||
const trimmed = raw.trim()
|
||||
if (!trimmed) return []
|
||||
|
||||
if (trimmed.startsWith('[')) {
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed) as unknown
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed.map((item) => String(item).trim()).filter(Boolean)
|
||||
}
|
||||
} catch {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
return [trimmed]
|
||||
}
|
||||
|
||||
export function hydrateTechnicalFormValues(
|
||||
fields: TechnicalFormField[],
|
||||
values: TechnicalFormValues,
|
||||
): TechnicalFormValues {
|
||||
const next: TechnicalFormValues = { ...values }
|
||||
for (const field of fields) {
|
||||
if (field.type !== 'multi_value') continue
|
||||
const current = next[field.id]
|
||||
if (Array.isArray(current)) continue
|
||||
if (typeof current === 'string') {
|
||||
next[field.id] = decodeMultiValueText(current)
|
||||
} else {
|
||||
next[field.id] = []
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
export function buildTechnicalValuesPayload(
|
||||
fields: TechnicalFormField[],
|
||||
values: TechnicalFormValues,
|
||||
@@ -125,6 +168,15 @@ export function buildTechnicalValuesPayload(
|
||||
continue
|
||||
}
|
||||
|
||||
if (field.type === 'multi_value') {
|
||||
const items = Array.isArray(value)
|
||||
? value.map((item) => String(item).trim()).filter(Boolean)
|
||||
: []
|
||||
if (!items.length) continue
|
||||
payload.push({ fieldId: field.id, textValue: JSON.stringify(items) })
|
||||
continue
|
||||
}
|
||||
|
||||
if (field.type === 'select') {
|
||||
const optionId = typeof value === 'string' ? value.trim() : ''
|
||||
if (!optionId) continue
|
||||
|
||||
Reference in New Issue
Block a user