Skip to content

[Frontend] Add Route Guards and Authentication Checks for All Protected Pages #157

Description

@devJaja

🎯 Objective

Create a centralized toast/notification system for the frontend to replace the inconsistent inline error/success message patterns scattered across components.


📁 Files to Create

Action File Path Description
Create frontend/src/context/ToastContext.tsx Toast provider with queue management
Create frontend/src/hooks/useToast.ts Hook for components to trigger toasts
Create frontend/src/components/common/Toast.tsx Toast UI component with animations
Create frontend/src/components/common/Toast.css Toast styles and animations

📁 Files to Modify

Action File Path Description
Modify frontend/src/App.tsx Wrap app with ToastProvider
Modify frontend/src/components/agents/TaskSubmissionForm.tsx Replace inline error div with useToast
Modify frontend/src/pages/dashboard.tsx Add error toasts for failed data fetches
Modify frontend/src/components/dashboard/RecentTasksTable.tsx Replace silent console.error with toast
Modify frontend/src/services/api.ts Trigger toast on 503 retry and 401 disconnect

🔍 Current Inconsistent Patterns

Component Current Pattern Problem
TaskSubmissionForm.tsx Fixed-position inline error div Duplicated per component
DashboardPage.tsx console.error() only No user feedback
RecentTasksTable.tsx setTasks([]) + console.error Silent failure
useTaskMonitor.ts Falls back to mock data Hides real errors
api.ts 503 retry with no user feedback User unaware of retries

✅ Expected Implementation

1. Create ToastContext.tsx

import { createContext, useContext, useState, useCallback } from 'react';

type ToastType = 'success' | 'error' | 'warning' | 'info';

interface Toast {
  id: string;
  message: string;
  type: ToastType;
  duration?: number;
}

interface ToastContextValue {
  toasts: Toast[];
  showToast: (message: string, type?: ToastType, duration?: number) => void;
  dismissToast: (id: string) => void;
}

const ToastContext = createContext<ToastContextValue | null>(null);

export function ToastProvider({ children }: { children: React.ReactNode }) {
  const [toasts, setToasts] = useState<Toast[]>([]);

  const showToast = useCallback((message: string, type: ToastType = 'info', duration = 5000) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(prev => [...prev, { id, message, type, duration }]);

    if (duration > 0) {
      setTimeout(() => dismissToast(id), duration);
    }
  }, []);

  const dismissToast = useCallback((id: string) => {
    setToasts(prev => prev.filter(t => t.id !== id));
  }, []);

  return (
    <ToastContext.Provider value={{ toasts, showToast, dismissToast }}>
      {children}
      <ToastContainer toasts={toasts} onDismiss={dismissToast} />
    </ToastContext.Provider>
  );
}

export function useToast() {
  const context = useContext(ToastContext);
  if (!context) throw new Error('useToast must be used within ToastProvider');
  return context;
}

2. Create Toast.tsx

function ToastContainer({ toasts, onDismiss }: { toasts: Toast[]; onDismiss: (id: string) => void }) {
  return (
    <div className="toast-container" role="alert" aria-live="polite">
      {toasts.map(toast => (
        <div key={toast.id} className={`toast toast-${toast.type}`}>
          <span>{toast.message}</span>
          <button onClick={() => onDismiss(toast.id)} aria-label="Dismiss">×</button>
        </div>
      ))}
    </div>
  );
}

3. Update App.tsx

import { ToastProvider } from './context/ToastContext';

function App() {
  return (
    <WalletProvider>
      <ToastProvider>
        <ErrorBoundary>
          <Router>{/* routes */}</Router>
        </ErrorBoundary>
      </ToastProvider>
    </WalletProvider>
  );
}

4. Replace Inline Error in TaskSubmissionForm.tsx

// BEFORE
{error && <div className="error-banner">{error}</div>}

// AFTER
const { showToast } = useToast();
// In submit handler:
if (error) showToast(error, 'error');

📁 Reference Files

File Path Purpose
frontend/src/App.tsx Wrap with ToastProvider
frontend/src/components/agents/TaskSubmissionForm.tsx Replace inline error
frontend/src/pages/dashboard.tsx Add toasts for fetch errors
frontend/src/components/dashboard/RecentTasksTable.tsx Replace silent error
frontend/src/services/api.ts Add toast on 503/401
frontend/src/components/common/ErrorBoundary.tsx Reference for error UI

✅ Acceptance Criteria

  • Create ToastContext.tsx with showToast(message, type, duration)
  • Create Toast.tsx component with auto-dismiss and close button
  • Create Toast.css with slide-in animation and type-based colors
  • Wrap app with ToastProvider in App.tsx
  • Replace inline error in TaskSubmissionForm.tsx
  • Add toasts in RecentTasksTable.tsx for failed fetches
  • Add toasts in api.ts for 503 retry and 401 disconnect
  • Toasts stack vertically in bottom-right corner
  • Toasts auto-dismiss after 5s (success/info) or 10s (error/warning)
  • Test accessibility: toast has role="alert" and aria-live="polite"

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions