From e644d67022ebbdd916f7e2b6bb7cee55aa6b2078 Mon Sep 17 00:00:00 2001 From: Felipe Coutinho Date: Fri, 27 Feb 2026 15:40:52 +0000 Subject: [PATCH] refactor: extrair data fetching da page de pagadores para data.ts Co-Authored-By: Claude Opus 4.6 --- app/(dashboard)/pagadores/data.ts | 80 ++++++++++++++++++++++++++ app/(dashboard)/pagadores/page.tsx | 91 +----------------------------- lib/avatar/options.ts | 30 ++++++++++ 3 files changed, 113 insertions(+), 88 deletions(-) create mode 100644 app/(dashboard)/pagadores/data.ts create mode 100644 lib/avatar/options.ts diff --git a/app/(dashboard)/pagadores/data.ts b/app/(dashboard)/pagadores/data.ts new file mode 100644 index 0000000..1f53dec --- /dev/null +++ b/app/(dashboard)/pagadores/data.ts @@ -0,0 +1,80 @@ +import { eq } from "drizzle-orm"; +import { user } from "@/db/schema"; +import { loadAvatarOptions } from "@/lib/avatar/options"; +import { db } from "@/lib/db"; +import { fetchPagadoresWithAccess } from "@/lib/pagadores/access"; +import type { PagadorStatus } from "@/lib/pagadores/constants"; +import { + PAGADOR_ROLE_ADMIN, + PAGADOR_STATUS_OPTIONS, +} from "@/lib/pagadores/constants"; + +export type PagadorData = { + id: string; + name: string; + email: string | null; + avatarUrl: string | null; + status: PagadorStatus; + note: string | null; + role: string; + isAutoSend: boolean; + createdAt: string; + canEdit: boolean; + sharedByName: string | null; + sharedByEmail: string | null; + shareId: string | null; + shareCode: string | null; +}; + +const resolveStatus = (status: string | null): PagadorStatus => { + const normalized = status?.trim() ?? ""; + const found = PAGADOR_STATUS_OPTIONS.find( + (option) => option.toLowerCase() === normalized.toLowerCase(), + ); + return found ?? PAGADOR_STATUS_OPTIONS[0]; +}; + +export async function fetchPagadoresForUser( + userId: string, +): Promise<{ pagadores: PagadorData[]; avatarOptions: string[] }> { + const [pagadorRows, localAvatarOptions, userData] = await Promise.all([ + fetchPagadoresWithAccess(userId), + loadAvatarOptions(), + db.query.user.findFirst({ + columns: { image: true }, + where: eq(user.id, userId), + }), + ]); + + const userImage = userData?.image; + const avatarOptions = userImage + ? [userImage, ...localAvatarOptions] + : localAvatarOptions; + + const pagadores = pagadorRows + .map((pagador) => ({ + id: pagador.id, + name: pagador.name, + email: pagador.email, + avatarUrl: pagador.avatarUrl, + status: resolveStatus(pagador.status), + note: pagador.note, + role: pagador.role, + isAutoSend: pagador.isAutoSend ?? false, + createdAt: pagador.createdAt?.toISOString() ?? new Date().toISOString(), + canEdit: pagador.canEdit, + sharedByName: pagador.sharedByName ?? null, + sharedByEmail: pagador.sharedByEmail ?? null, + shareId: pagador.shareId ?? null, + shareCode: pagador.canEdit ? (pagador.shareCode ?? null) : null, + })) + .sort((a, b) => { + if (a.role === PAGADOR_ROLE_ADMIN && b.role !== PAGADOR_ROLE_ADMIN) + return -1; + if (a.role !== PAGADOR_ROLE_ADMIN && b.role === PAGADOR_ROLE_ADMIN) + return 1; + return 0; + }); + + return { pagadores, avatarOptions }; +} diff --git a/app/(dashboard)/pagadores/page.tsx b/app/(dashboard)/pagadores/page.tsx index 55ef0d5..67008d3 100644 --- a/app/(dashboard)/pagadores/page.tsx +++ b/app/(dashboard)/pagadores/page.tsx @@ -1,99 +1,14 @@ -import { readdir } from "node:fs/promises"; -import path from "node:path"; -import { eq } from "drizzle-orm"; import { PagadoresPage } from "@/components/pagadores/pagadores-page"; -import { user } from "@/db/schema"; import { getUserId } from "@/lib/auth/server"; -import { db } from "@/lib/db"; -import { fetchPagadoresWithAccess } from "@/lib/pagadores/access"; -import type { PagadorStatus } from "@/lib/pagadores/constants"; -import { - DEFAULT_PAGADOR_AVATAR, - PAGADOR_ROLE_ADMIN, - PAGADOR_STATUS_OPTIONS, -} from "@/lib/pagadores/constants"; - -const AVATAR_DIRECTORY = path.join(process.cwd(), "public", "avatares"); -const AVATAR_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".svg", ".webp"]); - -async function loadAvatarOptions() { - try { - const files = await readdir(AVATAR_DIRECTORY, { withFileTypes: true }); - - const items = files - .filter((file) => file.isFile()) - .map((file) => file.name) - .filter((file) => AVATAR_EXTENSIONS.has(path.extname(file).toLowerCase())) - .sort((a, b) => a.localeCompare(b, "pt-BR", { sensitivity: "base" })); - - if (items.length === 0) { - items.push(DEFAULT_PAGADOR_AVATAR); - } - - return Array.from(new Set(items)); - } catch { - return [DEFAULT_PAGADOR_AVATAR]; - } -} - -const resolveStatus = (status: string | null): PagadorStatus => { - const normalized = status?.trim() ?? ""; - const found = PAGADOR_STATUS_OPTIONS.find( - (option) => option.toLowerCase() === normalized.toLowerCase(), - ); - return found ?? PAGADOR_STATUS_OPTIONS[0]; -}; +import { fetchPagadoresForUser } from "./data"; export default async function Page() { const userId = await getUserId(); - - const [pagadorRows, localAvatarOptions, userData] = await Promise.all([ - fetchPagadoresWithAccess(userId), - loadAvatarOptions(), - db.query.user.findFirst({ - columns: { image: true }, - where: eq(user.id, userId), - }), - ]); - - // Incluir a imagem do Google nas opções se disponível - const userImage = userData?.image; - const avatarOptions = userImage - ? [userImage, ...localAvatarOptions] - : localAvatarOptions; - - const pagadoresData = pagadorRows - .map((pagador) => ({ - id: pagador.id, - name: pagador.name, - email: pagador.email, - avatarUrl: pagador.avatarUrl, - status: resolveStatus(pagador.status), - note: pagador.note, - role: pagador.role, - isAutoSend: pagador.isAutoSend ?? false, - createdAt: pagador.createdAt?.toISOString() ?? new Date().toISOString(), - canEdit: pagador.canEdit, - sharedByName: pagador.sharedByName ?? null, - sharedByEmail: pagador.sharedByEmail ?? null, - shareId: pagador.shareId ?? null, - shareCode: pagador.canEdit ? (pagador.shareCode ?? null) : null, - })) - .sort((a, b) => { - // Admin sempre primeiro - if (a.role === PAGADOR_ROLE_ADMIN && b.role !== PAGADOR_ROLE_ADMIN) { - return -1; - } - if (a.role !== PAGADOR_ROLE_ADMIN && b.role === PAGADOR_ROLE_ADMIN) { - return 1; - } - // Se ambos são admin ou ambos não são, mantém ordem original - return 0; - }); + const { pagadores, avatarOptions } = await fetchPagadoresForUser(userId); return (
- +
); } diff --git a/lib/avatar/options.ts b/lib/avatar/options.ts new file mode 100644 index 0000000..20b414e --- /dev/null +++ b/lib/avatar/options.ts @@ -0,0 +1,30 @@ +import { readdir } from "node:fs/promises"; +import path from "node:path"; +import { DEFAULT_PAGADOR_AVATAR } from "@/lib/pagadores/constants"; + +const AVATAR_DIRECTORY = path.join(process.cwd(), "public", "avatares"); +const AVATAR_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".svg", ".webp"]); + +/** + * Loads available avatar files from the public/avatares directory + * @returns Array of unique avatar filenames sorted alphabetically + */ +export async function loadAvatarOptions() { + try { + const files = await readdir(AVATAR_DIRECTORY, { withFileTypes: true }); + + const items = files + .filter((file) => file.isFile()) + .map((file) => file.name) + .filter((file) => AVATAR_EXTENSIONS.has(path.extname(file).toLowerCase())) + .sort((a, b) => a.localeCompare(b, "pt-BR", { sensitivity: "base" })); + + if (items.length === 0) { + items.push(DEFAULT_PAGADOR_AVATAR); + } + + return Array.from(new Set(items)); + } catch { + return [DEFAULT_PAGADOR_AVATAR]; + } +}