Ajusta placeholders, formulários e widgets
This commit is contained in:
parent
343f0c8c64
commit
b94cea2f9a
33 changed files with 2122 additions and 462 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState, useTransition } from "react"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, useTransition } from "react"
|
||||
import { Controller, FormProvider, useFieldArray, useForm, type UseFormReturn } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import {
|
||||
|
|
@ -80,6 +80,7 @@ import { Textarea } from "@/components/ui/textarea"
|
|||
import { TimePicker } from "@/components/ui/time-picker"
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
import { useQuery, useMutation } from "convex/react"
|
||||
import { useAuth } from "@/lib/auth-client"
|
||||
import type { Id } from "@/convex/_generated/dataModel"
|
||||
|
|
@ -1691,6 +1692,7 @@ function CompanySheet({ tenantId, editor, onClose, onCreated, onUpdated }: Compa
|
|||
<AccordionTrigger className="py-3 font-semibold">Tipos de solicitação</AccordionTrigger>
|
||||
<AccordionContent className="pb-5">
|
||||
<CompanyRequestTypesControls tenantId={tenantId} companyId={editor?.mode === "edit" ? editor.company.id : null} />
|
||||
<CompanyExportTemplateSelector tenantId={tenantId} companyId={editor?.mode === "edit" ? editor.company.id : null} />
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
|
|
@ -2187,13 +2189,28 @@ type CompanyRequestTypesControlsProps = { tenantId?: string | null; companyId: s
|
|||
function CompanyRequestTypesControls({ tenantId, companyId }: CompanyRequestTypesControlsProps) {
|
||||
const { convexUserId } = useAuth()
|
||||
const canLoad = Boolean(tenantId && convexUserId)
|
||||
const ensureDefaults = useMutation(api.tickets.ensureTicketFormDefaults)
|
||||
const hasEnsuredRef = useRef(false)
|
||||
const settings = useQuery(
|
||||
api.ticketFormSettings.list,
|
||||
canLoad ? { tenantId: tenantId as string, viewerId: convexUserId as Id<"users"> } : "skip"
|
||||
) as Array<{ template: string; scope: string; companyId?: string | null; enabled: boolean; updatedAt: number }> | undefined
|
||||
const templates = useQuery(
|
||||
api.ticketFormTemplates.listActive,
|
||||
canLoad ? { tenantId: tenantId as string, viewerId: convexUserId as Id<"users"> } : "skip"
|
||||
) as Array<{ key: string; label: string }> | undefined
|
||||
const upsert = useMutation(api.ticketFormSettings.upsert)
|
||||
|
||||
const resolveEnabled = (template: "admissao" | "desligamento") => {
|
||||
useEffect(() => {
|
||||
if (!tenantId || !convexUserId || hasEnsuredRef.current) return
|
||||
hasEnsuredRef.current = true
|
||||
ensureDefaults({ tenantId, actorId: convexUserId as Id<"users"> }).catch((error) => {
|
||||
console.error("Falha ao garantir formulários padrão", error)
|
||||
hasEnsuredRef.current = false
|
||||
})
|
||||
}, [ensureDefaults, tenantId, convexUserId])
|
||||
|
||||
const resolveEnabled = (template: string) => {
|
||||
const scoped = (settings ?? []).filter((s) => s.template === template)
|
||||
const base = true
|
||||
if (!companyId) return base
|
||||
|
|
@ -2203,10 +2220,7 @@ function CompanyRequestTypesControls({ tenantId, companyId }: CompanyRequestType
|
|||
return typeof latest?.enabled === "boolean" ? latest.enabled : base
|
||||
}
|
||||
|
||||
const admissaoEnabled = resolveEnabled("admissao")
|
||||
const desligamentoEnabled = resolveEnabled("desligamento")
|
||||
|
||||
const handleToggle = async (template: "admissao" | "desligamento", enabled: boolean) => {
|
||||
const handleToggle = async (template: string, enabled: boolean) => {
|
||||
if (!tenantId || !convexUserId || !companyId) return
|
||||
try {
|
||||
await upsert({
|
||||
|
|
@ -2227,24 +2241,113 @@ function CompanyRequestTypesControls({ tenantId, companyId }: CompanyRequestType
|
|||
return (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-muted-foreground">Defina quais tipos de solicitação estão disponíveis para colaboradores/gestores desta empresa. Administradores e agentes sempre veem todas as opções.</p>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<label className="flex items-center gap-2 text-sm text-foreground">
|
||||
<Checkbox
|
||||
checked={admissaoEnabled}
|
||||
onCheckedChange={(v) => handleToggle("admissao", Boolean(v))}
|
||||
disabled={!companyId}
|
||||
/>
|
||||
<span>Admissão de colaborador</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-sm text-foreground">
|
||||
<Checkbox
|
||||
checked={desligamentoEnabled}
|
||||
onCheckedChange={(v) => handleToggle("desligamento", Boolean(v))}
|
||||
disabled={!companyId}
|
||||
/>
|
||||
<span>Desligamento de colaborador</span>
|
||||
</label>
|
||||
</div>
|
||||
{!templates ? (
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-10 w-full rounded-lg" />
|
||||
<Skeleton className="h-10 w-full rounded-lg" />
|
||||
</div>
|
||||
) : templates.length === 0 ? (
|
||||
<p className="text-sm text-neutral-500">Nenhum formulário disponível.</p>
|
||||
) : (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
{templates.map((template) => {
|
||||
const enabled = resolveEnabled(template.key)
|
||||
return (
|
||||
<label key={template.key} className="flex items-center gap-2 text-sm text-foreground">
|
||||
<Checkbox
|
||||
checked={enabled}
|
||||
onCheckedChange={(v) => handleToggle(template.key, Boolean(v))}
|
||||
disabled={!companyId}
|
||||
/>
|
||||
<span>{template.label}</span>
|
||||
</label>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type CompanyExportTemplateSelectorProps = { tenantId?: string | null; companyId: string | null }
|
||||
function CompanyExportTemplateSelector({ tenantId, companyId }: CompanyExportTemplateSelectorProps) {
|
||||
const { convexUserId } = useAuth()
|
||||
const canLoad = Boolean(tenantId && convexUserId)
|
||||
const templates = useQuery(
|
||||
api.deviceExportTemplates.list,
|
||||
canLoad
|
||||
? {
|
||||
tenantId: tenantId as string,
|
||||
viewerId: convexUserId as Id<"users">,
|
||||
companyId: companyId ? (companyId as unknown as Id<"companies">) : undefined,
|
||||
includeInactive: true,
|
||||
}
|
||||
: "skip"
|
||||
) as Array<{ id: string; name: string; companyId: string | null; isDefault: boolean; description?: string }> | undefined
|
||||
const setDefaultTemplate = useMutation(api.deviceExportTemplates.setDefault)
|
||||
const clearDefaultTemplate = useMutation(api.deviceExportTemplates.clearCompanyDefault)
|
||||
|
||||
const companyTemplates = useMemo(() => {
|
||||
if (!templates || !companyId) return []
|
||||
return templates.filter((tpl) => String(tpl.companyId ?? "") === String(companyId))
|
||||
}, [templates, companyId])
|
||||
|
||||
const companyDefault = useMemo(() => companyTemplates.find((tpl) => tpl.isDefault) ?? null, [companyTemplates])
|
||||
|
||||
const handleChange = async (value: string) => {
|
||||
if (!tenantId || !convexUserId || !companyId) return
|
||||
try {
|
||||
if (value === "inherit") {
|
||||
await clearDefaultTemplate({
|
||||
tenantId,
|
||||
actorId: convexUserId as Id<"users">,
|
||||
companyId: companyId as unknown as Id<"companies">,
|
||||
})
|
||||
toast.success("Template desta empresa voltou a herdar o padrão global.")
|
||||
} else {
|
||||
await setDefaultTemplate({
|
||||
tenantId,
|
||||
actorId: convexUserId as Id<"users">,
|
||||
templateId: value as Id<"deviceExportTemplates">,
|
||||
})
|
||||
toast.success("Template aplicado para esta empresa.")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Falha ao definir template de exportação", error)
|
||||
toast.error("Não foi possível atualizar o template.")
|
||||
}
|
||||
}
|
||||
|
||||
const selectValue = companyDefault ? companyDefault.id : "inherit"
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Defina o template padrão das exportações de inventário para esta empresa. Ao herdar, o template global será utilizado.
|
||||
</p>
|
||||
{!companyId ? (
|
||||
<p className="text-xs text-neutral-500">Salve a empresa antes de configurar o template.</p>
|
||||
) : !templates ? (
|
||||
<Skeleton className="h-10 w-full rounded-md" />
|
||||
) : companyTemplates.length === 0 ? (
|
||||
<p className="text-xs text-neutral-500">
|
||||
Nenhum template específico para esta empresa. Crie um template em <span className="font-semibold">Dispositivos > Exportações</span> e associe a esta empresa para habilitar aqui.
|
||||
</p>
|
||||
) : (
|
||||
<Select value={selectValue} onValueChange={handleChange} disabled={!companyId}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Herdar template global" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="inherit">Herdar template global</SelectItem>
|
||||
{companyTemplates.map((tpl) => (
|
||||
<SelectItem key={tpl.id} value={tpl.id}>
|
||||
{tpl.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue