diff --git a/management-dashboard-web-app/src/components/ErrorBoundary.tsx b/management-dashboard-web-app/src/components/ErrorBoundary.tsx index 059890c..88ccc07 100644 --- a/management-dashboard-web-app/src/components/ErrorBoundary.tsx +++ b/management-dashboard-web-app/src/components/ErrorBoundary.tsx @@ -5,20 +5,61 @@ type Props = { fallback?: ReactNode onRetry?: () => void showRetry?: boolean + autoRetry?: boolean + retryDelay?: number + maxRetries?: number } -type State = { hasError: boolean } +type State = { hasError: boolean; retryCount: number } export class ErrorBoundary extends Component { - state: State = { hasError: false } + private retryTimeoutId?: NodeJS.Timeout + + state: State = { hasError: false, retryCount: 0 } static getDerivedStateFromError() { return { hasError: true } } - componentDidCatch() {} + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + // Auto-retry logic for module federation loading issues + const maxRetries = this.props.maxRetries || 3 + if (this.props.autoRetry !== false && this.state.retryCount < maxRetries) { + const delay = this.props.retryDelay || 2000 + this.retryTimeoutId = setTimeout(() => { + this.setState(prevState => ({ + hasError: false, + retryCount: prevState.retryCount + 1 + })) + if (this.props.onRetry) { + this.props.onRetry() + } + }, delay) + } + } + + componentDidUpdate(prevProps: Props, prevState: State) { + // Reset retry count if error is cleared and component successfully rendered + if (prevState.hasError && !this.state.hasError && this.state.retryCount > 0) { + // Give it a moment to see if it stays error-free + setTimeout(() => { + if (!this.state.hasError) { + this.setState({ retryCount: 0 }) + } + }, 1000) + } + } + + componentWillUnmount() { + if (this.retryTimeoutId) { + clearTimeout(this.retryTimeoutId) + } + } handleRetry = () => { - this.setState({ hasError: false }) + if (this.retryTimeoutId) { + clearTimeout(this.retryTimeoutId) + } + this.setState({ hasError: false, retryCount: 0 }) if (this.props.onRetry) { this.props.onRetry() } @@ -43,6 +84,11 @@ export class ErrorBoundary extends Component {

Something went wrong loading this section

An error occurred while loading this component. Please try reloading it.

+ {this.props.autoRetry !== false && this.state.retryCount < (this.props.maxRetries || 3) && ( +

+ Retrying automatically... (Attempt {this.state.retryCount + 1} of {(this.props.maxRetries || 3) + 1}) +

+ )}
{(this.props.showRetry !== false) && (
diff --git a/scheduling-remote/package.json b/scheduling-remote/package.json index 4d550a8..ba2c731 100644 --- a/scheduling-remote/package.json +++ b/scheduling-remote/package.json @@ -9,7 +9,7 @@ "build:watch": "vite build --watch", "serve:dist": "serve -s dist -l 3003", "preview": "vite preview --port 3003", - "dev:watch": "npm run build && (npm run build:watch &) && sleep 1 && npx http-server dist -p 3003 --cors -c-1" + "dev:watch": "./wait-and-serve.sh" }, "dependencies": { "@supabase/supabase-js": "^2.52.0", diff --git a/scheduling-remote/wait-and-serve.sh b/scheduling-remote/wait-and-serve.sh new file mode 100755 index 0000000..adfdce0 --- /dev/null +++ b/scheduling-remote/wait-and-serve.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +# Build the project first +echo "Building scheduling-remote..." +npm run build + +# Verify the initial build created remoteEntry.js +REMOTE_ENTRY_PATH="dist/assets/remoteEntry.js" +if [ ! -f "$REMOTE_ENTRY_PATH" ]; then + echo "ERROR: Initial build did not create remoteEntry.js!" + exit 1 +fi + +echo "Initial build complete. remoteEntry.js exists." + +# Start build:watch in the background +echo "Starting build:watch in background..." +npm run build:watch & +BUILD_WATCH_PID=$! + +# Wait a moment for build:watch to start and potentially rebuild +echo "Waiting for build:watch to stabilize..." +sleep 3 + +# Verify remoteEntry.js still exists (build:watch might have rebuilt it) +MAX_WAIT=30 +WAIT_COUNT=0 +while [ ! -f "$REMOTE_ENTRY_PATH" ] && [ $WAIT_COUNT -lt $MAX_WAIT ]; do + sleep 1 + WAIT_COUNT=$((WAIT_COUNT + 1)) + if [ $((WAIT_COUNT % 5)) -eq 0 ]; then + echo "Waiting for remoteEntry.js after build:watch... (${WAIT_COUNT}s)" + fi +done + +if [ ! -f "$REMOTE_ENTRY_PATH" ]; then + echo "ERROR: remoteEntry.js was not available after ${MAX_WAIT} seconds!" + kill $BUILD_WATCH_PID 2>/dev/null || true + exit 1 +fi + +# Wait a bit more to ensure build:watch has finished any initial rebuild +echo "Ensuring build:watch has completed initial build..." +sleep 2 + +# Check file size to ensure it's not empty or being written +FILE_SIZE=$(stat -f%z "$REMOTE_ENTRY_PATH" 2>/dev/null || stat -c%s "$REMOTE_ENTRY_PATH" 2>/dev/null || echo "0") +if [ "$FILE_SIZE" -lt 100 ]; then + echo "WARNING: remoteEntry.js seems too small (${FILE_SIZE} bytes), waiting a bit more..." + sleep 2 +fi + +echo "remoteEntry.js is ready (${FILE_SIZE} bytes). Starting http-server..." + +# Start http-server and give it time to fully initialize +# Use a simple approach: start server and wait a moment for it to be ready +exec npx http-server dist -p 3003 --cors -c-1