admin: remove 'Espaço (ID interno)' from Convites and improve filter bar alignment; tickets: fix running timer by optimistic updating start/pause times
This commit is contained in:
parent
a325d612cb
commit
7c3bf00790
2 changed files with 41 additions and 20 deletions
|
|
@ -884,8 +884,8 @@ async function handleDeleteUser() {
|
||||||
Novo usuário
|
Novo usuário
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm sm:flex-row sm:items-center sm:justify-between">
|
<div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm md:grid md:grid-cols-[minmax(0,1fr)_auto_auto_auto_auto] md:items-center md:gap-3">
|
||||||
<div className="relative w-full sm:max-w-xs">
|
<div className="relative w-full md:max-w-sm">
|
||||||
<IconSearch className="text-muted-foreground pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2" />
|
<IconSearch className="text-muted-foreground pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2" />
|
||||||
<Input
|
<Input
|
||||||
value={teamSearch}
|
value={teamSearch}
|
||||||
|
|
@ -894,7 +894,7 @@ async function handleDeleteUser() {
|
||||||
className="h-9 pl-9"
|
className="h-9 pl-9"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="mt-3 flex flex-wrap items-center gap-3 md:mt-0">
|
||||||
<Select value={teamRoleFilter} onValueChange={(value) => setTeamRoleFilter(value as "all" | RoleOption)}>
|
<Select value={teamRoleFilter} onValueChange={(value) => setTeamRoleFilter(value as "all" | RoleOption)}>
|
||||||
<SelectTrigger className="h-9 w-full sm:w-48">
|
<SelectTrigger className="h-9 w-full sm:w-48">
|
||||||
<SelectValue placeholder="Todos os papéis" />
|
<SelectValue placeholder="Todos os papéis" />
|
||||||
|
|
@ -1102,8 +1102,8 @@ async function handleDeleteUser() {
|
||||||
Novo usuário
|
Novo usuário
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm sm:flex-row sm:items-center sm:justify-between">
|
<div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm md:grid md:grid-cols-[minmax(0,1fr)_auto_auto_auto_auto] md:items-center md:gap-3">
|
||||||
<div className="relative w-full sm:max-w-xs">
|
<div className="relative w-full md:max-w-sm">
|
||||||
<IconSearch className="text-muted-foreground pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2" />
|
<IconSearch className="text-muted-foreground pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2" />
|
||||||
<Input
|
<Input
|
||||||
value={peopleSearch}
|
value={peopleSearch}
|
||||||
|
|
@ -1112,7 +1112,7 @@ async function handleDeleteUser() {
|
||||||
className="h-9 pl-9"
|
className="h-9 pl-9"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="mt-3 flex flex-wrap items-center gap-3 md:mt-0">
|
||||||
<Select value={peopleRoleFilter} onValueChange={(value) => setPeopleRoleFilter(value as "all" | "manager" | "collaborator")}>
|
<Select value={peopleRoleFilter} onValueChange={(value) => setPeopleRoleFilter(value as "all" | "manager" | "collaborator")}>
|
||||||
<SelectTrigger className="h-9 w-full sm:w-48">
|
<SelectTrigger className="h-9 w-full sm:w-48">
|
||||||
<SelectValue placeholder="Perfil" />
|
<SelectValue placeholder="Perfil" />
|
||||||
|
|
@ -1423,7 +1423,7 @@ async function handleDeleteUser() {
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<form
|
<form
|
||||||
onSubmit={handleInviteSubmit}
|
onSubmit={handleInviteSubmit}
|
||||||
className="grid gap-4 md:grid-cols-2 xl:grid-cols-[minmax(0,2.4fr)_minmax(0,2fr)_minmax(0,1.2fr)_minmax(0,1.6fr)_minmax(0,1.2fr)_auto]"
|
className="grid gap-4 md:grid-cols-2 xl:grid-cols-[minmax(0,2.4fr)_minmax(0,2fr)_minmax(0,1.2fr)_minmax(0,1.2fr)_auto]"
|
||||||
>
|
>
|
||||||
<div className="grid gap-2 md:col-span-2 xl:col-auto">
|
<div className="grid gap-2 md:col-span-2 xl:col-auto">
|
||||||
<Label htmlFor="invite-email">E-mail corporativo</Label>
|
<Label htmlFor="invite-email">E-mail corporativo</Label>
|
||||||
|
|
@ -1463,19 +1463,6 @@ async function handleDeleteUser() {
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-2 md:col-span-2 xl:col-auto">
|
|
||||||
<Label htmlFor="invite-tenant">Espaço (ID interno)</Label>
|
|
||||||
<Input
|
|
||||||
id="invite-tenant"
|
|
||||||
value={tenantId}
|
|
||||||
onChange={(event) => setTenantId(event.target.value)}
|
|
||||||
placeholder="ex.: principal"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<p className="text-xs text-neutral-500">
|
|
||||||
Use este campo apenas se trabalhar com múltiplos espaços de clientes. Caso contrário, mantenha o valor padrão.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="grid gap-2 md:col-span-1 xl:col-auto">
|
<div className="grid gap-2 md:col-span-1 xl:col-auto">
|
||||||
<Label>Expira em</Label>
|
<Label>Expira em</Label>
|
||||||
<Select value={expiresInDays} onValueChange={setExpiresInDays}>
|
<Select value={expiresInDays} onValueChange={setExpiresInDays}>
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,27 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
|
||||||
} else {
|
} else {
|
||||||
toast.success("Atendimento iniciado", { id: "work" })
|
toast.success("Atendimento iniciado", { id: "work" })
|
||||||
}
|
}
|
||||||
|
// Otimização local: garantir startedAt correto imediatamente
|
||||||
|
const startedAtMs = typeof result?.startedAt === "number" ? result.startedAt : Date.now()
|
||||||
|
const sessionId = (result as { sessionId?: unknown })?.sessionId as Id<"ticketWorkSessions"> | undefined
|
||||||
|
setWorkSummary((prev) => {
|
||||||
|
const base: WorkSummarySnapshot = prev ?? {
|
||||||
|
ticketId: ticket.id as Id<"tickets">,
|
||||||
|
totalWorkedMs: 0,
|
||||||
|
internalWorkedMs: 0,
|
||||||
|
externalWorkedMs: 0,
|
||||||
|
activeSession: null,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
activeSession: {
|
||||||
|
id: (sessionId as Id<"ticketWorkSessions">) ?? (base.activeSession?.id as Id<"ticketWorkSessions">),
|
||||||
|
agentId: convexUserId as Id<"users">,
|
||||||
|
startedAt: startedAtMs,
|
||||||
|
workType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : "Não foi possível atualizar o atendimento"
|
const message = error instanceof Error ? error.message : "Não foi possível atualizar o atendimento"
|
||||||
toast.error(message, { id: "work" })
|
toast.error(message, { id: "work" })
|
||||||
|
|
@ -454,6 +475,19 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
|
||||||
toast.success("Atendimento pausado", { id: "work" })
|
toast.success("Atendimento pausado", { id: "work" })
|
||||||
}
|
}
|
||||||
setPauseDialogOpen(false)
|
setPauseDialogOpen(false)
|
||||||
|
// Otimização local: aplicar duração retornada no total e limpar sessão ativa
|
||||||
|
const delta = typeof (result as { durationMs?: unknown })?.durationMs === "number" ? (result as { durationMs?: number }).durationMs! : 0
|
||||||
|
setWorkSummary((prev) => {
|
||||||
|
if (!prev) return prev
|
||||||
|
const workType = prev.activeSession?.workType ?? "INTERNAL"
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
totalWorkedMs: prev.totalWorkedMs + delta,
|
||||||
|
internalWorkedMs: prev.internalWorkedMs + (workType === "INTERNAL" ? delta : 0),
|
||||||
|
externalWorkedMs: prev.externalWorkedMs + (workType === "EXTERNAL" ? delta : 0),
|
||||||
|
activeSession: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : "Não foi possível atualizar o atendimento"
|
const message = error instanceof Error ? error.message : "Não foi possível atualizar o atendimento"
|
||||||
toast.error(message, { id: "work" })
|
toast.error(message, { id: "work" })
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue