From d7fe03bd1f76ae03813493cc5ba790c18d6b3264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Tue, 25 Nov 2025 08:16:34 -0300 Subject: [PATCH] update tables --- .../_.$orgid.enrollments._index/columns.tsx | 176 ++-------------- .../routes/_.$orgid.orders._index/columns.tsx | 20 ++ .../routes/_.$orgid.orders._index/route.tsx | 3 +- .../routes/_.$orgid.users._index/columns.tsx | 92 +-------- .../app/routes/_.$orgid.users._index/data.tsx | 8 - .../routes/_.$orgid.users._index/route.tsx | 6 +- .../_app.enrollments._index/columns.tsx | 191 ++---------------- .../routes/_app.enrollments._index/route.tsx | 82 +------- .../app/routes/_app.orgs._index/columns.tsx | 50 ++--- .../routes/_app.payments._index/columns.tsx | 33 ++- .../app/routes/_app.users._index/columns.tsx | 51 ++--- .../components/data-table/column-cpfcnpj.tsx | 8 - ...lumn-header.tsx => column-header-sort.tsx} | 6 +- .../components/data-table/column-select.tsx | 43 ++++ .../ui/src/components/data-table/index.ts | 6 +- .../components/data-table/view-options.tsx | 2 +- .../ui/src/routes/enrollments/columns.tsx | 127 ++++++++++++ packages/ui/src/routes/enrollments/data.tsx | 16 ++ packages/ui/src/routes/orders/columns.tsx | 42 ++-- packages/ui/src/routes/orders/data.tsx | 35 ++-- packages/ui/src/routes/orgs/data.tsx | 8 + packages/ui/src/routes/users/columns.tsx | 60 ++++++ packages/ui/src/routes/users/data.tsx | 18 ++ 23 files changed, 432 insertions(+), 651 deletions(-) create mode 100644 apps/admin.saladeaula.digital/app/routes/_.$orgid.orders._index/columns.tsx delete mode 100644 apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/data.tsx rename packages/ui/src/components/data-table/{column-header.tsx => column-header-sort.tsx} (85%) create mode 100644 packages/ui/src/components/data-table/column-select.tsx create mode 100644 packages/ui/src/routes/enrollments/columns.tsx create mode 100644 packages/ui/src/routes/orgs/data.tsx create mode 100644 packages/ui/src/routes/users/columns.tsx create mode 100644 packages/ui/src/routes/users/data.tsx diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/columns.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/columns.tsx index c9a93eb..673a7b1 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/columns.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/columns.tsx @@ -1,21 +1,19 @@ 'use client' import type { ColumnDef } from '@tanstack/react-table' +import type { ComponentProps, MouseEvent } from 'react' import { useRequest, useToggle } from 'ahooks' import { CircleXIcon, EllipsisVerticalIcon, FileBadgeIcon, - HelpCircleIcon, LockOpenIcon } from 'lucide-react' -import type { ComponentProps, MouseEvent } from 'react' import { toast } from 'sonner' -import { Abbr } from '@repo/ui/components/abbr' import { - DataTableColumnDatetime, - DataTableColumnHeader + DataTableColumnHeaderSelect, + DataTableColumnSelect } from '@repo/ui/components/data-table' import { AlertDialog, @@ -28,10 +26,8 @@ import { AlertDialogTitle, AlertDialogTrigger } from '@repo/ui/components/ui/alert-dialog' -import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' -import { Badge } from '@repo/ui/components/ui/badge' + import { Button } from '@repo/ui/components/ui/button' -import { Checkbox } from '@repo/ui/components/ui/checkbox' import { DropdownMenu, DropdownMenuContent, @@ -39,170 +35,24 @@ import { DropdownMenuSeparator, DropdownMenuTrigger } from '@repo/ui/components/ui/dropdown-menu' -import { Progress } from '@repo/ui/components/ui/progress' import { Spinner } from '@repo/ui/components/ui/spinner' -import { cn, initials } from '@repo/ui/lib/utils' -import { labels, statuses } from '@repo/ui/routes/enrollments/data' +import { + type Enrollment, + columns as columns_ +} from '@repo/ui/routes/enrollments/columns' -// This type is used to define the shape of our data. -// You can use a Zod schema here if you want. -type Course = { - id: string - name: string -} - -export type Enrollment = { - id: string - name: string - course: Course - status: string - progress: string - created_at: string -} +export type { Enrollment } export const columns: ColumnDef[] = [ { id: 'select', - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - className="cursor-pointer" - aria-label="Selecionar tudo" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - className="cursor-pointer" - aria-label="Selecionar linha" - /> - ) - }, - { - accessorKey: 'user', - header: 'Colaborador', - enableHiding: false, - cell: ({ row }) => { - const user = row.getValue('user') as { name: string; email: string } - - return ( -
- - {initials(user.name)} - - -
    -
  • - {user.name} -
  • -
  • - {user.email} -
  • -
-
- ) - } - }, - { - accessorKey: 'course', - header: 'Curso', - enableHiding: false, - cell: ({ row }) => { - const { name } = row.getValue('course') as { name: string } - return {name} - } - }, - { - accessorKey: 'status', - header: 'Status', - enableHiding: false, - cell: ({ row }) => { - const s = row.getValue('status') as string - const status = labels[s] ?? s - const { icon: Icon, color } = statuses?.[s] ?? { icon: HelpCircleIcon } - - return ( - - - {status} - - ) - } - }, - { - accessorKey: 'progress', - header: 'Progresso', - enableHiding: false, - cell: ({ row }) => { - const progress = row.getValue('progress') - - return ( -
- - {String(progress)}% -
- ) - } - }, - { - accessorKey: 'created_at', - meta: { title: 'Cadastrado em' }, - enableSorting: true, - enableHiding: true, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) - }, - { - accessorKey: 'started_at', - meta: { title: 'Iniciado em' }, - enableSorting: true, - enableHiding: true, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) - }, - { - accessorKey: 'completed_at', - meta: { title: 'Concluído em' }, - enableSorting: true, - enableHiding: true, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) - }, - { - accessorKey: 'failed_at', - meta: { title: 'Reprovado em' }, - enableSorting: true, - enableHiding: true, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) - }, - { - accessorKey: 'canceled_at', - meta: { title: 'Cancelado em' }, - enableSorting: true, - enableHiding: true, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) + header: DataTableColumnHeaderSelect, + cell: DataTableColumnSelect }, + ...columns_, { id: 'actions', - cell: ({ row }) => + cell: ActionMenu } ] diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.orders._index/columns.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.orders._index/columns.tsx new file mode 100644 index 0000000..4bad4b3 --- /dev/null +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.orders._index/columns.tsx @@ -0,0 +1,20 @@ +'use client' + +import { type ColumnDef } from '@tanstack/react-table' + +import { + DataTableColumnHeaderSelect, + DataTableColumnSelect +} from '@repo/ui/components/data-table' +import { columns as columns_, type Order } from '@repo/ui/routes/orders/columns' + +export type { Order } + +export const columns: ColumnDef[] = [ + { + id: 'select', + header: DataTableColumnHeaderSelect, + cell: DataTableColumnSelect + }, + ...columns_ +] diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.orders._index/route.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.orders._index/route.tsx index 05c1a2b..59321b7 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.orders._index/route.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.orders._index/route.tsx @@ -7,7 +7,8 @@ import { MeiliSearchFilterBuilder } from 'meilisearch-helper' import { DataTable } from '@repo/ui/components/data-table' import { Skeleton } from '@repo/ui/components/skeleton' import { createSearch } from '@repo/util/meili' -import { columns, type Order } from '@repo/ui/routes/orders/columns' + +import { columns, type Order } from './columns' export function meta({}: Route.MetaArgs) { return [{ title: 'Histórico de compras' }] diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/columns.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/columns.tsx index 6dc1355..a758815 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/columns.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/columns.tsx @@ -10,12 +10,10 @@ import { import { NavLink, useParams } from 'react-router' import { toast } from 'sonner' -import { Abbr } from '@repo/ui/components/abbr' import { useDataTable, - DataTableColumnDatetime, - DataTableColumnHeader, - DataTableColumnCpfCnpj + DataTableColumnHeaderSelect, + DataTableColumnSelect } from '@repo/ui/components/data-table' import { AlertDialog, @@ -28,9 +26,7 @@ import { AlertDialogTitle, AlertDialogTrigger } from '@repo/ui/components/ui/alert-dialog' -import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' import { Button } from '@repo/ui/components/ui/button' -import { Checkbox } from '@repo/ui/components/ui/checkbox' import { DropdownMenu, DropdownMenuContent, @@ -39,92 +35,20 @@ import { } from '@repo/ui/components/ui/dropdown-menu' import { Spinner } from '@repo/ui/components/ui/spinner' import { initials } from '@repo/ui/lib/utils' +import { type User, columns as columns_ } from '@repo/ui/routes/users/columns' -// This type is used to define the shape of our data. -// You can use a Zod schema here if you want. -export type User = { - id: string - name: string - email: string - cpf?: string - cnpj?: string -} +export type { User } export const columns: ColumnDef[] = [ { id: 'select', - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - className="cursor-pointer" - aria-label="Selecionar tudo" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - className="cursor-pointer" - aria-label="Selecionar linha" - /> - ) - }, - { - header: 'Colaborador', - cell: ({ row }) => { - const { name, email } = row.original - - return ( -
- - {initials(name)} - - -
    -
  • - {name} -
  • -
  • - {email} -
  • -
-
- ) - } - }, - { - accessorKey: 'cpf', - header: 'CPF', - cell: ({ row, column }) => ( - - ) - }, - { - accessorKey: 'createDate', - enableSorting: true, - meta: { title: 'Cadastrado em' }, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) - }, - { - accessorKey: 'lastLogin', - enableSorting: true, - meta: { title: 'Último acesso' }, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) + header: DataTableColumnHeaderSelect, + cell: DataTableColumnSelect }, + ...columns_, { id: 'actions', - cell: ({ row }) => + cell: ActionMenu } ] diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/data.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/data.tsx deleted file mode 100644 index 7e2de4b..0000000 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/data.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export const headers = { - id: 'ID', - name: 'Nome', - email: 'Email', - cpf: 'CPF', - createDate: 'Cadastrado em', - lastLogin: 'Último acesso' -} diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/route.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/route.tsx index 1076cee..01caaaa 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/route.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/route.tsx @@ -12,6 +12,9 @@ import { Button } from '@repo/ui/components/ui/button' import { Kbd } from '@repo/ui/components/ui/kbd' import { ExportMenu } from '@repo/ui/components/export-menu' import { createSearch } from '@repo/util/meili' +import { headers } from '@repo/ui/routes/users/data' + +import { columns, type User } from './columns' export function meta({}: Route.MetaArgs) { return [ @@ -23,9 +26,6 @@ export function meta({}: Route.MetaArgs) { ] } -import { headers } from './data' -import { columns, type User } from './columns' - export async function loader({ params, context, request }: Route.LoaderArgs) { const { searchParams } = new URL(request.url) const { orgid } = params diff --git a/apps/insights.saladeaula.digital/app/routes/_app.enrollments._index/columns.tsx b/apps/insights.saladeaula.digital/app/routes/_app.enrollments._index/columns.tsx index 8e615d8..98b2d61 100644 --- a/apps/insights.saladeaula.digital/app/routes/_app.enrollments._index/columns.tsx +++ b/apps/insights.saladeaula.digital/app/routes/_app.enrollments._index/columns.tsx @@ -1,206 +1,39 @@ 'use client' -import type { CellContext, ColumnDef } from '@tanstack/react-table' +import type { ColumnDef } from '@tanstack/react-table' import { useToggle } from 'ahooks' -import { - EllipsisVerticalIcon, - FileBadgeIcon, - HelpCircleIcon -} from 'lucide-react' +import { EllipsisVerticalIcon, FileBadgeIcon } from 'lucide-react' import type { ComponentProps } from 'react' -import { Abbr } from '@repo/ui/components/abbr' -import { DataTableColumnHeader } from '@repo/ui/components/data-table' -import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' -import { Badge } from '@repo/ui/components/ui/badge' import { Button } from '@repo/ui/components/ui/button' -import { Checkbox } from '@repo/ui/components/ui/checkbox' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@repo/ui/components/ui/dropdown-menu' -import { Progress } from '@repo/ui/components/ui/progress' import { Spinner } from '@repo/ui/components/ui/spinner' -import { cn, initials } from '@repo/ui/lib/utils' -import { labels, statuses } from '@repo/ui/routes/enrollments/data' +import { DataTableColumnHeaderSelect } from '@repo/ui/components/data-table/column-select' +import { + columns as columns_, + type Enrollment +} from '@repo/ui/routes/enrollments/columns' -// This type is used to define the shape of our data. -// You can use a Zod schema here if you want. -type Course = { - id: string - name: string -} - -export type Enrollment = { - id: string - name: string - course: Course - status: string - progress: string - created_at: string -} - -const formatted = new Intl.DateTimeFormat('pt-BR', { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit' -}) +export type { Enrollment } export const columns: ColumnDef[] = [ { id: 'select', - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - className="cursor-pointer" - aria-label="Selecionar tudo" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - className="cursor-pointer" - aria-label="Selecionar linha" - /> - ) - }, - { - accessorKey: 'user', - header: 'Colaborador', - enableHiding: false, - cell: ({ row }) => { - const user = row.getValue('user') as { name: string; email: string } - - return ( -
- - {initials(user.name)} - - -
    -
  • - {user.name} -
  • -
  • - {user.email} -
  • -
-
- ) - } - }, - { - accessorKey: 'course', - header: 'Curso', - enableHiding: false, - cell: ({ row }) => { - const { name } = row.getValue('course') as { name: string } - - return {name} - } - }, - { - accessorKey: 'status', - header: 'Status', - enableHiding: false, - cell: ({ row }) => { - const s = row.getValue('status') as string - const status = labels[s] ?? s - const { icon: Icon, color } = statuses?.[s] ?? { icon: HelpCircleIcon } - - return ( - - - {status} - - ) - } - }, - { - accessorKey: 'progress', - header: 'Progresso', - enableHiding: false, - cell: ({ row }) => { - const progress = row.getValue('progress') - - return ( -
- - {String(progress)}% -
- ) - } - }, - { - accessorKey: 'created_at', - header: ({ column }) => , - meta: { title: 'Cadastrado em' }, - enableSorting: true, - enableHiding: true, - cell: cellDate - }, - { - accessorKey: 'started_at', - header: ({ column }) => , - meta: { title: 'Iniciado em' }, - enableSorting: true, - enableHiding: true, - cell: cellDate - }, - { - accessorKey: 'completed_at', - header: ({ column }) => , - meta: { title: 'Concluído em' }, - enableSorting: true, - enableHiding: true, - cell: cellDate - }, - { - accessorKey: 'failed_at', - header: ({ column }) => , - meta: { title: 'Reprovado em' }, - enableSorting: true, - enableHiding: true, - cell: cellDate - }, - { - accessorKey: 'canceled_at', - header: ({ column }) => , - meta: { title: 'Cancelado em' }, - enableSorting: true, - enableHiding: true, - cell: cellDate + header: DataTableColumnHeaderSelect, + cell: DataTableColumnHeaderSelect }, + ...columns_, { id: 'actions', - cell: ({ row }) => + cell: ActionMenu } ] -function cellDate({ - row: { original }, - cell: { column } -}: CellContext) { - const accessorKey = column.columnDef.accessorKey as keyof TData - const value = original?.[accessorKey] - - if (value) { - return formatted.format(new Date(value as string)) - } - - return <> -} - function ActionMenu({ row }: { row: any }) { const [open, { set: setOpen }] = useToggle(false) const cert = row.original?.cert diff --git a/apps/insights.saladeaula.digital/app/routes/_app.enrollments._index/route.tsx b/apps/insights.saladeaula.digital/app/routes/_app.enrollments._index/route.tsx index 5178464..d2e8fc1 100644 --- a/apps/insights.saladeaula.digital/app/routes/_app.enrollments._index/route.tsx +++ b/apps/insights.saladeaula.digital/app/routes/_app.enrollments._index/route.tsx @@ -1,37 +1,20 @@ import type { Route } from './+types' -import { flatten } from 'flat' -import { - CalendarIcon, - ChevronDownIcon, - DownloadIcon, - FileSpreadsheetIcon, - FileTextIcon, - PlusCircleIcon -} from 'lucide-react' +import { CalendarIcon, PlusCircleIcon } from 'lucide-react' import { MeiliSearchFilterBuilder } from 'meilisearch-helper' import { Suspense, useState } from 'react' import { Await, Outlet, useSearchParams } from 'react-router' -import type { BookType } from 'xlsx' -import * as XLSX from 'xlsx' import { DataTable, DataTableViewOptions } from '@repo/ui/components/data-table' import { FacetedFilter } from '@repo/ui/components/faceted-filter' import { RangeCalendarFilter } from '@repo/ui/components/range-calendar-filter' import { SearchForm } from '@repo/ui/components/search-form' import { Skeleton } from '@repo/ui/components/skeleton' -import { Button } from '@repo/ui/components/ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuTrigger -} from '@repo/ui/components/ui/dropdown-menu' import { Kbd } from '@repo/ui/components/ui/kbd' +import { ExportMenu } from '@repo/ui/components/export-menu' import { createSearch } from '@repo/util/meili' - import { headers, sortings, statuses } from '@repo/ui/routes/enrollments/data' + import { columns, type Enrollment } from './columns' export function meta({}: Route.MetaArgs) { @@ -112,7 +95,11 @@ export default function Route({ loaderData: { data } }) { {selectedRows.length ? ( <>
- +
) : ( @@ -200,7 +187,7 @@ export default function Route({ loaderData: { data } }) {
- +
@@ -233,54 +220,3 @@ function useRangeParams() { } } } - -export function ExportMenu({ - headers, - selectedRows = [] -}: { - headers: Record - selectedRows: object[] -}) { - const exportFile = (bookType: BookType) => () => { - if (!selectedRows.length) { - return - } - const now = new Date() - const header = Object.keys(headers) - const data = selectedRows.map((data) => { - const obj: Record = flatten(data) - return Object.fromEntries(header.map((k) => [k, obj?.[k]])) - }) - const workbook = XLSX.utils.book_new() - const worksheet = XLSX.utils.json_to_sheet(data, { header }) - - XLSX.utils.sheet_add_aoa(worksheet, [Object.values(headers)], { - origin: 'A1' - }) - XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1') - XLSX.writeFile(workbook, `users_${+now}.${bookType}`, { - bookType, - compression: true - }) - } - - return ( - - - - - - - - Microsoft Excel (.xlsx) - - - CSV (.csv) - - - - - ) -} diff --git a/apps/insights.saladeaula.digital/app/routes/_app.orgs._index/columns.tsx b/apps/insights.saladeaula.digital/app/routes/_app.orgs._index/columns.tsx index 679390c..31f9de7 100644 --- a/apps/insights.saladeaula.digital/app/routes/_app.orgs._index/columns.tsx +++ b/apps/insights.saladeaula.digital/app/routes/_app.orgs._index/columns.tsx @@ -1,30 +1,26 @@ 'use client' -import { formatCNPJ } from '@brazilian-utils/brazilian-utils' import { type ColumnDef } from '@tanstack/react-table' import { Abbr } from '@repo/ui/components/abbr' import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' import { initials } from '@repo/ui/lib/utils' +import { + DataTableColumnCpfCnpj, + DataTableColumnDatetime, + DataTableColumnHeaderSelect, + DataTableColumnSelect +} from '@repo/ui/components/data-table' +import type { Org } from '@repo/ui/routes/orgs/data' -// This type is used to define the shape of our data. -// You can use a Zod schema here if you want. -export type Org = { - id: string - name: string - email: string - cnpj?: string -} - -const formatted = new Intl.DateTimeFormat('pt-BR', { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit' -}) +export type { Org } export const columns: ColumnDef[] = [ + { + id: 'select', + header: DataTableColumnHeaderSelect, + cell: DataTableColumnSelect + }, { header: 'Empresa', cell: ({ row }) => { @@ -49,25 +45,13 @@ export const columns: ColumnDef[] = [ } }, { + accessorKey: 'cnpj', header: 'CNPJ', - cell: ({ row }) => { - const { cnpj } = row.original - return <>{formatCNPJ(cnpj)} - } + cell: DataTableColumnCpfCnpj }, { + accessorKey: 'createDate', header: 'Cadastrado em', - meta: { - className: 'w-1/12' - }, - cell: ({ row }) => { - const { createDate } = row.original - - if (!createDate) { - return <> - } - - return formatted.format(new Date(createDate)) - } + cell: DataTableColumnDatetime } ] diff --git a/apps/insights.saladeaula.digital/app/routes/_app.payments._index/columns.tsx b/apps/insights.saladeaula.digital/app/routes/_app.payments._index/columns.tsx index 6848f29..c20aca5 100644 --- a/apps/insights.saladeaula.digital/app/routes/_app.payments._index/columns.tsx +++ b/apps/insights.saladeaula.digital/app/routes/_app.payments._index/columns.tsx @@ -1,17 +1,23 @@ 'use client' -import { columns as columns_, type Order } from '@repo/ui/routes/orders/columns' - -import { formatCNPJ, formatCPF } from '@brazilian-utils/brazilian-utils' import { type ColumnDef } from '@tanstack/react-table' import { Abbr } from '@repo/ui/components/abbr' import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' import { initials } from '@repo/ui/lib/utils' - -export type { Order } +import { + DataTableColumnCpfCnpj, + DataTableColumnHeaderSelect, + DataTableColumnSelect +} from '@repo/ui/components/data-table' +import { columns as columns_, type Order } from '@repo/ui/routes/orders/columns' export const columns: ColumnDef[] = [ + { + id: 'select', + header: DataTableColumnHeaderSelect, + cell: DataTableColumnSelect + }, { header: 'Comprador', cell: ({ row }) => { @@ -36,20 +42,11 @@ export const columns: ColumnDef[] = [ } }, { + accessorKey: 'cnpj_cpf', header: 'CNPJ/CPF', - cell: ({ row }) => { - const { cpf, cnpj } = row.original - - if (cpf) { - return <>{formatCPF(cpf)} - } - - if (cnpj) { - return <>{formatCNPJ(cnpj)} - } - - return <> - } + // @ts-ignore + accessorFn: ({ cpf, cnpj }) => cpf ?? cnpj, + cell: DataTableColumnCpfCnpj }, ...columns_ ] diff --git a/apps/insights.saladeaula.digital/app/routes/_app.users._index/columns.tsx b/apps/insights.saladeaula.digital/app/routes/_app.users._index/columns.tsx index 2b7ab9c..cfa06e1 100644 --- a/apps/insights.saladeaula.digital/app/routes/_app.users._index/columns.tsx +++ b/apps/insights.saladeaula.digital/app/routes/_app.users._index/columns.tsx @@ -1,9 +1,14 @@ 'use client' -import { formatCPF } from '@brazilian-utils/brazilian-utils' import { type ColumnDef } from '@tanstack/react-table' import { Abbr } from '@repo/ui/components/abbr' +import { + DataTableColumnDatetime, + DataTableColumnSelect, + DataTableColumnCpfCnpj, + DataTableColumnHeaderSelect +} from '@repo/ui/components/data-table' import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' import { initials } from '@repo/ui/lib/utils' @@ -16,15 +21,12 @@ export type User = { cpf?: string } -const formatted = new Intl.DateTimeFormat('pt-BR', { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit' -}) - export const columns: ColumnDef[] = [ + { + id: 'select', + header: DataTableColumnHeaderSelect, + cell: DataTableColumnSelect + }, { header: 'Usuário', cell: ({ row }) => { @@ -49,37 +51,18 @@ export const columns: ColumnDef[] = [ } }, { + accessorKey: 'cpf', header: 'CPF', - cell: ({ row }) => { - const { cpf } = row.original - - if (cpf) { - return <>{formatCPF(cpf)} - } - - return <> - } + cell: DataTableColumnCpfCnpj }, { + accessorKey: 'lastLogin', header: 'Último accesso', - cell: ({ row }) => { - // Post-migration: rename `lastLogin` to `last_login` - if (row.original?.lastLogin) { - const lastLogin = new Date(row.original.lastLogin) - return formatted.format(lastLogin) - } - - return <> - } + cell: DataTableColumnDatetime }, { + accessorKey: 'createDate', header: 'Cadastrado em', - meta: { - className: 'w-1/12' - }, - cell: ({ row }) => { - const created_at = new Date(row.original.createDate) - return formatted.format(created_at) - } + cell: DataTableColumnDatetime } ] diff --git a/packages/ui/src/components/data-table/column-cpfcnpj.tsx b/packages/ui/src/components/data-table/column-cpfcnpj.tsx index 1891fd4..58849e5 100644 --- a/packages/ui/src/components/data-table/column-cpfcnpj.tsx +++ b/packages/ui/src/components/data-table/column-cpfcnpj.tsx @@ -12,14 +12,6 @@ interface DataTableColumnDatetimeProps column: Column } -const formatted = new Intl.DateTimeFormat('pt-BR', { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit' -}) - export function DataTableColumnCpfCnpj({ row, column diff --git a/packages/ui/src/components/data-table/column-header.tsx b/packages/ui/src/components/data-table/column-header-sort.tsx similarity index 85% rename from packages/ui/src/components/data-table/column-header.tsx rename to packages/ui/src/components/data-table/column-header-sort.tsx index d8e1a92..e68dd7f 100644 --- a/packages/ui/src/components/data-table/column-header.tsx +++ b/packages/ui/src/components/data-table/column-header-sort.tsx @@ -8,15 +8,15 @@ import { import { Button } from '@repo/ui/components/ui/button' import { cn } from '@repo/ui/lib/utils' -interface DataTableColumnHeaderProps +interface DataTableColumnHeaderSortProps extends React.HTMLAttributes { column: Column } -export function DataTableColumnHeader({ +export function DataTableColumnHeaderSort({ column, className -}: DataTableColumnHeaderProps) { +}: DataTableColumnHeaderSortProps) { // @ts-ignore const title = column.columnDef?.meta?.title ?? column.id diff --git a/packages/ui/src/components/data-table/column-select.tsx b/packages/ui/src/components/data-table/column-select.tsx new file mode 100644 index 0000000..a1a67ae --- /dev/null +++ b/packages/ui/src/components/data-table/column-select.tsx @@ -0,0 +1,43 @@ +import type { Table, Row } from '@tanstack/react-table' + +import { Checkbox } from '../ui/checkbox' + +interface DataTableColumnHeaderSelectProps + extends React.HTMLAttributes { + table: Table +} + +export function DataTableColumnHeaderSelect({ + table +}: DataTableColumnHeaderSelectProps) { + return ( + table.toggleAllPageRowsSelected(!!value)} + className="cursor-pointer" + aria-label="Selecionar tudo" + /> + ) +} + +interface DataTableColumnSelectProps + extends React.HTMLAttributes { + row: Row +} + +export function DataTableColumnSelect({ + row +}: DataTableColumnSelectProps) { + return ( + row.toggleSelected(!!value)} + className="cursor-pointer" + aria-label="Selecionar linha" + /> + ) +} diff --git a/packages/ui/src/components/data-table/index.ts b/packages/ui/src/components/data-table/index.ts index 38aadc1..01de20e 100644 --- a/packages/ui/src/components/data-table/index.ts +++ b/packages/ui/src/components/data-table/index.ts @@ -1,4 +1,8 @@ -export { DataTableColumnHeader } from './column-header' +export { DataTableColumnHeaderSort } from './column-header-sort' +export { + DataTableColumnHeaderSelect, + DataTableColumnSelect +} from './column-select' export { DataTableColumnDatetime } from './column-datetime' export { DataTableColumnCpfCnpj } from './column-cpfcnpj' export { DataTableColumnCurrency } from './column-currency' diff --git a/packages/ui/src/components/data-table/view-options.tsx b/packages/ui/src/components/data-table/view-options.tsx index 92f2e2f..1e756f6 100644 --- a/packages/ui/src/components/data-table/view-options.tsx +++ b/packages/ui/src/components/data-table/view-options.tsx @@ -15,7 +15,7 @@ import { useDataTable } from './data-table' export function DataTableViewOptions({ className }: { - className: string + className?: string }) { const { table } = useDataTable() diff --git a/packages/ui/src/routes/enrollments/columns.tsx b/packages/ui/src/routes/enrollments/columns.tsx new file mode 100644 index 0000000..2dd4a54 --- /dev/null +++ b/packages/ui/src/routes/enrollments/columns.tsx @@ -0,0 +1,127 @@ +'use client' + +import type { ColumnDef } from '@tanstack/react-table' +import { HelpCircleIcon } from 'lucide-react' + +import { Badge } from '@repo/ui/components/ui/badge' +import { Abbr } from '@repo/ui/components/abbr' +import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' +import { Progress } from '@repo/ui/components/ui/progress' +import { + DataTableColumnDatetime, + DataTableColumnHeaderSort +} from '@repo/ui/components/data-table' +import { cn, initials } from '@repo/ui/lib/utils' + +import { labels, statuses, type Enrollment } from './data' + +export type { Enrollment } + +export const columns: ColumnDef[] = [ + { + accessorKey: 'user', + header: 'Colaborador', + enableHiding: false, + cell: ({ row }) => { + const user = row.getValue('user') as { name: string; email: string } + + return ( +
+ + {initials(user.name)} + + +
    +
  • + {user.name} +
  • +
  • + {user.email} +
  • +
+
+ ) + } + }, + { + accessorKey: 'course', + header: 'Curso', + enableHiding: false, + cell: ({ row }) => { + const { name } = row.getValue('course') as { name: string } + return {name} + } + }, + { + accessorKey: 'status', + header: 'Status', + enableHiding: false, + cell: ({ row }) => { + const s = row.getValue('status') as string + const status = labels[s] ?? s + const { icon: Icon, color } = statuses?.[s] ?? { icon: HelpCircleIcon } + + return ( + + + {status} + + ) + } + }, + { + accessorKey: 'progress', + header: 'Progresso', + enableHiding: false, + cell: ({ row }) => { + const progress = row.getValue('progress') + + return ( +
+ + {String(progress)}% +
+ ) + } + }, + { + accessorKey: 'created_at', + meta: { title: 'Cadastrado em' }, + enableSorting: true, + enableHiding: true, + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime + }, + { + accessorKey: 'started_at', + meta: { title: 'Iniciado em' }, + enableSorting: true, + enableHiding: true, + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime + }, + { + accessorKey: 'completed_at', + meta: { title: 'Concluído em' }, + enableSorting: true, + enableHiding: true, + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime + }, + { + accessorKey: 'failed_at', + meta: { title: 'Reprovado em' }, + enableSorting: true, + enableHiding: true, + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime + }, + { + accessorKey: 'canceled_at', + meta: { title: 'Cancelado em' }, + enableSorting: true, + enableHiding: true, + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime + } +] diff --git a/packages/ui/src/routes/enrollments/data.tsx b/packages/ui/src/routes/enrollments/data.tsx index d345e15..fa48de2 100644 --- a/packages/ui/src/routes/enrollments/data.tsx +++ b/packages/ui/src/routes/enrollments/data.tsx @@ -7,6 +7,22 @@ import { type LucideIcon } from 'lucide-react' +// This type is used to define the shape of our data. +// You can use a Zod schema here if you want. +type Course = { + id: string + name: string +} + +export type Enrollment = { + id: string + name: string + course: Course + status: string + progress: string + created_at: string +} + export const statuses: Record< string, { icon: LucideIcon; color?: string; label: string } diff --git a/packages/ui/src/routes/orders/columns.tsx b/packages/ui/src/routes/orders/columns.tsx index 600c926..bdf3dd4 100644 --- a/packages/ui/src/routes/orders/columns.tsx +++ b/packages/ui/src/routes/orders/columns.tsx @@ -3,7 +3,7 @@ import { DataTableColumnDatetime, DataTableColumnCurrency, - DataTableColumnHeader + DataTableColumnHeaderSort } from '@repo/ui/components/data-table' import { type ColumnDef } from '@tanstack/react-table' import { HelpCircleIcon } from 'lucide-react' @@ -11,27 +11,17 @@ import { HelpCircleIcon } from 'lucide-react' import { cn } from '@repo/ui/lib/utils' import { Badge } from '@repo/ui/components/ui/badge' -import { labels, methods, statuses } from './data' +import { labels, methods, statuses, type Order } from './data' -// This type is used to define the shape of our data. -// You can use a Zod schema here if you want. -export type Order = { - id: string - total: number - status: 'PENDING' | 'PAID' | 'DECLINED' | 'EXPIRED' | 'REFUNDED' | 'CANCELED' - payment_method: 'PIX' | 'CREDIT_CARD' | 'MANUAL' | 'BANK_SLIP' - name: string -} +export type { Order } export const columns: ColumnDef[] = [ { accessorKey: 'payment_method', header: 'Forma de pag.', cell: ({ row }) => { - const s = row.getValue('payment_method') as string - const paymentMethod = methods[s] ?? s - - return <>{paymentMethod} + const paymentMethod = row.getValue('payment_method') as string + return <>{methods[paymentMethod] ?? paymentMethod} } }, { @@ -53,35 +43,27 @@ export const columns: ColumnDef[] = [ { accessorKey: 'total', header: 'Valor total', - cell: ({ row, column }) => ( - - ) + cell: DataTableColumnCurrency }, { accessorKey: 'create_date', enableSorting: true, meta: { title: 'Comprado em' }, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime }, { accessorKey: 'due_date', enableSorting: true, meta: { title: 'Vencimento em' }, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime }, { accessorKey: 'payment_date', enableSorting: true, meta: { title: 'Pago em' }, - header: ({ column }) => , - cell: ({ row, column }) => ( - - ) + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime } ] diff --git a/packages/ui/src/routes/orders/data.tsx b/packages/ui/src/routes/orders/data.tsx index 2fd588a..ccfd0e6 100644 --- a/packages/ui/src/routes/orders/data.tsx +++ b/packages/ui/src/routes/orders/data.tsx @@ -8,6 +8,17 @@ import { type LucideIcon } from 'lucide-react' +// This type is used to define the shape of our data. +// You can use a Zod schema here if you want. +export type Order = { + id: string + total: number + status: 'PENDING' | 'PAID' | 'DECLINED' | 'EXPIRED' | 'REFUNDED' | 'CANCELED' + payment_method: 'PIX' | 'CREDIT_CARD' | 'BANK_SLIP' | 'MANUAL' + name: string + email: string +} + export const statuses: Record< string, { icon: LucideIcon; color?: string; label: string } @@ -22,25 +33,25 @@ export const statuses: Record< color: 'text-green-400 [&_svg]:text-green-500', label: 'Pago' }, - REFUNDED: { - icon: RotateCcwIcon, - color: 'text-orange-400 [&_svg]:text-orange-500', - label: 'Estornado' + DECLINED: { + icon: BanIcon, + color: 'text-red-400 [&_svg]:text-red-500', + label: 'Negado' }, EXPIRED: { icon: ClockAlertIcon, color: 'text-orange-400 [&_svg]:text-orange-500', label: 'Expirado' }, + REFUNDED: { + icon: RotateCcwIcon, + color: 'text-orange-400 [&_svg]:text-orange-500', + label: 'Estornado' + }, CANCELED: { icon: CircleXIcon, color: 'text-red-400 [&_svg]:text-red-500', label: 'Cancelado' - }, - DECLINED: { - icon: BanIcon, - color: 'text-red-400 [&_svg]:text-red-500', - label: 'Negado' } } @@ -54,8 +65,8 @@ export const labels: Record = { } export const methods: Record = { - BANK_SLIP: 'Boleto bancário', - MANUAL: 'Depósito bancário', + PIX: 'Pix', CREDIT_CARD: 'Cartão de crédito', - PIX: 'Pix' + BANK_SLIP: 'Boleto bancário', + MANUAL: 'Depósito bancário' } diff --git a/packages/ui/src/routes/orgs/data.tsx b/packages/ui/src/routes/orgs/data.tsx new file mode 100644 index 0000000..c3e7bd2 --- /dev/null +++ b/packages/ui/src/routes/orgs/data.tsx @@ -0,0 +1,8 @@ +// This type is used to define the shape of our data. +// You can use a Zod schema here if you want. +export type Org = { + id: string + name: string + email: string + cnpj?: string +} diff --git a/packages/ui/src/routes/users/columns.tsx b/packages/ui/src/routes/users/columns.tsx new file mode 100644 index 0000000..d9fbf48 --- /dev/null +++ b/packages/ui/src/routes/users/columns.tsx @@ -0,0 +1,60 @@ +'use client' + +import { type ColumnDef } from '@tanstack/react-table' + +import { Abbr } from '@repo/ui/components/abbr' +import { + DataTableColumnDatetime, + DataTableColumnHeaderSort, + DataTableColumnCpfCnpj +} from '@repo/ui/components/data-table' +import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' +import { initials } from '@repo/ui/lib/utils' +import type { User } from '@repo/ui/routes/users/data' + +export type { User } + +export const columns: ColumnDef[] = [ + { + header: 'Colaborador', + cell: ({ row }) => { + const { name, email } = row.original + + return ( +
+ + {initials(name)} + + +
    +
  • + {name} +
  • +
  • + {email} +
  • +
+
+ ) + } + }, + { + accessorKey: 'cpf', + header: 'CPF', + cell: DataTableColumnCpfCnpj + }, + { + accessorKey: 'createDate', + enableSorting: true, + meta: { title: 'Cadastrado em' }, + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime + }, + { + accessorKey: 'lastLogin', + enableSorting: true, + meta: { title: 'Último acesso' }, + header: DataTableColumnHeaderSort, + cell: DataTableColumnDatetime + } +] diff --git a/packages/ui/src/routes/users/data.tsx b/packages/ui/src/routes/users/data.tsx new file mode 100644 index 0000000..45ca593 --- /dev/null +++ b/packages/ui/src/routes/users/data.tsx @@ -0,0 +1,18 @@ +// This type is used to define the shape of our data. +// You can use a Zod schema here if you want. +export type User = { + id: string + name: string + email: string + cpf?: string + cnpj?: string +} + +export const headers = { + id: 'ID', + name: 'Nome', + email: 'Email', + cpf: 'CPF', + createDate: 'Cadastrado em', + lastLogin: 'Último acesso' +}