From fd2fa328ad7f7ab3f0d77b6b95920f6a2afa5697 Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Mon, 23 Feb 2026 11:24:55 -0600 Subject: [PATCH] chore: replace native window.confirm with Custom ConfirmModal --- .../dashboard/admin/email-templates/page.tsx | 19 +++++++++-- .../src/app/dashboard/applications/page.tsx | 33 ++++++++++++++----- .../src/app/dashboard/backoffice/page.tsx | 21 ++++++++++-- frontend/src/app/dashboard/companies/page.tsx | 26 ++++++++++++--- frontend/src/app/dashboard/jobs/page.tsx | 24 +++++++++++--- 5 files changed, 101 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/dashboard/admin/email-templates/page.tsx b/frontend/src/app/dashboard/admin/email-templates/page.tsx index 76b86c4..98a08e8 100644 --- a/frontend/src/app/dashboard/admin/email-templates/page.tsx +++ b/frontend/src/app/dashboard/admin/email-templates/page.tsx @@ -4,6 +4,7 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import { toast } from "sonner"; import { emailTemplatesApi, EmailTemplate } from "@/lib/api"; +import { ConfirmModal } from "@/components/confirm-modal"; export default function EmailTemplatesPage() { const router = useRouter(); @@ -11,6 +12,7 @@ export default function EmailTemplatesPage() { const [loading, setLoading] = useState(true); const [showCreate, setShowCreate] = useState(false); const [newTemplate, setNewTemplate] = useState({ slug: "", subject: "", body_html: "", variables: "" }); + const [deleteTemplateSlug, setDeleteTemplateSlug] = useState(null); useEffect(() => { fetchTemplates(); @@ -50,13 +52,19 @@ export default function EmailTemplatesPage() { }; const handleDelete = async (slug: string) => { - if (!confirm("Delete this template?")) return; + setDeleteTemplateSlug(slug); + }; + + const confirmDelete = async () => { + if (!deleteTemplateSlug) return; try { - await emailTemplatesApi.delete(slug); + await emailTemplatesApi.delete(deleteTemplateSlug); toast.success("Template deleted"); fetchTemplates(); } catch (err: any) { toast.error(err.message || "Failed to delete template"); + } finally { + setDeleteTemplateSlug(null); } }; @@ -178,6 +186,13 @@ export default function EmailTemplatesPage() { + setDeleteTemplateSlug(null)} + onConfirm={confirmDelete} + title="Are you sure you want to delete this template?" + description="This action cannot be undone." + /> ); } diff --git a/frontend/src/app/dashboard/applications/page.tsx b/frontend/src/app/dashboard/applications/page.tsx index c6e4059..0cb8635 100644 --- a/frontend/src/app/dashboard/applications/page.tsx +++ b/frontend/src/app/dashboard/applications/page.tsx @@ -37,13 +37,14 @@ import { } from "lucide-react" import { applicationsApi, notificationsApi } from "@/lib/api" import { toast } from "sonner" +import { ConfirmModal } from "@/components/confirm-modal" const statusConfig = { - pending: { label: "Pendente", color: "bg-yellow-100 text-yellow-800 border-yellow-200", icon: Clock }, - reviewed: { label: "Em análise", color: "bg-blue-100 text-blue-800 border-blue-200", icon: Eye }, + pending: { label: "Pendente", color: "bg-yellow-100 text-yellow-800 border-yellow-200", icon: Clock }, + reviewed: { label: "Em análise", color: "bg-blue-100 text-blue-800 border-blue-200", icon: Eye }, shortlisted: { label: "Selecionado", color: "bg-purple-100 text-purple-800 border-purple-200", icon: Star }, - hired: { label: "Contratado", color: "bg-green-100 text-green-800 border-green-200", icon: Check }, - rejected: { label: "Rejeitado", color: "bg-red-100 text-red-800 border-red-200", icon: X }, + hired: { label: "Contratado", color: "bg-green-100 text-green-800 border-green-200", icon: Check }, + rejected: { label: "Rejeitado", color: "bg-red-100 text-red-800 border-red-200", icon: X }, } interface Application { @@ -64,6 +65,7 @@ export default function ApplicationsPage() { const [statusFilter, setStatusFilter] = useState("all") const [searchTerm, setSearchTerm] = useState("") const [selectedApp, setSelectedApp] = useState(null) + const [deleteConfirmId, setDeleteConfirmId] = useState(null) useEffect(() => { async function fetchApplications() { @@ -81,16 +83,22 @@ export default function ApplicationsPage() { const handleDelete = async (id: string, e?: React.MouseEvent) => { e?.stopPropagation() - if (!confirm("Are you sure you want to delete this application?")) return + setDeleteConfirmId(id) + } + + const confirmDelete = async () => { + if (!deleteConfirmId) return try { - await applicationsApi.delete(id) - setApplications(applications.filter(a => a.id !== id)) + await applicationsApi.delete(deleteConfirmId) + setApplications(applications.filter(a => a.id !== deleteConfirmId)) toast.success("Application deleted") - if (selectedApp?.id === id) setSelectedApp(null) + if (selectedApp?.id === deleteConfirmId) setSelectedApp(null) } catch (error) { console.error("Delete error:", error) toast.error("Failed to delete application") + } finally { + setDeleteConfirmId(null) } } @@ -168,7 +176,7 @@ export default function ApplicationsPage() { - + All Status Pendente Em análise @@ -346,6 +354,13 @@ export default function ApplicationsPage() { ) } + setDeleteConfirmId(null)} + onConfirm={confirmDelete} + title="Are you sure you want to delete this application?" + description="This action cannot be undone." + /> ) } diff --git a/frontend/src/app/dashboard/backoffice/page.tsx b/frontend/src/app/dashboard/backoffice/page.tsx index 7abaa16..6da5db5 100644 --- a/frontend/src/app/dashboard/backoffice/page.tsx +++ b/frontend/src/app/dashboard/backoffice/page.tsx @@ -43,6 +43,7 @@ import { import { getCurrentUser, isAdminUser } from "@/lib/auth" import { toast } from "sonner" import { Archive, CheckCircle, Copy, ExternalLink, PauseCircle, Plus, RefreshCw, XCircle } from "lucide-react" +import { ConfirmModal } from "@/components/confirm-modal" const auditDateFormatter = new Intl.DateTimeFormat("pt-BR", { dateStyle: "short", @@ -94,6 +95,7 @@ export default function BackofficePage() { const [isPlanDialogOpen, setIsPlanDialogOpen] = useState(false) const [planForm, setPlanForm] = useState({ name: "", description: "", monthlyPrice: 0, yearlyPrice: 0, features: [] }) const [editingPlanId, setEditingPlanId] = useState(null) + const [deletePlanId, setDeletePlanId] = useState(null) const loadBackoffice = async (silent = false) => { try { @@ -223,13 +225,19 @@ export default function BackofficePage() { } const handleDeletePlan = async (id: string) => { - if (!confirm("Delete this plan?")) return + setDeletePlanId(id) + } + + const confirmDeletePlan = async () => { + if (!deletePlanId) return try { - await plansApi.delete(id) + await plansApi.delete(deletePlanId) toast.success("Plan deleted") loadBackoffice(true) } catch (error) { toast.error("Failed to delete plan") + } finally { + setDeletePlanId(null) } } @@ -534,6 +542,13 @@ export default function BackofficePage() { + setDeletePlanId(null)} + onConfirm={confirmDeletePlan} + title="Are you sure you want to delete this plan?" + description="This action cannot be undone." + /> ) -} +} \ No newline at end of file diff --git a/frontend/src/app/dashboard/companies/page.tsx b/frontend/src/app/dashboard/companies/page.tsx index e7f936a..064f2b2 100644 --- a/frontend/src/app/dashboard/companies/page.tsx +++ b/frontend/src/app/dashboard/companies/page.tsx @@ -23,6 +23,7 @@ import { Switch } from "@/components/ui/switch" import { adminCompaniesApi, type AdminCompany } from "@/lib/api" import { getCurrentUser, isAdminUser } from "@/lib/auth" import { toast } from "sonner" +import { ConfirmModal } from "@/components/confirm-modal" import { Skeleton } from "@/components/ui/skeleton" import { useTranslation } from "@/lib/i18n" @@ -89,6 +90,7 @@ export default function AdminCompaniesPage() { const [isDialogOpen, setIsDialogOpen] = useState(false) const [isViewDialogOpen, setIsViewDialogOpen] = useState(false) const [selectedCompany, setSelectedCompany] = useState(null) + const [companyToDelete, setCompanyToDelete] = useState(null) const [creating, setCreating] = useState(false) const [updating, setUpdating] = useState(false) const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) @@ -223,16 +225,24 @@ export default function AdminCompaniesPage() { } const handleDelete = async (company: AdminCompany) => { - if (!window.confirm(t('admin.companies.deleteConfirm', { name: company.name }))) return + setCompanyToDelete(company) + } + + const confirmDelete = async () => { + if (!companyToDelete) return try { - await adminCompaniesApi.delete(company.id) + await adminCompaniesApi.delete(companyToDelete.id) toast.success(t('admin.companies.success.deleted')) - setIsViewDialogOpen(false) + if (selectedCompany?.id === companyToDelete.id) { + setIsViewDialogOpen(false) + } loadCompanies() } catch (error) { console.error("Error deleting company:", error) toast.error("Failed to delete company") + } finally { + setCompanyToDelete(null) } } @@ -888,6 +898,14 @@ export default function AdminCompaniesPage() { - + + setCompanyToDelete(null)} + onConfirm={confirmDelete} + title={companyToDelete ? t('admin.companies.deleteConfirm', { name: companyToDelete.name }) : ""} + description="This action cannot be undone." + /> + ) } diff --git a/frontend/src/app/dashboard/jobs/page.tsx b/frontend/src/app/dashboard/jobs/page.tsx index f63b0ac..094a713 100644 --- a/frontend/src/app/dashboard/jobs/page.tsx +++ b/frontend/src/app/dashboard/jobs/page.tsx @@ -22,6 +22,7 @@ import { Switch } from "@/components/ui/switch" import { Plus, Search, Edit, Trash2, Eye, ChevronLeft, ChevronRight, Loader2 } from "lucide-react" import { adminJobsApi, jobsApi, type AdminJob } from "@/lib/api" import { useTranslation } from "@/lib/i18n" +import { ConfirmModal } from "@/components/confirm-modal" type EditForm = { title: string @@ -70,6 +71,7 @@ export default function AdminJobsPage() { const [isLoading, setIsLoading] = useState(true) const [isSaving, setIsSaving] = useState(false) const [errorMessage, setErrorMessage] = useState(null) + const [deleteConfirmDialog, setDeleteConfirmDialog] = useState<{ isOpen: boolean, jobId: string | null }>({ isOpen: false, jobId: null }) const [page, setPage] = useState(1) const [limit] = useState(10) @@ -144,12 +146,18 @@ export default function AdminJobsPage() { } const handleDeleteJob = async (id: string) => { - if (!confirm(t('admin.jobs.deleteConfirm'))) return + setDeleteConfirmDialog({ isOpen: true, jobId: id }) + } + + const confirmDeleteJob = async () => { + if (!deleteConfirmDialog.jobId) return try { - await jobsApi.delete(id) - setJobs((prev) => prev.filter((job) => job.id !== id)) + await jobsApi.delete(deleteConfirmDialog.jobId) + setJobs((prev) => prev.filter((job) => job.id !== deleteConfirmDialog.jobId)) } catch { alert(t('admin.jobs.deleteError')) + } finally { + setDeleteConfirmDialog({ isOpen: false, jobId: null }) } } @@ -208,6 +216,14 @@ export default function AdminJobsPage() { + setDeleteConfirmDialog({ isOpen: false, jobId: null })} + onConfirm={confirmDeleteJob} + title={t('admin.jobs.deleteConfirm')} + description="Esta ação não pode ser desfeita e irá excluir a vaga permanentemente." + /> + {/* View Job Dialog */} @@ -363,7 +379,7 @@ export default function AdminJobsPage() {