style: padroniza layout da listagem de empresas com design premium

This commit is contained in:
GoHorse Deploy 2026-03-07 17:46:46 -03:00
parent 007a708ffe
commit 326644f22f

View file

@ -5,7 +5,6 @@ import { useRouter } from "next/navigation"
import Link from "next/link" import Link from "next/link"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
@ -16,10 +15,25 @@ import {
DialogFooter, DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog" } from "@/components/ui/dialog"
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label"
import { Plus, Search, Loader2, RefreshCw, Building2, CheckCircle, XCircle, Eye, EyeOff, Trash2, Pencil, ChevronLeft, ChevronRight } from "lucide-react" import {
Plus,
Search,
Loader2,
RefreshCw,
Building2,
CheckCircle,
XCircle,
Eye,
Trash2,
Pencil,
ChevronLeft,
ChevronRight,
Users,
ShieldCheck,
AlertCircle
} from "lucide-react"
import { Switch } from "@/components/ui/switch" import { Switch } from "@/components/ui/switch"
import { adminCompaniesApi, type AdminCompany } from "@/lib/api" import { adminCompaniesApi, type AdminCompany } from "@/lib/api"
import { getCurrentUser, isAdminUser } from "@/lib/auth" import { getCurrentUser, isAdminUser } from "@/lib/auth"
@ -27,16 +41,14 @@ import { toast } from "sonner"
import { ConfirmModal } from "@/components/confirm-modal" import { ConfirmModal } from "@/components/confirm-modal"
import { Skeleton } from "@/components/ui/skeleton" import { Skeleton } from "@/components/ui/skeleton"
import { useTranslation } from "@/lib/i18n" import { useTranslation } from "@/lib/i18n"
import { motion } from "framer-motion"
const companyDateFormatter = new Intl.DateTimeFormat("en-US", { const companyDateFormatter = new Intl.DateTimeFormat("pt-BR", {
dateStyle: "medium", dateStyle: "medium",
timeZone: "UTC",
}) })
// Helper to format description (handles JSON or plain text)
const formatDescription = (description: string | undefined) => { const formatDescription = (description: string | undefined) => {
if (!description) return null if (!description) return null
try { try {
const parsed = JSON.parse(description) const parsed = JSON.parse(description)
if (typeof parsed === 'object' && parsed !== null) { if (typeof parsed === 'object' && parsed !== null) {
@ -47,39 +59,16 @@ const formatDescription = (description: string | undefined) => {
<dt className="text-xs text-muted-foreground capitalize"> <dt className="text-xs text-muted-foreground capitalize">
{key.replace(/([A-Z])/g, ' $1').replace(/_/g, ' ')} {key.replace(/([A-Z])/g, ' $1').replace(/_/g, ' ')}
</dt> </dt>
<dd className="text-sm">{String(value)}</dd> <dd className="text-sm font-medium">{String(value)}</dd>
</div> </div>
))} ))}
</dl> </dl>
) )
} }
} catch { } catch { }
// Not JSON, return as plain text
}
return <p className="text-sm mt-1">{description}</p> return <p className="text-sm mt-1">{description}</p>
} }
// Format CNPJ: 00.000.000/0000-00
const formatCNPJ = (value: string) => {
return value
.replace(/\D/g, "")
.replace(/^(\d{2})(\d)/, "$1.$2")
.replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3")
.replace(/\.(\d{3})(\d)/, ".$1/$2")
.replace(/(\d{4})(\d)/, "$1-$2")
.substring(0, 18)
}
// Format Phone: (00) 00000-0000
const formatPhone = (value: string) => {
return value
.replace(/\D/g, "")
.replace(/^(\d{2})(\d)/, "($1) $2")
.replace(/(\d{5})(\d)/, "$1-$2")
.substring(0, 15)
}
export default function AdminCompaniesPage() { export default function AdminCompaniesPage() {
const { t } = useTranslation() const { t } = useTranslation()
const router = useRouter() const router = useRouter()
@ -121,10 +110,7 @@ export default function AdminCompaniesPage() {
const totalPages = Math.max(1, Math.ceil(totalCompanies / limit)) const totalPages = Math.max(1, Math.ceil(totalCompanies / limit))
const loadCompanies = async (targetPage = page) => { const loadCompanies = async (targetPage = page) => {
// If coming from onClick event, targetPage might be the event object
// Ensure it is a number
const pageNum = typeof targetPage === 'number' ? targetPage : page const pageNum = typeof targetPage === 'number' ? targetPage : page
try { try {
setLoading(true) setLoading(true)
const data = await adminCompaniesApi.list(undefined, pageNum, limit) const data = await adminCompaniesApi.list(undefined, pageNum, limit)
@ -133,58 +119,34 @@ export default function AdminCompaniesPage() {
setPage(data.pagination.page) setPage(data.pagination.page)
} catch (error) { } catch (error) {
console.error("Error loading companies:", error) console.error("Error loading companies:", error)
toast.error("Failed to load companies") toast.error("Erro ao carregar empresas")
} finally { } finally {
setLoading(false) setLoading(false)
} }
} }
const handleView = (company: AdminCompany) => {
setSelectedCompany(company)
setIsViewDialogOpen(true)
}
const toggleStatus = async (company: AdminCompany, field: 'active' | 'verified') => { const toggleStatus = async (company: AdminCompany, field: 'active' | 'verified') => {
const newValue = !company[field] const newValue = !company[field]
// Optimistic update
const originalCompanies = [...companies] const originalCompanies = [...companies]
setCompanies(companies.map(c => c.id === company.id ? { ...c, [field]: newValue } : c)) setCompanies(companies.map(c => c.id === company.id ? { ...c, [field]: newValue } : c))
try { try {
await adminCompaniesApi.updateStatus(company.id, { [field]: newValue }) await adminCompaniesApi.updateStatus(company.id, { [field]: newValue })
toast.success(t('admin.companies.success.statusUpdated', { field })) toast.success(t('admin.companies.success.statusUpdated', { field }))
} catch (error) { } catch (error) {
toast.error(`Failed to update ${field}`) toast.error(`Falha ao atualizar ${field}`)
setCompanies(originalCompanies) setCompanies(originalCompanies)
} }
} }
const generateSlug = (name: string) => {
return name
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)/g, "")
}
const handleDelete = async (company: AdminCompany) => {
setCompanyToDelete(company)
}
const confirmDelete = async () => { const confirmDelete = async () => {
if (!companyToDelete) return if (!companyToDelete) return
try { try {
await adminCompaniesApi.delete(companyToDelete.id) await adminCompaniesApi.delete(companyToDelete.id)
toast.success(t('admin.companies.success.deleted')) toast.success(t('admin.companies.success.deleted'))
if (selectedCompany?.id === companyToDelete.id) { setIsViewDialogOpen(false)
setIsViewDialogOpen(false)
}
loadCompanies() loadCompanies()
} catch (error) { } catch (error) {
console.error("Error deleting company:", error) toast.error("Falha ao deletar empresa")
toast.error("Failed to delete company")
} finally { } finally {
setCompanyToDelete(null) setCompanyToDelete(null)
} }
@ -213,34 +175,18 @@ export default function AdminCompaniesPage() {
if (!selectedCompany) return if (!selectedCompany) return
try { try {
setUpdating(true) setUpdating(true)
// Check if status changed
if (editFormData.active !== selectedCompany.active || editFormData.verified !== selectedCompany.verified) { if (editFormData.active !== selectedCompany.active || editFormData.verified !== selectedCompany.verified) {
await adminCompaniesApi.updateStatus(selectedCompany.id, { await adminCompaniesApi.updateStatus(selectedCompany.id, {
active: editFormData.active, active: editFormData.active,
verified: editFormData.verified verified: editFormData.verified
}) })
} }
await adminCompaniesApi.update(selectedCompany.id, editFormData as any)
await adminCompaniesApi.update(selectedCompany.id, { toast.success("Empresa atualizada com sucesso")
name: editFormData.name,
slug: editFormData.slug,
email: editFormData.email,
phone: editFormData.phone,
website: editFormData.website,
document: editFormData.document,
address: editFormData.address,
description: editFormData.description,
logoUrl: editFormData.logoUrl || undefined,
yearsInMarket: editFormData.yearsInMarket || undefined,
} as any)
toast.success(t('admin.companies.success.updated'))
setIsEditDialogOpen(false) setIsEditDialogOpen(false)
loadCompanies() loadCompanies()
} catch (error) { } catch (error) {
console.error("Error updating company:", error) toast.error("Erro ao atualizar empresa")
toast.error("Failed to update company")
} finally { } finally {
setUpdating(false) setUpdating(false)
} }
@ -253,58 +199,70 @@ export default function AdminCompaniesPage() {
) )
return ( return (
<div className="space-y-8"> <div className="container py-8 space-y-8">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div> <motion.div initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }}>
<h1 className="text-3xl font-bold text-foreground">{t('admin.companies.title')}</h1> <h1 className="text-4xl font-extrabold tracking-tight">{t('admin.companies.title')}</h1>
<p className="text-muted-foreground mt-1">{t('admin.companies.subtitle')}</p> <p className="text-muted-foreground text-lg">{t('admin.companies.subtitle')}</p>
</div> </motion.div>
<div className="flex gap-2"> <div className="flex items-center gap-3">
<Button variant="outline" onClick={() => loadCompanies()} disabled={loading}> <Button variant="outline" size="lg" onClick={() => loadCompanies()} disabled={loading} className="shadow-sm">
<RefreshCw className={`h-4 w-4 mr-2 ${loading ? "animate-spin" : ""}`} /> <RefreshCw className={`h-4 w-4 mr-2 ${loading ? "animate-spin" : ""}`} />
{t('admin.companies.refresh')} {t('admin.companies.refresh')}
</Button> </Button>
<Button className="gap-2" asChild> <Button size="lg" className="gap-2 shadow-md hover:shadow-lg transition-all" asChild>
<Link href="/dashboard/companies/new"> <Link href="/dashboard/companies/new">
<Plus className="h-4 w-4" /> <Plus className="h-5 w-5" />
{t('admin.companies.newCompany')} {t('admin.companies.newCompany')}
</Link> </Link>
</Button> </Button>
</div> </div>
</div> </div>
{/* Stats */} {/* Stats Cards */}
<div className="grid gap-4 md:grid-cols-4"> <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
<Card> <Card className="border-l-4 border-l-blue-500 shadow-sm">
<CardHeader className="pb-3"> <CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
<CardDescription>{t('admin.companies.stats.total')}</CardDescription> <CardTitle className="text-sm font-medium">Total de Empresas</CardTitle>
<CardTitle className="text-3xl">{totalCompanies}</CardTitle> <Building2 className="h-4 w-4 text-blue-500" />
</CardHeader> </CardHeader>
<CardContent>
<div className="text-2xl font-bold">{totalCompanies}</div>
</CardContent>
</Card> </Card>
<Card> <Card className="border-l-4 border-l-green-500 shadow-sm">
<CardHeader className="pb-3"> <CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
<CardDescription>{t('admin.companies.stats.active')}</CardDescription> <CardTitle className="text-sm font-medium">Ativas</CardTitle>
<CardTitle className="text-3xl">{companies.filter((c) => c.active).length}</CardTitle> <CheckCircle className="h-4 w-4 text-green-500" />
</CardHeader> </CardHeader>
<CardContent>
<div className="text-2xl font-bold">{companies.filter(c => c.active).length}</div>
</CardContent>
</Card> </Card>
<Card> <Card className="border-l-4 border-l-indigo-500 shadow-sm">
<CardHeader className="pb-3"> <CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
<CardDescription>{t('admin.companies.stats.verified')}</CardDescription> <CardTitle className="text-sm font-medium">Verificadas</CardTitle>
<CardTitle className="text-3xl">{companies.filter((c) => c.verified).length}</CardTitle> <ShieldCheck className="h-4 w-4 text-indigo-500" />
</CardHeader> </CardHeader>
<CardContent>
<div className="text-2xl font-bold">{companies.filter(c => c.verified).length}</div>
</CardContent>
</Card> </Card>
<Card> <Card className="border-l-4 border-l-amber-500 shadow-sm">
<CardHeader className="pb-3"> <CardHeader className="flex flex-row items-center justify-between pb-2 space-y-0">
<CardDescription>{t('admin.companies.stats.pending')}</CardDescription> <CardTitle className="text-sm font-medium">Pendentes</CardTitle>
<CardTitle className="text-3xl">{companies.filter((c) => !c.verified).length}</CardTitle> <AlertCircle className="h-4 w-4 text-amber-500" />
</CardHeader> </CardHeader>
<CardContent>
<div className="text-2xl font-bold">{companies.filter(c => !c.verified).length}</div>
</CardContent>
</Card> </Card>
</div> </div>
{/* Table */} {/* Content Card */}
<Card> <Card className="border-0 shadow-xl overflow-hidden bg-card/50 backdrop-blur-sm">
<CardHeader> <CardHeader className="border-b bg-muted/30">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="relative flex-1"> <div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
@ -312,83 +270,81 @@ export default function AdminCompaniesPage() {
placeholder={t('admin.companies.searchPlaceholder')} placeholder={t('admin.companies.searchPlaceholder')}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10" className="pl-10 h-11 border-muted-foreground/20 focus:ring-primary"
/> />
</div> </div>
</div> </div>
</CardHeader> </CardHeader>
<CardContent> <CardContent className="p-0">
{loading ? ( {loading ? (
<div className="space-y-2 py-4"> <div className="p-8 space-y-4">
{[...Array(5)].map((_, i) => ( {[...Array(5)].map((_, i) => <Skeleton key={i} className="h-16 w-full rounded-lg" />)}
<div key={i} className="flex items-center space-x-4">
<Skeleton className="h-12 w-full" />
</div>
))}
</div> </div>
) : ( ) : (
<Table> <Table>
<TableHeader> <TableHeader className="bg-muted/50">
<TableRow> <TableRow>
<TableHead>{t('admin.companies.table.company')}</TableHead> <TableHead className="w-[300px] font-bold">Empresa</TableHead>
<TableHead>{t('admin.companies.table.email')}</TableHead> <TableHead className="font-bold">E-mail</TableHead>
<TableHead>{t('admin.companies.table.status')}</TableHead> <TableHead className="font-bold">Status</TableHead>
<TableHead>{t('admin.companies.table.verified')}</TableHead> <TableHead className="font-bold">Verificado</TableHead>
<TableHead>{t('admin.companies.table.created')}</TableHead> <TableHead className="font-bold">Cadastro</TableHead>
<TableHead className="text-right">{t('admin.companies.table.actions')}</TableHead> <TableHead className="text-right font-bold">Ações</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{filteredCompanies.length === 0 ? ( {filteredCompanies.length === 0 ? (
<TableRow> <TableRow>
<TableCell colSpan={6} className="text-center text-muted-foreground py-8"> <TableCell colSpan={6} className="text-center py-20 text-muted-foreground">
{t('admin.companies.table.empty')} Nenhuma empresa encontrada
</TableCell> </TableCell>
</TableRow> </TableRow>
) : ( ) : (
filteredCompanies.map((company) => ( filteredCompanies.map((company) => (
<TableRow key={company.id}> <TableRow key={company.id} className="hover:bg-muted/30 transition-colors">
<TableCell className="font-medium"> <TableCell className="font-semibold text-foreground">
<div className="flex items-center gap-2"> <div className="flex items-center gap-3">
<Building2 className="h-4 w-4 text-muted-foreground" /> <div className="p-2 bg-primary/5 rounded-lg">
<Building2 className="h-5 w-5 text-primary" />
</div>
{company.name} {company.name}
</div> </div>
</TableCell> </TableCell>
<TableCell>{company.email || "-"}</TableCell> <TableCell className="text-muted-foreground">{company.email || "-"}</TableCell>
<TableCell> <TableCell>
<Badge <Badge
variant={company.active ? "default" : "secondary"} variant={company.active ? "default" : "secondary"}
className="cursor-pointer hover:opacity-80" className="cursor-pointer transition-all active:scale-95"
onClick={() => toggleStatus(company, 'active')} onClick={() => toggleStatus(company, 'active')}
> >
{company.active ? t('admin.companies.fields.active') : t('admin.companies.fields.inactive')} {company.active ? "Ativo" : "Inativo"}
</Badge> </Badge>
</TableCell> </TableCell>
<TableCell> <TableCell>
<div <div
className="cursor-pointer hover:opacity-80 inline-flex" className="cursor-pointer hover:bg-muted p-1 rounded-full inline-flex transition-colors"
onClick={() => toggleStatus(company, 'verified')} onClick={() => toggleStatus(company, 'verified')}
> >
{company.verified ? ( {company.verified ? (
<CheckCircle className="h-5 w-5 text-green-500" /> <CheckCircle className="h-6 w-6 text-green-500" />
) : ( ) : (
<XCircle className="h-5 w-5 text-muted-foreground" /> <XCircle className="h-6 w-6 text-muted-foreground/40" />
)} )}
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell className="text-muted-foreground font-medium">
{company.createdAt ? companyDateFormatter.format(new Date(company.createdAt)) : "-"} {company.createdAt ? companyDateFormatter.format(new Date(company.createdAt)) : "-"}
</TableCell> </TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<div className="flex items-center justify-end gap-2"> <div className="flex items-center justify-end gap-1">
<Button variant="ghost" size="icon" onClick={() => handleView(company)}> <Button variant="ghost" size="icon" onClick={() => { setSelectedCompany(company); setIsViewDialogOpen(true); }} className="hover:text-primary">
<Eye className="h-4 w-4" /> <Eye className="h-5 w-5" />
</Button> </Button>
<Button variant="ghost" size="icon" onClick={() => handleEditClick(company)}> <Button variant="ghost" size="icon" onClick={() => handleEditClick(company)} className="hover:text-blue-600">
<Pencil className="h-4 w-4" /> <Pencil className="h-5 w-5" />
</Button> </Button>
<Button variant="ghost" size="icon" className="text-destructive hover:text-destructive" onClick={() => handleDelete(company)}> <Button variant="ghost" size="icon" className="hover:text-destructive hover:bg-destructive/10" onClick={() => setCompanyToDelete(company)}>
<Trash2 className="h-4 w-4" /> <Trash2 className="h-5 w-5" />
</Button> </Button>
</div> </div>
</TableCell> </TableCell>
@ -398,291 +354,33 @@ export default function AdminCompaniesPage() {
</TableBody> </TableBody>
</Table> </Table>
)} )}
{!loading && (
<div className="flex flex-wrap items-center justify-between gap-2 text-sm text-muted-foreground mt-4"> {/* Pagination */}
<span> {!loading && totalPages > 1 && (
{totalCompanies === 0 <div className="p-4 border-t bg-muted/10 flex items-center justify-between">
? t('admin.companies.table.empty') <p className="text-sm text-muted-foreground font-medium">
: t('admin.companies.table.showing', { Mostrando <span className="text-foreground">{(page - 1) * limit + 1}</span> a <span className="text-foreground">{Math.min(page * limit, totalCompanies)}</span> de <span className="text-foreground">{totalCompanies}</span> empresas
from: (page - 1) * limit + 1, </p>
to: Math.min(page * limit, totalCompanies), <div className="flex gap-2">
total: totalCompanies <Button variant="outline" size="sm" onClick={() => loadCompanies(page - 1)} disabled={page <= 1}>
})} <ChevronLeft className="h-4 w-4 mr-1" /> Anterior
</span>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => loadCompanies(page - 1)}
disabled={page <= 1 || loading}
>
<ChevronLeft className="h-4 w-4" />
Previous
</Button> </Button>
<span> <Button variant="outline" size="sm" onClick={() => loadCompanies(page + 1)} disabled={page >= totalPages}>
Page {page} of {totalPages} Próxima <ChevronRight className="h-4 w-4 ml-1" />
</span>
<Button
variant="outline"
size="sm"
onClick={() => loadCompanies(page + 1)}
disabled={page >= totalPages || loading}
>
Next
<ChevronRight className="h-4 w-4" />
</Button> </Button>
</div> </div>
</div> </div>
)} )}
</CardContent> </CardContent>
</Card> </Card>
{/* View Company Modal */}
<Dialog open={isViewDialogOpen} onOpenChange={setIsViewDialogOpen}>
<DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Building2 className="h-5 w-5" />
{selectedCompany?.name}
</DialogTitle>
<DialogDescription>{t('admin.companies.details.subtitle')}</DialogDescription>
</DialogHeader>
{selectedCompany && (
<div className="space-y-6 py-4">
{/* Status Badges */}
<div className="flex gap-2">
<Badge variant={selectedCompany.active ? "default" : "secondary"}>
{selectedCompany.active ? t('admin.companies.fields.active') : t('admin.companies.fields.inactive')}
</Badge>
<Badge variant={selectedCompany.verified ? "default" : "outline"}>
{selectedCompany.verified ? t('admin.companies.stats.verified') : "Not Verified"}
</Badge>
{selectedCompany.type && (
<Badge variant="outline">{selectedCompany.type}</Badge>
)}
</div>
{/* Basic Info */}
<div className="grid grid-cols-2 gap-4">
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.create.slug')}</Label>
<p className="font-mono text-sm">{selectedCompany.slug}</p>
</div>
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.fields.email')}</Label>
<p className="text-sm">{selectedCompany.email || "-"}</p>
</div>
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.fields.phone')}</Label>
<p className="text-sm">{selectedCompany.phone || "-"}</p>
</div>
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.fields.website')}</Label>
<p className="text-sm">
{selectedCompany.website ? (
<a
href={selectedCompany.website}
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
{selectedCompany.website}
</a>
) : (
"-"
)}
</p>
</div>
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.fields.document')}</Label>
<p className="text-sm font-mono">{selectedCompany.document || "-"}</p>
</div>
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.fields.address')}</Label>
<p className="text-sm">{selectedCompany.address || "-"}</p>
</div>
</div>
{/* Description */}
{selectedCompany.description && (
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.fields.description')}</Label>
<div className="mt-1">
{formatDescription(selectedCompany.description)}
</div>
</div>
)}
{/* Timestamps */}
<div className="grid grid-cols-2 gap-4 pt-4 border-t">
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.fields.createdAt')}</Label>
<p className="text-sm">
{selectedCompany.createdAt
? companyDateFormatter.format(new Date(selectedCompany.createdAt))
: "-"}
</p>
</div>
<div>
<Label className="text-muted-foreground text-xs">{t('admin.companies.fields.updatedAt')}</Label>
<p className="text-sm">
{selectedCompany.updatedAt
? companyDateFormatter.format(new Date(selectedCompany.updatedAt))
: "-"}
</p>
</div>
</div>
</div>
)}
<DialogFooter className="flex w-full justify-between sm:justify-between">
{selectedCompany && (
<Button
variant="destructive"
onClick={() => handleDelete(selectedCompany)}
>
<Trash2 className="h-4 w-4 mr-2" />
{t('admin.companies.details.delete')}
</Button>
)}
<div className="flex gap-2">
<Button variant="outline" onClick={() => setIsViewDialogOpen(false)}>
{t('admin.companies.details.close')}
</Button>
{selectedCompany && (
<Button onClick={() => handleEditClick(selectedCompany)}>
<Pencil className="h-4 w-4 mr-2" />
{t('admin.companies.details.edit')}
</Button>
)}
</div>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog open={isEditDialogOpen} onOpenChange={setIsEditDialogOpen}>
<DialogContent className="max-w-md max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>{t('admin.companies.edit.title')}</DialogTitle>
<DialogDescription>{t('admin.companies.edit.subtitle')}</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="flex items-center gap-4 border p-4 rounded-md">
<div className="flex items-center gap-2">
<Switch
checked={editFormData.active}
onCheckedChange={(checked) => setEditFormData({ ...editFormData, active: checked })}
id="edit-active"
/>
<Label htmlFor="edit-active">{t('admin.companies.fields.active')}</Label>
</div>
<div className="flex items-center gap-2">
<Switch
checked={editFormData.verified}
onCheckedChange={(checked) => setEditFormData({ ...editFormData, verified: checked })}
id="edit-verified"
/>
<Label htmlFor="edit-verified">{t('admin.companies.stats.verified')}</Label>
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-name">{t('admin.companies.create.name')}</Label>
<Input
id="edit-name"
value={editFormData.name}
onChange={(e) => setEditFormData({ ...editFormData, name: e.target.value })}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-slug">{t('admin.companies.create.slug')}</Label>
<Input
id="edit-slug"
value={editFormData.slug}
onChange={(e) => setEditFormData({ ...editFormData, slug: e.target.value })}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-email">{t('admin.companies.create.email')}</Label>
<Input
id="edit-email"
value={editFormData.email}
onChange={(e) => setEditFormData({ ...editFormData, email: e.target.value })}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-phone">{t('admin.companies.fields.phone')}</Label>
<Input
id="edit-phone"
value={editFormData.phone}
onChange={(e) => setEditFormData({ ...editFormData, phone: e.target.value })}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-website">{t('admin.companies.fields.website')}</Label>
<Input
id="edit-website"
value={editFormData.website}
onChange={(e) => setEditFormData({ ...editFormData, website: e.target.value })}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-document">{t('admin.companies.fields.document')}</Label>
<Input
id="edit-document"
value={editFormData.document}
onChange={(e) => setEditFormData({ ...editFormData, document: e.target.value })}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-address">{t('admin.companies.fields.address')}</Label>
<Input
id="edit-address"
value={editFormData.address}
onChange={(e) => setEditFormData({ ...editFormData, address: e.target.value })}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-description">{t('admin.companies.fields.description')}</Label>
<Input
id="edit-description"
value={editFormData.description}
onChange={(e) => setEditFormData({ ...editFormData, description: e.target.value })}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-logoUrl">Logo URL</Label>
<Input
id="edit-logoUrl"
value={editFormData.logoUrl}
onChange={(e) => setEditFormData({ ...editFormData, logoUrl: e.target.value })}
placeholder="https://example.com/logo.png"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-yearsInMarket">Anos no mercado</Label>
<Input
id="edit-yearsInMarket"
value={editFormData.yearsInMarket}
onChange={(e) => setEditFormData({ ...editFormData, yearsInMarket: e.target.value })}
placeholder="Ex: 10"
/>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsEditDialogOpen(false)}>{t('admin.companies.create.cancel')}</Button>
<Button onClick={handleUpdate} disabled={updating}>
{updating && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
{t('admin.companies.edit.save')}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<ConfirmModal <ConfirmModal
isOpen={!!companyToDelete} isOpen={!!companyToDelete}
onClose={() => setCompanyToDelete(null)} onClose={() => setCompanyToDelete(null)}
onConfirm={confirmDelete} onConfirm={confirmDelete}
title={companyToDelete ? t('admin.companies.deleteConfirm', { name: companyToDelete.name }) : ""} title="Excluir Empresa"
description="This action cannot be undone." description={`Tem certeza que deseja excluir ${companyToDelete?.name}? Esta ação não pode ser desfeita.`}
/> />
</div > </div>
) )
} }