Files
ERP/frontend/src/components/ErrorBoundary.tsx
T
MMD e17d1f0883 feat(monitoring): observability stack + app instrumentation
- /health/ready (DB+MinIO+Casdoor checks, webhook post-deploy gate),
  /metrics via prometheus-fastapi-instrumentator, Sentry/GlitchTip capture,
  X-Request-ID middleware + JSON access logging (PROCUREMENT_LOG_FORMAT)
- server-side analytics (OpenPanel Track API): login, quote lifecycle,
  receipts — fire-and-forget, disabled without env config
- monitoring/ compose project: GlitchTip(+pg), Prometheus(+alerts),
  Grafana(provisioned ERP dashboard), Loki+Promtail, node/postgres
  exporters, cAdvisor — mem-limited (~2.2GB), loopback admin UIs,
  separate from app deploys; erp_shared network for scraping
- frontend: @openpanel/web + @sentry/react (build-arg gated, no-op when
  unset), funnel events (login, wizard steps, quote submitted)
- compose: api healthcheck, observability env wiring, .env.example keys
2026-09-18 22:18:02 +03:30

52 lines
1.7 KiB
TypeScript

import { Component, type ErrorInfo, type ReactNode } from "react";
import { captureError } from "../lib/analytics";
interface Props {
children: ReactNode;
}
interface State {
error: Error | null;
}
// خطای رندر/لود ماژول نباید کل DOM را خالی کند — با این باکس، به‌جای صفحه‌ی
// سفید، پیام + دکمه‌ی «تلاش مجدد» (بارگذاری مجدد صفحه) نمایش داده می‌شود.
export class ErrorBoundary extends Component<Props, State> {
state: State = { error: null };
static getDerivedStateFromError(error: Error): State {
return { error };
}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error("ErrorBoundary caught:", error, info);
// GlitchTip/Sentry (no-op بدون VITE_SENTRY_DSN)
captureError(error);
}
private retry = () => {
this.setState({ error: null });
window.location.reload();
};
render() {
if (!this.state.error) return this.props.children;
return (
<div className="flex min-h-screen flex-col items-center justify-center gap-4 bg-bg px-6 text-center text-ink">
<div className="text-4xl">⚠️</div>
<h1 className="text-lg font-bold">خطایی در نمایش صفحه رخ داد</h1>
<p className="max-w-md text-sm text-muted">
{this.state.error.message || "مشکلی پیش آمد — لطفاً دوباره تلاش کنید."}
</p>
<button
type="button"
onClick={this.retry}
className="cursor-pointer rounded-lg bg-accent px-5 py-2.5 text-sm font-bold text-white transition-colors hover:bg-accent-dark"
>
تلاش مجدد
</button>
</div>
);
}
}