Ajusta placeholders, formulários e widgets
This commit is contained in:
parent
343f0c8c64
commit
b94cea2f9a
33 changed files with 2122 additions and 462 deletions
|
|
@ -29,6 +29,7 @@ type Field = {
|
|||
required: boolean
|
||||
options: FieldOption[]
|
||||
order: number
|
||||
scope: string
|
||||
}
|
||||
|
||||
const TYPE_LABELS: Record<Field["type"], string> = {
|
||||
|
|
@ -48,6 +49,25 @@ export function FieldsManager() {
|
|||
convexUserId ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip"
|
||||
) as Field[] | undefined
|
||||
|
||||
const templates = useQuery(
|
||||
api.ticketFormTemplates.listActive,
|
||||
convexUserId ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip"
|
||||
) as Array<{ id: string; key: string; label: string }> | undefined
|
||||
|
||||
const scopeOptions = useMemo(
|
||||
() => [
|
||||
{ value: "all", label: "Todos os formulários" },
|
||||
...((templates ?? []).map((tpl) => ({ value: tpl.key, label: tpl.label })) ?? []),
|
||||
],
|
||||
[templates]
|
||||
)
|
||||
|
||||
const templateLabelByKey = useMemo(() => {
|
||||
const map = new Map<string, string>()
|
||||
templates?.forEach((tpl) => map.set(tpl.key, tpl.label))
|
||||
return map
|
||||
}, [templates])
|
||||
|
||||
const createField = useMutation(api.fields.create)
|
||||
const updateField = useMutation(api.fields.update)
|
||||
const removeField = useMutation(api.fields.remove)
|
||||
|
|
@ -58,8 +78,10 @@ export function FieldsManager() {
|
|||
const [type, setType] = useState<Field["type"]>("text")
|
||||
const [required, setRequired] = useState(false)
|
||||
const [options, setOptions] = useState<FieldOption[]>([])
|
||||
const [scopeSelection, setScopeSelection] = useState<string>("all")
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [editingField, setEditingField] = useState<Field | null>(null)
|
||||
const [editingScope, setEditingScope] = useState<string>("all")
|
||||
|
||||
const totals = useMemo(() => {
|
||||
if (!fields) return { total: 0, required: 0, select: 0 }
|
||||
|
|
@ -76,6 +98,7 @@ export function FieldsManager() {
|
|||
setType("text")
|
||||
setRequired(false)
|
||||
setOptions([])
|
||||
setScopeSelection("all")
|
||||
}
|
||||
|
||||
const normalizeOptions = (source: FieldOption[]) =>
|
||||
|
|
@ -97,6 +120,7 @@ export function FieldsManager() {
|
|||
return
|
||||
}
|
||||
const preparedOptions = type === "select" ? normalizeOptions(options) : undefined
|
||||
const scopeValue = scopeSelection === "all" ? undefined : scopeSelection
|
||||
setSaving(true)
|
||||
toast.loading("Criando campo...", { id: "field" })
|
||||
try {
|
||||
|
|
@ -108,6 +132,7 @@ export function FieldsManager() {
|
|||
type,
|
||||
required,
|
||||
options: preparedOptions,
|
||||
scope: scopeValue,
|
||||
})
|
||||
toast.success("Campo criado", { id: "field" })
|
||||
resetForm()
|
||||
|
|
@ -147,6 +172,7 @@ export function FieldsManager() {
|
|||
setType(field.type)
|
||||
setRequired(field.required)
|
||||
setOptions(field.options)
|
||||
setEditingScope(field.scope ?? "all")
|
||||
}
|
||||
|
||||
const handleUpdate = async () => {
|
||||
|
|
@ -160,6 +186,7 @@ export function FieldsManager() {
|
|||
return
|
||||
}
|
||||
const preparedOptions = type === "select" ? normalizeOptions(options) : undefined
|
||||
const scopeValue = editingScope === "all" ? undefined : editingScope
|
||||
setSaving(true)
|
||||
toast.loading("Atualizando campo...", { id: "field-edit" })
|
||||
try {
|
||||
|
|
@ -172,6 +199,7 @@ export function FieldsManager() {
|
|||
type,
|
||||
required,
|
||||
options: preparedOptions,
|
||||
scope: scopeValue,
|
||||
})
|
||||
toast.success("Campo atualizado", { id: "field-edit" })
|
||||
setEditingField(null)
|
||||
|
|
@ -304,6 +332,21 @@ export function FieldsManager() {
|
|||
Campo obrigatório na abertura
|
||||
</Label>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Aplicar em</Label>
|
||||
<Select value={scopeSelection} onValueChange={setScopeSelection}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Todos os formulários" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{scopeOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
|
|
@ -378,7 +421,12 @@ export function FieldsManager() {
|
|||
</CardHeader>
|
||||
</Card>
|
||||
) : (
|
||||
fields.map((field, index) => (
|
||||
fields.map((field, index) => {
|
||||
const scopeLabel =
|
||||
field.scope === "all"
|
||||
? "Todos os formulários"
|
||||
: templateLabelByKey.get(field.scope) ?? `Formulário: ${field.scope}`
|
||||
return (
|
||||
<Card key={field.id} className="border-slate-200">
|
||||
<CardHeader>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
|
|
@ -395,6 +443,9 @@ export function FieldsManager() {
|
|||
) : null}
|
||||
</div>
|
||||
<CardDescription className="text-neutral-600">Identificador: {field.key}</CardDescription>
|
||||
<Badge variant="secondary" className="rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-semibold text-neutral-700">
|
||||
{scopeLabel}
|
||||
</Badge>
|
||||
{field.description ? (
|
||||
<p className="text-sm text-neutral-600">{field.description}</p>
|
||||
) : null}
|
||||
|
|
@ -446,7 +497,8 @@ export function FieldsManager() {
|
|||
</CardContent>
|
||||
) : null}
|
||||
</Card>
|
||||
))
|
||||
)
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
|
@ -487,6 +539,21 @@ export function FieldsManager() {
|
|||
Campo obrigatório na abertura
|
||||
</Label>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Aplicar em</Label>
|
||||
<Select value={editingScope} onValueChange={setEditingScope}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Todos os formulários" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{scopeOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue