From 45dbb1ebfb39ef438f36684316d2f25b0f777488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Mon, 24 Nov 2025 12:50:31 -0300 Subject: [PATCH] add export to users --- .../_.$orgid.enrollments._index/route.tsx | 84 +++---------------- .../app/routes/_.$orgid.users._index/data.tsx | 8 ++ .../routes/_.$orgid.users._index/route.tsx | 72 ++++++++++------ packages/ui/src/components/export-menu.tsx | 70 ++++++++++++++++ packages/ui/src/components/faceted-filter.tsx | 2 +- 5 files changed, 135 insertions(+), 101 deletions(-) create mode 100644 apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/data.tsx create mode 100644 packages/ui/src/components/export-menu.tsx diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/route.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/route.tsx index e8e40a4..1208c1d 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/route.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/route.tsx @@ -1,20 +1,9 @@ import type { Route } from './+types/route' -import { flatten } from 'flat' -import { - CalendarIcon, - ChevronDownIcon, - DownloadIcon, - FileSpreadsheetIcon, - FileTextIcon, - PlusCircleIcon, - PlusIcon -} from 'lucide-react' +import { CalendarIcon, PlusCircleIcon, PlusIcon } from 'lucide-react' import { MeiliSearchFilterBuilder } from 'meilisearch-helper' import { Suspense, useState } from 'react' import { Await, Link, Outlet, useParams, 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' @@ -22,13 +11,8 @@ 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 { ExportMenu } from '@repo/ui/components/export-menu' + import { Kbd } from '@repo/ui/components/ui/kbd' import { createSearch } from '@repo/util/meili' @@ -81,6 +65,7 @@ const formatted = new Intl.DateTimeFormat('en-CA', { }) export default function Route({ loaderData: { data } }) { + const { orgid } = useParams() const [searchParams, setSearchParams] = useSearchParams() const [selectedRows, setSelectedRows] = useState([]) const status = searchParams.get('status') @@ -115,11 +100,15 @@ export default function Route({ loaderData: { data } }) { canceled_at: false }} > -
+
{selectedRows.length ? ( <>
- +
) : ( @@ -246,56 +235,3 @@ function useRangeParams() { } } } - -export function ExportMenu({ - headers, - selectedRows = [] -}: { - headers: Record - selectedRows: object[] -}) { - const { orgid } = useParams() - - 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, `${orgid}_users_${+now}.${bookType}`, { - bookType, - compression: true - }) - } - - return ( - - - - - - - - Microsoft Excel (.xlsx) - - - CSV (.csv) - - - - - ) -} 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 new file mode 100644 index 0000000..7e2de4b --- /dev/null +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.users._index/data.tsx @@ -0,0 +1,8 @@ +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 2935df0..1076cee 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 @@ -1,8 +1,8 @@ import type { Route } from './+types/route' import { PlusIcon } from 'lucide-react' -import { Suspense } from 'react' -import { Await, Link, useSearchParams } from 'react-router' +import { Suspense, useState } from 'react' +import { Await, Link, useParams, useSearchParams } from 'react-router' import { MeiliSearchFilterBuilder } from 'meilisearch-helper' import { DataTable } from '@repo/ui/components/data-table' @@ -10,6 +10,7 @@ import { SearchForm } from '@repo/ui/components/search-form' import { Skeleton } from '@repo/ui/components/skeleton' 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' export function meta({}: Route.MetaArgs) { @@ -22,6 +23,7 @@ export function meta({}: Route.MetaArgs) { ] } +import { headers } from './data' import { columns, type User } from './columns' export async function loader({ params, context, request }: Route.LoaderArgs) { @@ -48,7 +50,9 @@ export async function loader({ params, context, request }: Route.LoaderArgs) { } export default function Route({ loaderData: { data } }) { + const { orgid } = useParams() const [searchParams, setSearchParams] = useSearchParams() + const [selectedRows, setSelectedRows] = useState([]) return ( }> @@ -71,31 +75,47 @@ export default function Route({ loaderData: { data } }) { pageIndex={page - 1} pageSize={hitsPerPage} rowCount={totalHits} + setSelectedRows={setSelectedRows} > -
-
- - Digite / para - pesquisar - - } - defaultValue={searchParams.get('q') || ''} - onChange={(value) => - setSearchParams((searchParams) => { - searchParams.set('q', String(value)) - searchParams.delete('p') - return searchParams - }) - } - /> -
- +
+ {selectedRows.length ? ( + <> +
+ +
+ + ) : ( + <> +
+ + Digite /{' '} + para pesquisar + + } + defaultValue={searchParams.get('q') || ''} + onChange={(value) => + setSearchParams((searchParams) => { + searchParams.set('q', String(value)) + searchParams.delete('p') + return searchParams + }) + } + /> +
+ + + + )}
) diff --git a/packages/ui/src/components/export-menu.tsx b/packages/ui/src/components/export-menu.tsx new file mode 100644 index 0000000..3d8c8c7 --- /dev/null +++ b/packages/ui/src/components/export-menu.tsx @@ -0,0 +1,70 @@ +import type { BookType } from 'xlsx' +import { flatten } from 'flat' +import { + ChevronDownIcon, + DownloadIcon, + FileSpreadsheetIcon, + FileTextIcon +} from 'lucide-react' +import * as XLSX from 'xlsx' + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuTrigger +} from '@repo/ui/components/ui/dropdown-menu' +import { Button } from '@repo/ui/components/ui/button' + +export function ExportMenu({ + name, + headers, + selectedRows = [] +}: { + name: string + headers: Record + selectedRows: object[] +}) { + const exportFile = (bookType: BookType) => () => { + if (!selectedRows.length) { + return + } + 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, `${name}_${+new Date()}.${bookType}`, { + bookType, + compression: true + }) + } + + return ( + + + + + + + + Microsoft Excel (.xlsx) + + + CSV (.csv) + + + + + ) +} diff --git a/packages/ui/src/components/faceted-filter.tsx b/packages/ui/src/components/faceted-filter.tsx index 168f533..01fc307 100644 --- a/packages/ui/src/components/faceted-filter.tsx +++ b/packages/ui/src/components/faceted-filter.tsx @@ -108,7 +108,7 @@ export function FacetedFilter({ >