Add descriptive error messages across all API routes and toast notifications on client pages
Build & Auto-Repair / build (push) Has been cancelled

- 86 catch blocks in 49 API route files now return the actual error message via error.message
- 14 campaign/lead/config catch blocks that lacked console.error now log errors
- 17 client pages/components now show toast.error notifications on API failures
- Silent .catch(() => {}) and finally-only try blocks now surface errors to users
This commit is contained in:
2026-07-01 15:20:30 +02:00
parent 727dcddf37
commit 3fe32d923e
76 changed files with 344 additions and 155 deletions
+16 -6
View File
@@ -15,6 +15,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { toast } from "sonner"
export default function ProposalDetailPage() {
const { id } = useParams<{ id: string }>()
@@ -35,6 +36,7 @@ export default function ProposalDetailPage() {
setProposal(await pRes.json())
setStatuses(await sRes.json())
} catch (e) {
toast.error("Failed to load proposal")
console.error("Failed to load proposal", e)
} finally {
setLoading(false)
@@ -44,12 +46,17 @@ export default function ProposalDetailPage() {
useEffect(() => { fetchProposal() }, [fetchProposal])
const updateStatus = async (statusId: string) => {
await fetch(`/api/proposals/${id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ statusId }),
})
fetchProposal()
try {
await fetch(`/api/proposals/${id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ statusId }),
})
fetchProposal()
} catch (e) {
console.error("Failed to update proposal status", e)
toast.error("Failed to update proposal status")
}
}
const handleSign = async () => {
@@ -62,6 +69,9 @@ export default function ProposalDetailPage() {
body: JSON.stringify({ name: signName, email: signEmail }),
})
if (res.ok) fetchProposal()
} catch (e) {
console.error("Failed to sign proposal", e)
toast.error("Failed to sign proposal")
} finally {
setSigning(false)
}
+4 -2
View File
@@ -14,6 +14,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { toast } from "sonner"
interface LineItem {
id: string
@@ -39,8 +40,8 @@ export default function NewProposalPage() {
const [saving, setSaving] = useState(false)
useEffect(() => {
fetch("/api/leads?limit=100").then((r) => r.json()).then(setLeads).catch(() => {})
fetch("/api/customers?limit=100").then((r) => r.json()).then(setCustomers).catch(() => {})
fetch("/api/leads?limit=100").then((r) => r.json()).then(setLeads).catch((e) => { console.error("Failed to load leads", e); toast.error("Failed to load leads") })
fetch("/api/customers?limit=100").then((r) => r.json()).then(setCustomers).catch((e) => { console.error("Failed to load customers", e); toast.error("Failed to load customers") })
}, [])
const addItem = () => {
@@ -107,6 +108,7 @@ export default function NewProposalPage() {
router.push(`/proposals/${data.id}`)
} catch (e) {
toast.error("Failed to create proposal")
console.error("Failed to create proposal", e)
} finally {
setSaving(false)
+3 -2
View File
@@ -15,6 +15,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { toast } from "sonner"
export default function ProposalsPage() {
const router = useRouter()
@@ -31,12 +32,12 @@ export default function ProposalsPage() {
fetch(`/api/proposals?${params}`)
.then((r) => r.json())
.then(setProposals)
.catch(() => {})
.catch((e) => { console.error("Failed to load proposals", e); toast.error("Failed to load proposals") })
.finally(() => setLoading(false))
}, [search, statusFilter])
useEffect(() => {
fetch("/api/proposal-statuses").then((r) => r.json()).then(setStatuses).catch(() => {})
fetch("/api/proposal-statuses").then((r) => r.json()).then(setStatuses).catch((e) => { console.error("Failed to load proposal statuses", e); toast.error("Failed to load proposal statuses") })
}, [])
const formatCurrency = (n: number) => n ? `R${Number(n).toLocaleString()}` : "R0"