From 538704b62069150b3aa2e2f9467419148ed8f191 Mon Sep 17 00:00:00 2001
From: Samed <sbalcioglu@graphpolaris.com>
Date: Tue, 12 Nov 2024 11:35:20 +0000
Subject: [PATCH] feat: stack trace interface in error boundaries

---
 .../errorBoundary/ErrorBoundary.tsx           | 46 +++++++++++++++++--
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/libs/shared/lib/components/errorBoundary/ErrorBoundary.tsx b/libs/shared/lib/components/errorBoundary/ErrorBoundary.tsx
index 14908635c..e3169344e 100644
--- a/libs/shared/lib/components/errorBoundary/ErrorBoundary.tsx
+++ b/libs/shared/lib/components/errorBoundary/ErrorBoundary.tsx
@@ -1,20 +1,27 @@
 import React, { Component, ErrorInfo, ReactNode } from 'react';
 import { Button } from '../buttons';
+import { useAppDispatch } from '@graphpolaris/shared/lib/data-access';
+import { addInfo } from '@graphpolaris/shared/lib/data-access/store/configSlice';
 
 interface ErrorBoundaryProps {
   children: ReactNode;
   fallback?: ReactNode;
   onError?: (error: Error, errorInfo: ErrorInfo) => void;
+  dispatch?: ReturnType<typeof useAppDispatch>;
 }
 
 interface ErrorBoundaryState {
   hasError: boolean;
+  error?: Error;
+  errorInfo?: ErrorInfo;
 }
 
 class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
   constructor(props: ErrorBoundaryProps) {
     super(props);
     this.state = { hasError: false };
+    this.handleRetry = this.handleRetry.bind(this);
+    this.handleCopyError = this.handleCopyError.bind(this);
   }
 
   static getDerivedStateFromError(): ErrorBoundaryState {
@@ -22,20 +29,48 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
   }
 
   componentDidCatch(error: Error, errorInfo: ErrorInfo) {
+    this.setState({ error, errorInfo });
     if (this.props.onError) {
       this.props.onError(error, errorInfo);
     }
   }
 
   handleRetry() {
-    this.setState({ hasError: false });
+    this.setState({ hasError: false, error: undefined, errorInfo: undefined });
+  }
+
+  handleCopyError() {
+    const errorText = `Error: ${this.state.error?.toString()}\n\nStack trace: ${this.state.errorInfo?.componentStack}`;
+    navigator.clipboard.writeText(errorText);
+    if (this.props.dispatch) {
+      this.props.dispatch(addInfo('Stack trace copied to clipboard'));
+    }
   }
 
   render() {
     if (this.state.hasError) {
       return (
-        <div>
+        <div className="flex flex-col w-full h-full">
           {this.props.fallback || <div>Something went wrong. Please try again later.</div>}
+          {import.meta.env.GRAPHPOLARIS_VERSION === 'dev' && (
+            <div className="overflow-auto max-h-[500px] p-2">
+              <div className="flex justify-end mb-2">
+                <Button 
+                  label="Copy Stack Trace" 
+                  variant="outline" 
+                  variantType="secondary" 
+                  size="xs"
+                  onClick={this.handleCopyError} 
+                />
+              </div>
+              <pre className="whitespace-pre-wrap break-words text-sm">
+                {this.state.error?.toString()}
+              </pre>
+              <pre className="whitespace-pre-wrap break-words text-sm mt-2">
+                {this.state.errorInfo?.componentStack}
+              </pre>
+            </div>
+          )}
           <Button label="Retry now" variant="outline" variantType="primary" onClick={this.handleRetry} />
         </div>
       );
@@ -45,4 +80,9 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
   }
 }
 
-export { ErrorBoundary };
+function ErrorBoundaryWithDispatch(props: Omit<ErrorBoundaryProps, 'dispatch'>) {
+  const dispatch = useAppDispatch();
+  return <ErrorBoundary {...props} dispatch={dispatch} />;
+}
+
+export { ErrorBoundaryWithDispatch as ErrorBoundary };
\ No newline at end of file
-- 
GitLab