frontend_nyla/docs/PAGEMANAGER_SYSTEM_DOCUMENTATION.md

9.2 KiB

PageManager System Documentation

Status: Production Ready - All critical issues resolved
📖 New to PageManager? See USAGE_GUIDE.md for step-by-step instructions on creating new pages

Overview

The PageManager is a declarative, data-driven page rendering system that manages routing, navigation, and page lifecycle through configuration objects instead of hardcoded components.

Architecture: Page Definition → PageManager (instances) → PageRenderer (hooks) → FormGenerator (table) → Action Buttons


Core Concepts

Hook Factory Pattern

Pages define data hooks using a factory pattern to ensure React rules compliance:

const createFilesHook = () => {
    return () => {
        // Call hooks at component level
        const { data, loading, error, refetch, removeFileOptimistically } = useUserFiles();
        const { handleFileDownload, handleFileDelete, handleFilePreview, handleFileUpdate, 
                downloadingFiles, deletingFiles, previewingFiles, editingFiles } = useFileOperations();
        
        // Return unified interface (hookData)
        return { data, loading, error, refetch, removeFileOptimistically,
                 handleDownload, handleDelete, handlePreview, handleUpload, handleFileUpdate,
                 downloadingFiles, deletingFiles, previewingFiles, editingFiles };
    };
};

Why?

  • Allows PageRenderer to call hooks at component level
  • Creates stable hook instance via useMemo
  • Single source of truth for all operations

Page Configuration

Pages are defined as data objects in src/core/PageManager/data/pages/:

export const dateienPageData: GenericPageData = {
    id: 'verwaltung-dateien',
    path: 'verwaltung/dateien',
    title: 'Dateien',
    icon: FaRegFileAlt,
    
    headerButtons: [
        { id: 'upload-file', label: 'Upload File', icon: FaUpload, variant: 'primary' }
    ],
    
    content: [{
        type: 'table',
        tableConfig: {
            hookFactory: createFilesHook,
            columns: filesColumns,
            actionButtons: [
                { type: 'view', operationName: 'handlePreview', loadingStateName: 'previewingFiles' },
                { type: 'edit', operationName: 'handleFileUpdate', loadingStateName: 'editingFiles' },
                { type: 'download', operationName: 'handleDownload', loadingStateName: 'downloadingFiles' },
                { type: 'delete', operationName: 'handleDelete', loadingStateName: 'deletingFiles' }
            ]
        }
    }],
    
    privilegeChecker: privilegeCheckers.viewerRole,
    preserveState: false
};

Data Flow

State Management

PageRenderer (calls hookFactory)
    ↓
hookData = { data, operations, loadingStates, refetch }
    ↓
FormGenerator (receives hookData)
    ↓
Action Buttons (use hookData operations)
    ↓
API Calls (via operations)
    ↓
refetch() updates data
    ↓
FormGenerator re-renders

Key Point: Single source of truth - all components use the same hook instance via hookData.

Component Responsibilities

Component Responsibility State
PageManager Instance lifecycle, routing Page instances map
PageRenderer Execute hooks, render structure None (passes hookData down)
FormGenerator Table UI (search, sort, filter, pagination) Local UI state only
Action Buttons Trigger operations from hookData Internal loading flags
Popup/EditForm Presentational UI Local form state only

Action Buttons Deep Dive

All action buttons follow the same pattern:

  1. Receive hookData as required prop (no fallback hooks)
  2. Extract operation: const handleOp = hookData[operationName]
  3. Extract loading state: const loading = hookData[loadingStateName]
  4. Validate operations exist (throw error if missing)
  5. Call operation, show loading indicator, handle result

Upload Button

Trigger: User selects file
Flow: Upload → refetch() → table updates
Memoized: Uses useCallback([refetch])

View Button

Trigger: User clicks eye icon
Flow: Opens FilePreview → fetches preview data → displays
Refetch: Not needed (read-only)

Edit Button

Trigger: User clicks edit icon
Flow: Opens Popup → EditForm → Save → handleFileUpdate() → refetch() → table updates
Components: EditActionButton → Popup (presentational) → EditForm (presentational)
State: Local form state in EditForm, operations via hookData

Download Button

Trigger: User clicks download icon
Flow: Fetch blob → trigger browser download
Refetch: Not needed (read-only)

Delete Button

Trigger: User confirms delete
Flow: removeFileOptimistically() → handleFileDelete() → refetch() (on success/failure)
Optimistic Update: Instant UI feedback, rollback on error


Request Management

Caching (useApi.ts)

  • GET requests cached for 5 seconds
  • Cache key: ${method}:${url}:${params}
  • Prevents duplicate simultaneous requests
  • Cleared on error or timeout

CSRF & Auth

  • CSRF token: Auto-added via addCSRFTokenToHeaders()
  • JWT token: Auto-added by axios interceptor
  • Handled transparently by api instance

Critical Issues Fixed

1. Hook Duplication in Action Buttons

Problem: DeleteActionButton and EditActionButton called useFileOperations() and useUserFiles() unconditionally as fallbacks, creating duplicate hook instances with separate state.

Fix:

  • Made hookData required (not optional)
  • Removed all fallback hook imports and calls
  • Added validation: throw error if operations missing
  • All buttons now use single shared state from hookData

2. Missing Edit Operations

Problem: handleFileUpdate and editingFiles not included in hookData

Fix:

  • Added to hook factory destructuring and return statement
  • Added operationName and loadingStateName to button config

3. Upload Function Not Memoized

Problem: handleFileUpload recreated every render

Fix: Wrapped with useCallback([refetch])

Result

No duplicate hooks
Single source of truth
Consistent state across all components
Better performance


Page Lifecycle

Navigation Flow

1. User navigates to /verwaltung/dateien
2. PageManager.useEffect triggered
3. getPageDataByPath('verwaltung/dateien')
4. Check privilegeChecker
5. Create PageInstance (or reuse if preserveState: true)
6. PageRenderer calls hookFactory() → useTableData
7. Hooks execute: useUserFiles(), useFileOperations()
8. API call: /api/files/list
9. setFiles(data) updates state
10. FormGenerator renders table
11. Action buttons render per row

Cleanup

preserveState: false (default):

  • Component unmounted after 500ms
  • All state lost
  • Next visit: Full reload

preserveState: true:

  • Component stays mounted (hidden)
  • State preserved
  • Next visit: Instant

Best Practices

Do

  • Use hook factory pattern for data fetching
  • Pass hookData to all action buttons
  • Make hookData required (not optional)
  • Use useCallback for functions inside hooks
  • Implement optimistic updates for better UX
  • Use per-item loading states (Set)
  • Keep presentational components stateless (Popup, EditForm)

Don't

  • Call hooks conditionally or in loops
  • Create fallback hooks in action buttons
  • Duplicate state across components
  • Call operations directly without hookData
  • Mutate hookData (it's a shared reference)

Troubleshooting

"hookData.X is not defined"

Cause: Operation not included in hook factory return statement
Fix: Add operation to hook factory's return object

Hook duplication / inconsistent state

Cause: Action button calling hooks directly instead of using hookData
Fix: Remove fallback hooks, make hookData required, use hookData operations

Backend 500 errors

Cause: Backend issue (e.g., "'str' object has no attribute 'name'")
Fix: Check backend logs for stack trace - not a frontend issue


Summary

Architecture Quality: A- (Excellent)

Strengths:

  • Declarative page configuration
  • Separation of concerns (data/logic/UI)
  • Reusable components (FormGenerator, ActionButtons)
  • Optimistic updates for better UX
  • Single source of truth for state
  • Hook factory pattern follows React rules
  • All critical issues resolved

Remaining Improvements

  1. Global error handling (Priority: High) - Add toast notification system
  2. TypeScript strict mode (Priority: Medium) - Remove any types, proper hookData interface
  3. Unit tests (Priority: Medium) - Test hook factory, optimistic updates, error recovery
  4. Performance (Priority: Low) - Virtual scrolling, pagination caching, React.memo

Status: 🟢 Production Ready

Critical issues have been resolved. The system is fully functional with clean architecture. Remaining improvements are nice-to-haves that would enhance UX and maintainability.


Next Steps

📖 Ready to create a new page? Check out the USAGE_GUIDE.md for:

  • Step-by-step instructions
  • Complete code examples
  • Advanced features
  • Best practices
  • Troubleshooting tips