forked from git.gladyson/openmonetis
- 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>
122 lines
3.3 KiB
TypeScript
122 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
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;
|
|
}
|
|
|
|
export function PagadorCard({ pagador, onEdit, onRemove }: PagadorCardProps) {
|
|
const avatarSrc = useMemo(
|
|
() => getAvatarSrc(pagador.avatarUrl),
|
|
[pagador.avatarUrl],
|
|
);
|
|
|
|
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>
|
|
|
|
{/* 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>
|
|
)}
|
|
|
|
{/* 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>
|
|
|
|
{/* 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>
|
|
|
|
{!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>
|
|
);
|
|
}
|