chore: replace native window.confirm with Custom ConfirmModal

This commit is contained in:
Tiago Yamamoto 2026-02-23 11:24:55 -06:00
parent c5f6b1317e
commit fd2fa328ad
5 changed files with 101 additions and 22 deletions

View file

@ -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<string | null>(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() {
</tbody>
</table>
</div>
<ConfirmModal
isOpen={!!deleteTemplateSlug}
onClose={() => setDeleteTemplateSlug(null)}
onConfirm={confirmDelete}
title="Are you sure you want to delete this template?"
description="This action cannot be undone."
/>
</div>
);
}

View file

@ -37,6 +37,7 @@ 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 },
@ -64,6 +65,7 @@ export default function ApplicationsPage() {
const [statusFilter, setStatusFilter] = useState("all")
const [searchTerm, setSearchTerm] = useState("")
const [selectedApp, setSelectedApp] = useState<Application | null>(null)
const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(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)
}
}
@ -346,6 +354,13 @@ export default function ApplicationsPage() {
</div>
)
}
<ConfirmModal
isOpen={!!deleteConfirmId}
onClose={() => setDeleteConfirmId(null)}
onConfirm={confirmDelete}
title="Are you sure you want to delete this application?"
description="This action cannot be undone."
/>
</div >
)
}

View file

@ -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<any>({ name: "", description: "", monthlyPrice: 0, yearlyPrice: 0, features: [] })
const [editingPlanId, setEditingPlanId] = useState<string | null>(null)
const [deletePlanId, setDeletePlanId] = useState<string | null>(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() {
</Card>
</TabsContent>
</Tabs>
<ConfirmModal
isOpen={!!deletePlanId}
onClose={() => setDeletePlanId(null)}
onConfirm={confirmDeletePlan}
title="Are you sure you want to delete this plan?"
description="This action cannot be undone."
/>
</div>
)
}

View file

@ -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<AdminCompany | null>(null)
const [companyToDelete, setCompanyToDelete] = useState<AdminCompany | null>(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'))
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() {
</DialogFooter>
</DialogContent>
</Dialog>
<ConfirmModal
isOpen={!!companyToDelete}
onClose={() => setCompanyToDelete(null)}
onConfirm={confirmDelete}
title={companyToDelete ? t('admin.companies.deleteConfirm', { name: companyToDelete.name }) : ""}
description="This action cannot be undone."
/>
</div >
)
}

View file

@ -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<string | null>(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() {
</Link>
</div>
<ConfirmModal
isOpen={deleteConfirmDialog.isOpen}
onClose={() => 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 */}
<Dialog open={isViewDialogOpen} onOpenChange={setIsViewDialogOpen}>
<DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto">