diff --git a/frontend/src/components/ErrorBoundary.jsx b/frontend/src/components/ErrorBoundary.jsx
index 39bbbdd..91bbe07 100644
--- a/frontend/src/components/ErrorBoundary.jsx
+++ b/frontend/src/components/ErrorBoundary.jsx
@@ -27,6 +27,35 @@ function makeErrorId() {
return `ERR-${ts}-${rand}`;
}
+// Best-effort, fire-and-forget crash report to the dedicated /api/analytics
+// sink. Self-contained (no page-chunk imports) so the root backstop never
+// depends on a module that may itself have failed to load. Wrapped so a
+// reporting failure can NEVER take the app down — this is observability only.
+function reportCrash({ errorId, error, errorInfo, scope }) {
+ try {
+ const payload = JSON.stringify({
+ event: 'react_error_boundary',
+ errorId,
+ scope: scope || undefined,
+ message: error?.toString?.() ?? 'unknown',
+ componentStack: errorInfo?.componentStack ?? undefined,
+ path: typeof window !== 'undefined' ? window.location.pathname : undefined,
+ });
+ if (typeof navigator !== 'undefined' && navigator.sendBeacon) {
+ navigator.sendBeacon('/api/analytics', new Blob([payload], { type: 'application/json' }));
+ } else if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
+ window.fetch('/api/analytics', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: payload,
+ keepalive: true,
+ }).catch(() => {});
+ }
+ } catch {
+ // Crash reporting must never interrupt the fallback UI.
+ }
+}
+
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
@@ -51,6 +80,9 @@ class ErrorBoundary extends React.Component {
// Always log to console so it's visible in DevTools / Cloud Run logs.
console.error(`[ErrorBoundary ${errorId}]`, error, errorInfo);
+ // Best-effort crash report to /api/analytics. Self-wrapped; never throws.
+ reportCrash({ errorId, error, errorInfo, scope: this.props.scope });
+
// Call the global Sentry hook if observability layer is attached.
if (typeof window !== 'undefined' && typeof window.__SENTRY_HOOK__ === 'function') {
try {
diff --git a/frontend/src/pages/AdStudio.jsx b/frontend/src/pages/AdStudio.jsx
index 04307c6..d0cc00c 100644
--- a/frontend/src/pages/AdStudio.jsx
+++ b/frontend/src/pages/AdStudio.jsx
@@ -996,25 +996,31 @@ export default function AdStudio({ onBack }) {
Create AI voiceover audio from your script (Google Cloud TTS)
{/* Voice selector - grouped by tier */}
-
- Voice:
-
-
-
+ {voices.length === 0 ? (
+
+ No voiceover voices are available right now. Check your Google Cloud TTS configuration and retry.
+
+ ) : (
+
+ Voice:
+
+
+ )}
+
{/* Generate button */}