refactor: migrate from ESLint to Biome and extract SQL queries to data.ts
- Replace ESLint with Biome for linting and formatting - Configure Biome with tabs, double quotes, and organized imports - Move all SQL/Drizzle queries from page.tsx files to data.ts files - Create new data.ts files for: ajustes, dashboard, relatorios/categorias - Update existing data.ts files: extrato, fatura (add lancamentos queries) - Remove all drizzle-orm imports from page.tsx files - Update README.md with new tooling info Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,121 +1,121 @@
|
||||
"use client";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { PAGADOR_ROLE_ADMIN } from "@/lib/pagadores/constants";
|
||||
import { getAvatarSrc } from "@/lib/pagadores/utils";
|
||||
import {
|
||||
RiDeleteBin5Line,
|
||||
RiEyeLine,
|
||||
RiMailSendLine,
|
||||
RiPencilLine,
|
||||
RiVerifiedBadgeFill,
|
||||
RiDeleteBin5Line,
|
||||
RiEyeLine,
|
||||
RiMailSendLine,
|
||||
RiPencilLine,
|
||||
RiVerifiedBadgeFill,
|
||||
} from "@remixicon/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useMemo } from "react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { PAGADOR_ROLE_ADMIN } from "@/lib/pagadores/constants";
|
||||
import { getAvatarSrc } from "@/lib/pagadores/utils";
|
||||
import type { Pagador } from "./types";
|
||||
|
||||
interface PagadorCardProps {
|
||||
pagador: Pagador;
|
||||
onEdit?: () => void;
|
||||
onRemove?: () => void;
|
||||
pagador: Pagador;
|
||||
onEdit?: () => void;
|
||||
onRemove?: () => void;
|
||||
}
|
||||
|
||||
export function PagadorCard({ pagador, onEdit, onRemove }: PagadorCardProps) {
|
||||
const avatarSrc = useMemo(
|
||||
() => getAvatarSrc(pagador.avatarUrl),
|
||||
[pagador.avatarUrl]
|
||||
);
|
||||
const avatarSrc = useMemo(
|
||||
() => getAvatarSrc(pagador.avatarUrl),
|
||||
[pagador.avatarUrl],
|
||||
);
|
||||
|
||||
const isAdmin = pagador.role === PAGADOR_ROLE_ADMIN;
|
||||
const isReadOnly = !pagador.canEdit;
|
||||
const isAdmin = pagador.role === PAGADOR_ROLE_ADMIN;
|
||||
const isReadOnly = !pagador.canEdit;
|
||||
|
||||
return (
|
||||
<Card className=" overflow-hidden px-6">
|
||||
{/* Avatar posicionado sobre o header */}
|
||||
<div className="relative flex flex-col items-start">
|
||||
<div className="relative mb-3 flex size-16 items-center justify-center overflow-hidden rounded-full border-background bg-background shadow-lg">
|
||||
<Image
|
||||
src={avatarSrc}
|
||||
alt={`Avatar de ${pagador.name}`}
|
||||
width={80}
|
||||
height={80}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
return (
|
||||
<Card className=" overflow-hidden px-6">
|
||||
{/* Avatar posicionado sobre o header */}
|
||||
<div className="relative flex flex-col items-start">
|
||||
<div className="relative mb-3 flex size-16 items-center justify-center overflow-hidden rounded-full border-background bg-background shadow-lg">
|
||||
<Image
|
||||
src={avatarSrc}
|
||||
alt={`Avatar de ${pagador.name}`}
|
||||
width={80}
|
||||
height={80}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Nome e badges */}
|
||||
<div className="flex items-center gap-1.5">
|
||||
<h3 className="text-base font-semibold text-foreground">
|
||||
{pagador.name}
|
||||
</h3>
|
||||
{isAdmin ? (
|
||||
<RiVerifiedBadgeFill className="size-4 text-blue-500" aria-hidden />
|
||||
) : null}
|
||||
{pagador.isAutoSend ? (
|
||||
<RiMailSendLine className="size-4 text-primary" aria-hidden />
|
||||
) : null}
|
||||
</div>
|
||||
{/* Nome e badges */}
|
||||
<div className="flex items-center gap-1.5">
|
||||
<h3 className="text-base font-semibold text-foreground">
|
||||
{pagador.name}
|
||||
</h3>
|
||||
{isAdmin ? (
|
||||
<RiVerifiedBadgeFill className="size-4 text-blue-500" aria-hidden />
|
||||
) : null}
|
||||
{pagador.isAutoSend ? (
|
||||
<RiMailSendLine className="size-4 text-primary" aria-hidden />
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
{pagador.email ? (
|
||||
<p className="mt-1 text-xs text-muted-foreground">{pagador.email}</p>
|
||||
) : (
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
Sem Email cadastrado
|
||||
</p>
|
||||
)}
|
||||
{/* Email */}
|
||||
{pagador.email ? (
|
||||
<p className="mt-1 text-xs text-muted-foreground">{pagador.email}</p>
|
||||
) : (
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
Sem Email cadastrado
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Status badges */}
|
||||
<div className="mt-2 flex flex-wrap items-center justify-center gap-1.5">
|
||||
<Badge
|
||||
variant={pagador.status === "Ativo" ? "success" : "outline"}
|
||||
className="text-xs"
|
||||
>
|
||||
{pagador.status}
|
||||
</Badge>
|
||||
{/* Status badges */}
|
||||
<div className="mt-2 flex flex-wrap items-center justify-center gap-1.5">
|
||||
<Badge
|
||||
variant={pagador.status === "Ativo" ? "success" : "outline"}
|
||||
className="text-xs"
|
||||
>
|
||||
{pagador.status}
|
||||
</Badge>
|
||||
|
||||
{isReadOnly ? (
|
||||
<Badge variant="outline" className="text-xs text-amber-600">
|
||||
Somente leitura
|
||||
</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{isReadOnly ? (
|
||||
<Badge variant="outline" className="text-xs text-amber-600">
|
||||
Somente leitura
|
||||
</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer com links */}
|
||||
<div className="flex flex-wrap items-start justify-start gap-3 text-sm font-medium">
|
||||
{!isReadOnly && onEdit ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onEdit}
|
||||
className={`text-primary flex items-center gap-1 font-medium transition-opacity hover:opacity-80`}
|
||||
>
|
||||
<RiPencilLine className="size-4" aria-hidden />
|
||||
editar
|
||||
</button>
|
||||
) : null}
|
||||
{/* Footer com links */}
|
||||
<div className="flex flex-wrap items-start justify-start gap-3 text-sm font-medium">
|
||||
{!isReadOnly && onEdit ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onEdit}
|
||||
className={`text-primary flex items-center gap-1 font-medium transition-opacity hover:opacity-80`}
|
||||
>
|
||||
<RiPencilLine className="size-4" aria-hidden />
|
||||
editar
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
<Link
|
||||
href={`/pagadores/${pagador.id}`}
|
||||
className={`text-primary flex items-center gap-1 font-medium transition-opacity hover:opacity-80`}
|
||||
>
|
||||
<RiEyeLine className="size-4" aria-hidden />
|
||||
detalhes
|
||||
</Link>
|
||||
<Link
|
||||
href={`/pagadores/${pagador.id}`}
|
||||
className={`text-primary flex items-center gap-1 font-medium transition-opacity hover:opacity-80`}
|
||||
>
|
||||
<RiEyeLine className="size-4" aria-hidden />
|
||||
detalhes
|
||||
</Link>
|
||||
|
||||
{!isAdmin && !isReadOnly && onRemove ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRemove}
|
||||
className={`text-destructive flex items-center gap-1 font-medium transition-opacity hover:opacity-80`}
|
||||
>
|
||||
<RiDeleteBin5Line className="size-4" aria-hidden />
|
||||
remover
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
{!isAdmin && !isReadOnly && onRemove ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRemove}
|
||||
className={`text-destructive flex items-center gap-1 font-medium transition-opacity hover:opacity-80`}
|
||||
>
|
||||
<RiDeleteBin5Line className="size-4" aria-hidden />
|
||||
remover
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user