- /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
52 lines
1.7 KiB
TypeScript
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>
|
|
);
|
|
}
|
|
}
|