121 lines
4.6 KiB
TypeScript
121 lines
4.6 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useMemo, useState } from "react"
|
|
import { useQuery } from "convex/react"
|
|
import { format } from "date-fns"
|
|
import { ptBR } from "date-fns/locale/pt-BR"
|
|
|
|
import { api } from "@/convex/_generated/api"
|
|
import type { Id } from "@/convex/_generated/dataModel"
|
|
import { DEFAULT_TENANT_ID } from "@/lib/constants"
|
|
import { mapTicketsFromServerList } from "@/lib/mappers/ticket"
|
|
import type { Ticket } from "@/lib/schemas/ticket"
|
|
import { AppShell } from "@/components/app-shell"
|
|
import { SiteHeader } from "@/components/site-header"
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
import { Button } from "@/components/ui/button"
|
|
import { CalendarPlus } from "lucide-react"
|
|
import { useAuth } from "@/lib/auth-client"
|
|
import { AgendaFilters, AgendaFilterState, AgendaPeriod, defaultAgendaFilters } from "@/components/agenda/agenda-filters"
|
|
import { AgendaSummaryView } from "@/components/agenda/agenda-summary-view"
|
|
import { AgendaCalendarView } from "@/components/agenda/agenda-calendar-view"
|
|
import { buildAgendaDataset, type AgendaDataset } from "@/lib/agenda-utils"
|
|
|
|
export function AgendaPageClient() {
|
|
const [activeTab, setActiveTab] = useState<"summary" | "calendar">("summary")
|
|
const [filters, setFilters] = useState<AgendaFilterState>(defaultAgendaFilters)
|
|
|
|
const { convexUserId, session } = useAuth()
|
|
const userId = convexUserId as Id<"users"> | null
|
|
const tenantId = session?.user?.tenantId ?? DEFAULT_TENANT_ID
|
|
|
|
const ticketsArgs = userId
|
|
? {
|
|
tenantId,
|
|
viewerId: userId,
|
|
status: undefined,
|
|
priority: filters.priorities.length ? filters.priorities : undefined,
|
|
queueId: undefined,
|
|
channel: undefined,
|
|
assigneeId: filters.onlyMyTickets ? userId : undefined,
|
|
search: undefined,
|
|
}
|
|
: "skip"
|
|
|
|
const ticketsRaw = useQuery(api.tickets.list, ticketsArgs)
|
|
const mappedTickets = useMemo<Ticket[] | null>(() => {
|
|
if (!Array.isArray(ticketsRaw)) return null
|
|
return mapTicketsFromServerList(ticketsRaw as unknown[])
|
|
}, [ticketsRaw])
|
|
|
|
const [cachedTickets, setCachedTickets] = useState<Ticket[]>([])
|
|
|
|
useEffect(() => {
|
|
if (mappedTickets) {
|
|
setCachedTickets(mappedTickets)
|
|
}
|
|
}, [mappedTickets])
|
|
|
|
const effectiveTickets = mappedTickets ?? cachedTickets
|
|
const isInitialLoading = !mappedTickets && cachedTickets.length === 0 && ticketsArgs !== "skip"
|
|
|
|
const dataset: AgendaDataset = useMemo(
|
|
() => buildAgendaDataset(effectiveTickets, filters),
|
|
[effectiveTickets, filters]
|
|
)
|
|
|
|
const greeting = getGreetingMessage()
|
|
const firstName = session?.user?.name?.split(" ")[0] ?? session?.user?.email?.split("@")[0] ?? "equipe"
|
|
const rangeDescription = formatRangeDescription(filters.period, dataset.range)
|
|
const headerLead = `${greeting}, ${firstName}! ${rangeDescription}`
|
|
|
|
return (
|
|
<AppShell
|
|
header={
|
|
<SiteHeader
|
|
title="Agenda"
|
|
lead={headerLead}
|
|
secondaryAction={
|
|
<Button variant="secondary" size="sm" className="gap-2" disabled>
|
|
<CalendarPlus className="size-4" />
|
|
Em breve: novo compromisso
|
|
</Button>
|
|
}
|
|
/>
|
|
}
|
|
>
|
|
<div className="flex flex-col gap-6 px-4 lg:px-6">
|
|
<AgendaFilters filters={filters} onChange={setFilters} queues={dataset.availableQueues} />
|
|
<Tabs value={activeTab} onValueChange={(value) => setActiveTab(value as typeof activeTab)}>
|
|
<TabsList className="mb-4">
|
|
<TabsTrigger value="summary">Resumo</TabsTrigger>
|
|
<TabsTrigger value="calendar">Calendário</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="summary" className="focus-visible:outline-none">
|
|
<AgendaSummaryView data={dataset} isLoading={isInitialLoading} />
|
|
</TabsContent>
|
|
<TabsContent value="calendar" className="focus-visible:outline-none">
|
|
<AgendaCalendarView events={dataset.calendarEvents} range={dataset.range} />
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
</AppShell>
|
|
)
|
|
}
|
|
|
|
function getGreetingMessage(date: Date = new Date()) {
|
|
const hour = date.getHours()
|
|
if (hour < 12) return "Bom dia"
|
|
if (hour < 18) return "Boa tarde"
|
|
return "Boa noite"
|
|
}
|
|
|
|
function formatRangeDescription(period: AgendaPeriod, range: { start: Date; end: Date }) {
|
|
if (period === "today") {
|
|
return `Hoje é ${format(range.start, "eeee, d 'de' MMMM", { locale: ptBR })}`
|
|
}
|
|
if (period === "week") {
|
|
return `Semana de ${format(range.start, "d MMM", { locale: ptBR })} a ${format(range.end, "d MMM", { locale: ptBR })}`
|
|
}
|
|
return `Mês de ${format(range.start, "MMMM 'de' yyyy", { locale: ptBR })}`
|
|
}
|