feat(calendar): migrate to react-day-picker v9 and polish UI - Update classNames and CSS import (style.css) - Custom Dropdown via shadcn Select - Nav arrows aligned with caption (around) - Today highlight with cyan tone, weekdays in sentence case - Wider layout to avoid overflow; remove inner wrapper chore(tickets): make 'Patrimônio do computador (se houver)' optional - Backend hotfix to enforce optional + label on existing tenants - Hide required asterisk for this field in portal/new-ticket refactor(new-ticket): remove channel dropdown from admin/agent flow - Keep default channel as MANUAL feat(ux): simplify requester section and enlarge combobox trigger - Remove RequesterPreview redundancy; show company badge in trigger
70 lines
2.1 KiB
TypeScript
70 lines
2.1 KiB
TypeScript
import type { Id } from "@/convex/_generated/dataModel"
|
|
|
|
import type { TicketFormFieldDefinition } from "./ticket-form-types"
|
|
|
|
type CustomFieldPayload = { fieldId: Id<"ticketFields">; value: unknown }
|
|
|
|
type NormalizeResult =
|
|
| { ok: true; payload: CustomFieldPayload[] }
|
|
| { ok: false; message: string }
|
|
|
|
function isEmptyValue(value: unknown): boolean {
|
|
if (value === undefined || value === null) return true
|
|
if (typeof value === "string" && value.trim().length === 0) return true
|
|
return false
|
|
}
|
|
|
|
export function normalizeCustomFieldInputs(
|
|
fields: TicketFormFieldDefinition[],
|
|
values: Record<string, unknown>
|
|
): NormalizeResult {
|
|
const payload: CustomFieldPayload[] = []
|
|
|
|
for (const field of fields) {
|
|
const raw = values[field.id]
|
|
if (field.type === "boolean") {
|
|
const boolValue = Boolean(raw)
|
|
payload.push({ fieldId: field.id as Id<"ticketFields">, value: boolValue })
|
|
continue
|
|
}
|
|
|
|
if (isEmptyValue(raw)) {
|
|
if (field.required) {
|
|
return { ok: false, message: `Preencha o campo "${field.label}".` }
|
|
}
|
|
continue
|
|
}
|
|
|
|
if (field.type === "number") {
|
|
const normalized = typeof raw === "number" ? raw : Number(String(raw).replace(",", "."))
|
|
if (!Number.isFinite(normalized)) {
|
|
return { ok: false, message: `Informe um valor numérico válido para "${field.label}".` }
|
|
}
|
|
payload.push({ fieldId: field.id as Id<"ticketFields">, value: normalized })
|
|
continue
|
|
}
|
|
|
|
if (field.type === "date") {
|
|
payload.push({ fieldId: field.id as Id<"ticketFields">, value: String(raw) })
|
|
continue
|
|
}
|
|
|
|
const value = typeof raw === "string" ? raw.trim() : raw
|
|
payload.push({ fieldId: field.id as Id<"ticketFields">, value })
|
|
}
|
|
|
|
return { ok: true, payload }
|
|
}
|
|
|
|
export function hasMissingRequiredCustomFields(
|
|
fields: TicketFormFieldDefinition[],
|
|
values: Record<string, unknown>
|
|
): boolean {
|
|
return fields.some((field) => {
|
|
if (!field.required || field.type === "boolean") {
|
|
return false
|
|
}
|
|
const value = values[field.id]
|
|
return isEmptyValue(value)
|
|
})
|
|
}
|