diff --git a/src/app/(authenticated)/admin/food/_components/orders-report-modal.tsx b/src/app/(authenticated)/admin/food/_components/orders-report-modal.tsx
index 242216d..4426440 100644
--- a/src/app/(authenticated)/admin/food/_components/orders-report-modal.tsx
+++ b/src/app/(authenticated)/admin/food/_components/orders-report-modal.tsx
@@ -104,6 +104,9 @@ export function OrdersReportModal({
// Seleção de itens na tabela para cálculo da soma e exportação
const [deselectedOrderIds, setDeselectedOrderIds] = useState>(new Set())
+ // Opção de agrupar por usuário (ativo por padrão)
+ const [groupByUser, setGroupByUser] = useState(true)
+
// Paginação
const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(25)
@@ -157,6 +160,7 @@ export function OrdersReportModal({
setSelectedUserIds([])
setUserSearchText(initialUserSearch ? initialUserSearch.trim() : "")
setDeselectedOrderIds(new Set())
+ setGroupByUser(true)
setPage(1)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -196,6 +200,85 @@ export function OrdersReportModal({
const isAllSelected = orders.length > 0 && selectedOrders.length === orders.length
const isSomeSelected = selectedOrders.length > 0 && selectedOrders.length < orders.length
+ // Agrupamento de pedidos por colaborador
+ const groupedUsers = useMemo(() => {
+ const map = new Map<
+ string,
+ {
+ userId: string
+ name: string
+ email: string
+ matricula: string
+ setor: string
+ empresa: string
+ filial: string
+ orders: typeof orders
+ }
+ >()
+
+ for (const order of orders) {
+ const user = order.user
+ const key = user?.id ?? order.userId ?? "unknown"
+ const existing = map.get(key)
+ if (existing) {
+ existing.orders.push(order)
+ } else {
+ const matricula = user?.matricula ?? "-"
+ const name =
+ `${user?.firstName ?? ""} ${user?.lastName ?? ""}`.trim() ||
+ user?.email ||
+ "Não identificado"
+ const email = user?.email ?? ""
+ const setor = user?.setor ?? "-"
+ const empresa = user?.filial?.empresa?.name ?? user?.enterprise ?? "-"
+ const filial = user?.filial ? `${user.filial.name} (${user.filial.code})` : ""
+
+ map.set(key, {
+ userId: key,
+ name,
+ email,
+ matricula,
+ setor,
+ empresa,
+ filial,
+ orders: [order],
+ })
+ }
+ }
+
+ return Array.from(map.values())
+ .map((item) => {
+ const selectedOrdersOfUser = item.orders.filter((o) => !deselectedOrderIds.has(o.id))
+ const totalValue = item.orders.reduce(
+ (acc, o) => acc + (o.unitPrice ?? o.menuItem?.price ?? 0),
+ 0,
+ )
+ const selectedTotalValue = selectedOrdersOfUser.reduce(
+ (acc, o) => acc + (o.unitPrice ?? o.menuItem?.price ?? 0),
+ 0,
+ )
+ const isUserAllSelected =
+ selectedOrdersOfUser.length === item.orders.length && item.orders.length > 0
+ const isUserSomeSelected =
+ selectedOrdersOfUser.length > 0 && selectedOrdersOfUser.length < item.orders.length
+
+ return {
+ ...item,
+ selectedOrders: selectedOrdersOfUser,
+ ordersCount: item.orders.length,
+ totalValue,
+ selectedTotalValue,
+ isAllSelected: isUserAllSelected,
+ isSomeSelected: isUserSomeSelected,
+ }
+ })
+ .sort((a, b) => a.name.localeCompare(b.name, "pt-BR"))
+ }, [orders, deselectedOrderIds])
+
+ const selectedGroupedUsers = useMemo(() => {
+ return groupedUsers.filter((u) => u.selectedOrders.length > 0)
+ }, [groupedUsers])
+
const handleToggleSelectAll = () => {
if (isAllSelected) {
setDeselectedOrderIds(new Set(orders.map((o) => o.id)))
@@ -216,6 +299,20 @@ export function OrdersReportModal({
})
}
+ const handleToggleUserGroup = (userGroup: (typeof groupedUsers)[number]) => {
+ setDeselectedOrderIds((prev) => {
+ const next = new Set(prev)
+ if (userGroup.isAllSelected) {
+ // Desmarcar todos os pedidos deste colaborador
+ userGroup.orders.forEach((o) => next.add(o.id))
+ } else {
+ // Marcar todos os pedidos deste colaborador
+ userGroup.orders.forEach((o) => next.delete(o.id))
+ }
+ return next
+ })
+ }
+
const handleToggleUser = (userId: string) => {
setSelectedUserIds((prev) =>
prev.includes(userId) ? prev.filter((id) => id !== userId) : [...prev, userId]
@@ -237,6 +334,7 @@ export function OrdersReportModal({
setSelectedRestaurant(ALL_FILTER)
setSelectedFilial(ALL_FILTER)
setDeselectedOrderIds(new Set())
+ setGroupByUser(true)
setPage(1)
}
@@ -249,12 +347,19 @@ export function OrdersReportModal({
endDate.getTime() !== defaultEnd.getTime()
// Paginação
- const totalPages = Math.max(1, Math.ceil(orders.length / pageSize))
+ const totalItems = groupByUser ? groupedUsers.length : orders.length
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize))
+
const paginatedOrders = useMemo(() => {
const startIndex = (page - 1) * pageSize
return orders.slice(startIndex, startIndex + pageSize)
}, [orders, page, pageSize])
+ const paginatedGroupedUsers = useMemo(() => {
+ const startIndex = (page - 1) * pageSize
+ return groupedUsers.slice(startIndex, startIndex + pageSize)
+ }, [groupedUsers, page, pageSize])
+
const formatCurrency = (val: number) => {
return new Intl.NumberFormat("pt-BR", {
style: "currency",
@@ -272,53 +377,128 @@ export function OrdersReportModal({
}
try {
- const detailedRows = ordersToExport.map((order) => {
- const user = order.user
- const matricula = user?.matricula ?? ""
- const nome = `${user?.firstName ?? ""} ${user?.lastName ?? ""}`.trim() || "Não identificado"
- const email = user?.email ?? ""
- const setor = user?.setor ?? ""
- const empresa = user?.filial?.empresa?.name ?? user?.enterprise ?? ""
- const filial = user?.filial ? `${user.filial.name} (${user.filial.code})` : ""
- const restaurante = order.restaurant?.name ?? "Restaurante excluído"
- const prato = order.menuItem?.name ?? ""
- const valor = order.unitPrice ?? order.menuItem?.price ?? 0
-
- const statusLabel =
- order.status === "DELIVERED"
- ? "Entregue"
- : order.status === "CONFIRMED"
- ? "Confirmado"
- : order.status === "PENDING"
- ? "Pendente"
- : order.status === "CANCELLED"
- ? "Cancelado"
- : order.status
-
- return {
- "Data do Pedido": formatOrderDate(order.orderDate),
- "Hora": order.orderTime ? format(new Date(order.orderTime), "HH:mm", { locale: ptBR }) : "",
- "Matrícula": matricula,
- "Colaborador": nome,
- "E-mail": email,
- "Setor": setor,
- "Empresa": empresa,
- "Filial": filial,
- "Restaurante": restaurante,
- "Prato": prato,
- "Valor (R$)": valor,
- "Status": statusLabel,
- }
- })
-
const wb = XLSX.utils.book_new()
- const wsDetailed = XLSX.utils.json_to_sheet(detailedRows)
- XLSX.utils.book_append_sheet(wb, wsDetailed, "Pedidos")
-
const fileStart = format(startDate, "yyyy-MM-dd")
const fileEnd = format(endDate, "yyyy-MM-dd")
- XLSX.writeFile(wb, `pedidos_almoco_${fileStart}_a_${fileEnd}.xlsx`)
- toast.success(`Planilha exportada com sucesso! (${ordersToExport.length} pedidos)`)
+
+ if (groupByUser) {
+ // Agrupar por usuário somando os gastos totais do período
+ const userGroupsMap = new Map<
+ string,
+ {
+ matricula: string
+ nome: string
+ email: string
+ setor: string
+ empresa: string
+ filial: string
+ ordersCount: number
+ totalValue: number
+ }
+ >()
+
+ for (const order of ordersToExport) {
+ const user = order.user
+ const key = user?.id ?? order.userId ?? "unknown"
+ const val = order.unitPrice ?? order.menuItem?.price ?? 0
+
+ const existing = userGroupsMap.get(key)
+ if (existing) {
+ existing.ordersCount += 1
+ existing.totalValue += val
+ } else {
+ const matricula = user?.matricula ?? ""
+ const nome =
+ `${user?.firstName ?? ""} ${user?.lastName ?? ""}`.trim() ||
+ user?.email ||
+ "Não identificado"
+ const email = user?.email ?? ""
+ const setor = user?.setor ?? ""
+ const empresa = user?.filial?.empresa?.name ?? user?.enterprise ?? ""
+ const filial = user?.filial ? `${user.filial.name} (${user.filial.code})` : ""
+
+ userGroupsMap.set(key, {
+ matricula,
+ nome,
+ email,
+ setor,
+ empresa,
+ filial,
+ ordersCount: 1,
+ totalValue: val,
+ })
+ }
+ }
+
+ const groupedRows = Array.from(userGroupsMap.values())
+ .sort((a, b) => a.nome.localeCompare(b.nome, "pt-BR"))
+ .map((group) => ({
+ "Matrícula": group.matricula,
+ "Colaborador": group.nome,
+ "E-mail": group.email,
+ "Setor": group.setor,
+ "Empresa": group.empresa,
+ "Filial": group.filial,
+ "Qtd. de Pedidos": group.ordersCount,
+ "Gasto Total (R$)": Number(group.totalValue.toFixed(2)),
+ }))
+
+ const wsGrouped = XLSX.utils.json_to_sheet(groupedRows)
+ XLSX.utils.book_append_sheet(wb, wsGrouped, "Gastos por Usuário")
+ XLSX.writeFile(wb, `pedidos_almoco_agrupado_${fileStart}_a_${fileEnd}.xlsx`)
+ toast.success(
+ `Planilha agrupada exportada com sucesso! (${groupedRows.length} colaboradores, ${ordersToExport.length} pedidos)`,
+ )
+ } else {
+ const detailedRows = ordersToExport.map((order) => {
+ const user = order.user
+ const matricula = user?.matricula ?? ""
+ const nome =
+ `${user?.firstName ?? ""} ${user?.lastName ?? ""}`.trim() || "Não identificado"
+ const email = user?.email ?? ""
+ const setor = user?.setor ?? ""
+ const empresa = user?.filial?.empresa?.name ?? user?.enterprise ?? ""
+ const filial = user?.filial ? `${user.filial.name} (${user.filial.code})` : ""
+ const restaurante = order.restaurant?.name ?? "Restaurante excluído"
+ const prato = order.menuItem?.name ?? ""
+ const valor = order.unitPrice ?? order.menuItem?.price ?? 0
+
+ const statusLabel =
+ order.status === "DELIVERED"
+ ? "Entregue"
+ : order.status === "CONFIRMED"
+ ? "Confirmado"
+ : order.status === "PENDING"
+ ? "Pendente"
+ : order.status === "CANCELLED"
+ ? "Cancelado"
+ : order.status
+
+ return {
+ "Data do Pedido": formatOrderDate(order.orderDate),
+ "Hora": order.orderTime
+ ? format(new Date(order.orderTime), "HH:mm", { locale: ptBR })
+ : "",
+ "Matrícula": matricula,
+ "Colaborador": nome,
+ "E-mail": email,
+ "Setor": setor,
+ "Empresa": empresa,
+ "Filial": filial,
+ "Restaurante": restaurante,
+ "Prato": prato,
+ "Valor (R$)": valor,
+ "Status": statusLabel,
+ }
+ })
+
+ const wb = XLSX.utils.book_new()
+ const wsDetailed = XLSX.utils.json_to_sheet(detailedRows)
+ XLSX.utils.book_append_sheet(wb, wsDetailed, "Pedidos")
+
+ XLSX.writeFile(wb, `pedidos_almoco_${fileStart}_a_${fileEnd}.xlsx`)
+ toast.success(`Planilha exportada com sucesso! (${ordersToExport.length} pedidos)`)
+ }
} catch (err: unknown) {
console.error(err)
toast.error("Ocorreu um erro ao exportar a planilha.")
@@ -539,6 +719,95 @@ export function OrdersReportModal({
Ajuste as datas ou os filtros selecionados para visualizar os registros.
+ ) : groupByUser ? (
+
+
+
+
+
+
+ Colaborador
+ Matrícula
+ Setor
+ Empresa / Filial
+ Qtd. Pedidos
+ Gasto Total
+
+
+
+ {paginatedGroupedUsers.map((group) => {
+ return (
+
+
+ handleToggleUserGroup(group)}
+ />
+
+
+ {group.name}
+
+ {group.email}
+
+
+
+ {group.matricula}
+
+
+ {group.setor !== "-" ? (
+
+ {group.setor}
+
+ ) : (
+ -
+ )}
+
+
+ {group.empresa}
+ {group.filial && (
+
+ {group.filial}
+
+ )}
+
+
+
+ {group.ordersCount} {group.ordersCount === 1 ? "pedido" : "pedidos"}
+
+
+
+ {formatCurrency(
+ group.selectedTotalValue > 0
+ ? group.selectedTotalValue
+ : group.totalValue,
+ )}
+
+
+ )
+ })}
+
+
) : (
@@ -638,6 +907,11 @@ export function OrdersReportModal({
{selectedOrders.length}
{" "}
de {orders.length} pedidos
+ {groupByUser && (
+
+ ({selectedGroupedUsers.length} de {groupedUsers.length} colaboradores)
+
+ )}
{formatCurrency(selectedTotalValue)}
@@ -648,8 +922,10 @@ export function OrdersReportModal({
- Mostrando {orders.length === 0 ? 0 : (page - 1) * pageSize + 1} a{" "}
- {Math.min(page * pageSize, orders.length)} de {orders.length} pedidos
+ Mostrando {totalItems === 0 ? 0 : (page - 1) * pageSize + 1} a{" "}
+ {Math.min(page * pageSize, totalItems)} de {totalItems}{" "}
+ {groupByUser ? "colaboradores" : "pedidos"}
+ {groupByUser && orders.length > 0 && ` (${orders.length} pedidos)`}
@@ -701,6 +977,25 @@ export function OrdersReportModal({
)}
+ {/* Checkbox sutil para agrupar usuários (default marcado) */}
+
+ {
+ setGroupByUser(!!checked)
+ setPage(1)
+ }}
+ className="h-3.5 w-3.5 rounded"
+ />
+
+
+
@@ -711,7 +1006,7 @@ export function OrdersReportModal({
className="gap-1.5 text-xs h-8 font-medium"
>
- Exportar Excel ({selectedOrders.length})
+ Exportar Excel ({groupByUser ? selectedGroupedUsers.length : selectedOrders.length})