Files
Sankofa/docs/infrastructure/IMPLEMENTATION_STEPS.md
defiQUG 9daf1fd378 Apply Composer changes: comprehensive API updates, migrations, middleware, and infrastructure improvements
- Add comprehensive database migrations (001-024) for schema evolution
- Enhance API schema with expanded type definitions and resolvers
- Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth
- Implement new services: AI optimization, billing, blockchain, compliance, marketplace
- Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage)
- Update Crossplane provider with enhanced VM management capabilities
- Add comprehensive test suite for API endpoints and services
- Update frontend components with improved GraphQL subscriptions and real-time updates
- Enhance security configurations and headers (CSP, CORS, etc.)
- Update documentation and configuration files
- Add new CI/CD workflows and validation scripts
- Implement design system improvements and UI enhancements
2025-12-12 18:01:35 -08:00

28 KiB

Infrastructure Documentation Dashboard - Complete Implementation Steps

Overview

This document provides step-by-step implementation instructions for all enhancements, organized by phase and priority. Each step includes specific actions, file paths, code structure, and time estimates.


Phase 1: Critical Foundation (Weeks 1-2) - 80-120 hours

Step 1.1: Install Dependencies

Time: 15 minutes
Action: Add required packages to package.json

npm install html2canvas jspdf xlsx mapbox-gl react-map-gl @tanstack/react-virtual

Files to Modify:

  • package.json

Verification:

  • Run npm install
  • Verify packages in node_modules

Step 1.2: Create Validation Schemas

Time: 2-3 hours
File: src/lib/validation/schemas/infrastructure.ts

Implementation Steps:

  1. Import Zod

  2. Create countrySchema:

    export const countrySchema = z.object({
      name: z.string().min(1),
      region: z.enum(['Africa (Sub-Saharan)', 'Middle East & North Africa', 'Americas', 'Asia-Pacific', 'Europe']),
      relationshipType: z.enum(['Full Diplomatic Relations', 'Official (Non-Diplomatic)', 'Ambassador Level', 'Full Diplomatic Relations (Special Mission)']),
      priority: z.enum(['Critical', 'High', 'Medium', 'Low']),
      cloudflareCoverage: z.boolean(),
      networkInfrastructurePriority: z.string(),
      notes: z.string().optional(),
      coordinates: z.object({
        lat: z.number().min(-90).max(90),
        lng: z.number().min(-180).max(180)
      }).optional()
    })
    
  3. Create topologyNodeSchema:

    export const topologyNodeSchema = z.object({
      id: z.string(),
      type: z.enum(['region', 'datacenter', 'tunnel', 'vm', 'service']),
      label: z.string().min(1),
      region: z.string(),
      entity: z.string(),
      position: z.object({
        x: z.number(),
        y: z.number()
      }),
      metadata: z.record(z.any())
    })
    
  4. Create topologyEdgeSchema:

    export const topologyEdgeSchema = z.object({
      id: z.string(),
      source: z.string(),
      target: z.string(),
      type: z.enum(['tunnel', 'peering', 'network-route']),
      metadata: z.record(z.any())
    })
    
  5. Create networkTopologySchema:

    export const networkTopologySchema = z.object({
      nodes: z.array(topologyNodeSchema),
      edges: z.array(topologyEdgeSchema),
      region: z.string(),
      entity: z.string(),
      lastUpdated: z.string().datetime()
    })
    
  6. Create complianceRequirementSchema:

    export const complianceRequirementSchema = z.object({
      country: z.string().min(1),
      region: z.string(),
      frameworks: z.array(z.string()).min(1),
      status: z.enum(['Compliant', 'Partial', 'Pending', 'Non-Compliant']),
      requirements: z.array(z.string()),
      lastAuditDate: z.string().datetime().optional(),
      notes: z.string().optional()
    })
    
  7. Create deploymentMilestoneSchema:

    export const deploymentMilestoneSchema = z.object({
      id: z.string(),
      title: z.string().min(1),
      region: z.string(),
      entity: z.string(),
      priority: z.enum(['Critical', 'High', 'Medium', 'Low']),
      startDate: z.string().datetime(),
      endDate: z.string().datetime(),
      status: z.enum(['Planned', 'In Progress', 'Complete', 'Blocked']),
      dependencies: z.array(z.string()).optional(),
      cost: z.number().min(0).optional(),
      description: z.string().optional()
    }).refine((data) => new Date(data.endDate) > new Date(data.startDate), {
      message: "End date must be after start date",
      path: ["endDate"]
    })
    
  8. Create costEstimateSchema:

    export const costEstimateSchema = z.object({
      region: z.string(),
      entity: z.string(),
      category: z.enum(['Infrastructure', 'Network', 'Compliance', 'Operations']),
      monthly: z.number().min(0),
      annual: z.number().min(0),
      breakdown: z.object({
        compute: z.number().min(0).optional(),
        storage: z.number().min(0).optional(),
        network: z.number().min(0).optional(),
        licenses: z.number().min(0).optional(),
        personnel: z.number().min(0).optional()
      }),
      currency: z.string().optional(),
      lastUpdated: z.string().datetime().optional()
    }).refine((data) => {
      const breakdownSum = (data.breakdown.compute || 0) + 
                          (data.breakdown.storage || 0) + 
                          (data.breakdown.network || 0) + 
                          (data.breakdown.licenses || 0) + 
                          (data.breakdown.personnel || 0)
      return Math.abs(breakdownSum - data.monthly) < 0.01
    }, {
      message: "Breakdown sum must equal monthly cost",
      path: ["breakdown"]
    })
    
  9. Create input schemas for mutations:

    • updateTopologyInputSchema
    • createMilestoneInputSchema
    • updateMilestoneInputSchema
    • updateComplianceInputSchema
    • updateCostEstimateInputSchema
  10. Export all schemas

Testing:

  • Test each schema with valid data
  • Test each schema with invalid data
  • Verify error messages

Step 1.3: Create Data Serving API Route

Time: 1-2 hours
File: src/app/api/infrastructure/data/[filename]/route.ts

Implementation Steps:

  1. Create route handler:

    import { NextRequest, NextResponse } from 'next/server'
    import * as fs from 'fs'
    import * as path from 'path'
    
    export async function GET(
      request: NextRequest,
      { params }: { params: { filename: string } }
    ) {
      try {
        const filePath = path.join(process.cwd(), 'docs/infrastructure/data', params.filename)
    
        if (!fs.existsSync(filePath)) {
          return NextResponse.json(
            { error: 'File not found' },
            { status: 404 }
          )
        }
    
        const fileContent = fs.readFileSync(filePath, 'utf-8')
        const data = JSON.parse(fileContent)
        const stats = fs.statSync(filePath)
    
        const response = NextResponse.json({
          data,
          metadata: {
            lastModified: stats.mtime.toISOString(),
            filename: params.filename
          }
        })
    
        // Add caching headers
        response.headers.set('ETag', `"${stats.mtime.getTime()}"`)
        response.headers.set('Last-Modified', stats.mtime.toUTCString())
        response.headers.set('Cache-Control', 'public, max-age=3600')
    
        return response
      } catch (error) {
        return NextResponse.json(
          { error: 'Failed to load file' },
          { status: 500 }
        )
      }
    }
    
  2. Add error handling for invalid JSON

  3. Add security checks (prevent directory traversal)

  4. Add rate limiting (optional)

  5. Test with various filenames

  6. Test error cases

Testing:

  • Test with valid filename
  • Test with invalid filename
  • Test with missing file
  • Test caching headers

Step 1.4: Update Data Loading Hook

Time: 2-3 hours
File: src/lib/hooks/useInfrastructureData.ts

Implementation Steps:

  1. Import React Query:

    import { useQuery } from '@tanstack/react-query'
    
  2. Update DATA_BASE_PATH:

    const DATA_BASE_PATH = '/api/infrastructure/data'
    
  3. Create data fetcher function:

    async function fetchJSONData<T>(filename: string): Promise<T[]> {
      const response = await fetch(`${DATA_BASE_PATH}/${filename}`)
      if (!response.ok) {
        throw new Error(`Failed to load ${filename}: ${response.statusText}`)
      }
      const result = await response.json()
      return result.data || []
    }
    
  4. Update useCountries hook:

    export function useCountries(filter?: {...}) {
      return useQuery({
        queryKey: ['countries', filter],
        queryFn: () => fetchJSONData<Country>('smom_countries.json'),
        staleTime: 5 * 60 * 1000, // 5 minutes
        retry: 3,
        retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000)
      })
    }
    
  5. Apply same pattern to all hooks:

    • useNetworkTopologies
    • useComplianceRequirements
    • useDeploymentMilestones
    • useCostEstimates
    • useInfrastructureSummary
  6. Add filtering logic after data fetch

  7. Add error handling

  8. Test all hooks

Testing:

  • Test data loading
  • Test filtering
  • Test error handling
  • Test caching

Step 1.5: Implement Topology PNG Export

Time: 1-2 hours
File: src/components/infrastructure/NetworkTopologyDocs.tsx

Implementation Steps:

  1. Import html2canvas:

    import html2canvas from 'html2canvas'
    
  2. Add ref to topology container:

    const topologyRef = useRef<HTMLDivElement>(null)
    
  3. Implement handleExportPNG:

    const handleExportPNG = async () => {
      if (!topologyRef.current) return
    
      setExporting(true)
      try {
        const canvas = await html2canvas(topologyRef.current, {
          backgroundColor: '#000000',
          scale: 2, // High resolution
          logging: false
        })
    
        canvas.toBlob((blob) => {
          if (!blob) return
          const url = URL.createObjectURL(blob)
          const a = document.createElement('a')
          a.href = url
          a.download = `topology-${selectedRegion}-${Date.now()}.png`
          a.click()
          URL.revokeObjectURL(url)
    
          toast({
            title: 'Export successful',
            description: 'Topology exported as PNG',
            variant: 'success'
          })
        }, 'image/png')
      } catch (error) {
        toast({
          title: 'Export failed',
          description: error instanceof Error ? error.message : 'Unknown error',
          variant: 'error'
        })
      } finally {
        setExporting(false)
      }
    }
    
  4. Add exporting state

  5. Add loading indicator to button

  6. Test export functionality

Testing:

  • Test PNG export
  • Test with different topologies
  • Test error handling
  • Verify file download

Step 1.6: Implement Topology SVG Export

Time: 1 hour
File: src/components/infrastructure/NetworkTopologyDocs.tsx

Implementation Steps:

  1. Implement handleExportSVG:

    const handleExportSVG = () => {
      const svgElement = topologyRef.current?.querySelector('svg')
      if (!svgElement) return
    
      try {
        const svgData = new XMLSerializer().serializeToString(svgElement)
        const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' })
        const url = URL.createObjectURL(svgBlob)
        const a = document.createElement('a')
        a.href = url
        a.download = `topology-${selectedRegion}-${Date.now()}.svg`
        a.click()
        URL.revokeObjectURL(url)
    
        toast({
          title: 'Export successful',
          description: 'Topology exported as SVG',
          variant: 'success'
        })
      } catch (error) {
        toast({
          title: 'Export failed',
          description: error instanceof Error ? error.message : 'Unknown error',
          variant: 'error'
        })
      }
    }
    
  2. Test SVG export

  3. Verify SVG quality


Step 1.7: Implement Timeline PDF Export

Time: 3-4 hours
File: src/components/infrastructure/DeploymentTimeline.tsx

Implementation Steps:

  1. Import libraries:

    import jsPDF from 'jspdf'
    import html2canvas from 'html2canvas'
    
  2. Create handleExportPDF function:

    const handleExportPDF = async () => {
      setExporting(true)
      try {
        const pdf = new jsPDF('landscape', 'mm', 'a4')
    
        // Page 1: Title and metadata
        pdf.setFontSize(20)
        pdf.text('Deployment Timeline', 20, 20)
        pdf.setFontSize(12)
        pdf.text(`Region: ${selectedRegion}`, 20, 30)
        pdf.text(`Entity: ${selectedEntity}`, 20, 35)
        pdf.text(`Export Date: ${new Date().toLocaleDateString()}`, 20, 40)
    
        // Page 2: Gantt chart
        const chartElement = document.getElementById('gantt-chart')
        if (chartElement) {
          const canvas = await html2canvas(chartElement)
          const imgData = canvas.toDataURL('image/png')
          pdf.addPage()
          pdf.addImage(imgData, 'PNG', 10, 10, 277, 190)
        }
    
        // Page 3: Milestone list
        pdf.addPage()
        pdf.setFontSize(16)
        pdf.text('Milestone List', 20, 20)
    
        let y = 30
        milestones.forEach((milestone, index) => {
          if (y > 270) {
            pdf.addPage()
            y = 20
          }
          pdf.setFontSize(12)
          pdf.text(`${index + 1}. ${milestone.title}`, 20, y)
          pdf.setFontSize(10)
          pdf.text(`Status: ${milestone.status} | Priority: ${milestone.priority}`, 20, y + 5)
          y += 15
        })
    
        pdf.save(`deployment-timeline-${Date.now()}.pdf`)
    
        toast({
          title: 'Export successful',
          description: 'Timeline exported as PDF',
          variant: 'success'
        })
      } catch (error) {
        toast({
          title: 'Export failed',
          description: error instanceof Error ? error.message : 'Unknown error',
          variant: 'error'
        })
      } finally {
        setExporting(false)
      }
    }
    
  3. Add exporting state

  4. Test PDF generation

  5. Adjust layout and formatting


Step 1.8: Implement Cost Estimates Excel Export

Time: 3-4 hours
File: src/components/infrastructure/CostEstimates.tsx

Implementation Steps:

  1. Import xlsx:

    import * as XLSX from 'xlsx'
    
  2. Create handleExportExcel function:

    const handleExportExcel = () => {
      try {
        const workbook = XLSX.utils.book_new()
    
        // Summary sheet
        const summaryData = [
          ['Total Monthly', (totalMonthly / 1000).toFixed(0) + 'K'],
          ['Total Annual', (totalAnnual / 1000000).toFixed(1) + 'M'],
          ['Number of Estimates', estimates.length]
        ]
        const summarySheet = XLSX.utils.aoa_to_sheet(summaryData)
        XLSX.utils.book_append_sheet(workbook, summarySheet, 'Summary')
    
        // Detailed breakdown sheet
        const detailData = estimates.map(e => ({
          Region: e.region,
          Entity: e.entity,
          Category: e.category,
          'Monthly (USD)': e.monthly,
          'Annual (USD)': e.annual,
          'Compute': e.breakdown.compute || 0,
          'Storage': e.breakdown.storage || 0,
          'Network': e.breakdown.network || 0,
          'Licenses': e.breakdown.licenses || 0,
          'Personnel': e.breakdown.personnel || 0
        }))
        const detailSheet = XLSX.utils.json_to_sheet(detailData)
    
        // Format currency columns
        const range = XLSX.utils.decode_range(detailSheet['!ref'] || 'A1')
        for (let C = 3; C <= 4; ++C) {
          for (let R = 1; R <= range.e.r; ++R) {
            const cellAddress = XLSX.utils.encode_cell({ r: R, c: C })
            if (!detailSheet[cellAddress]) continue
            detailSheet[cellAddress].z = '$#,##0.00'
          }
        }
    
        XLSX.utils.book_append_sheet(workbook, detailSheet, 'Detailed Breakdown')
    
        // By region sheet
        const byRegion = estimates.reduce((acc, e) => {
          acc[e.region] = (acc[e.region] || 0) + e.annual
          return acc
        }, {} as Record<string, number>)
        const regionData = Object.entries(byRegion).map(([region, annual]) => ({
          Region: region,
          'Annual Cost (USD)': annual
        }))
        const regionSheet = XLSX.utils.json_to_sheet(regionData)
        XLSX.utils.book_append_sheet(workbook, regionSheet, 'By Region')
    
        XLSX.writeFile(workbook, `cost-estimates-${Date.now()}.xlsx`)
    
        toast({
          title: 'Export successful',
          description: 'Cost estimates exported as Excel',
          variant: 'success'
        })
      } catch (error) {
        toast({
          title: 'Export failed',
          description: error instanceof Error ? error.message : 'Unknown error',
          variant: 'error'
        })
      }
    }
    
  3. Test Excel export

  4. Verify formatting


Step 1.9: Create Error Boundary Component

Time: 1-2 hours
File: src/components/infrastructure/InfrastructureErrorBoundary.tsx

Implementation Steps:

  1. Create error boundary class component:

    'use client'
    
    import React from 'react'
    import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
    import { Button } from '@/components/ui/button'
    import { AlertTriangle } from 'lucide-react'
    
    interface Props {
      children: React.ReactNode
      fallback?: React.ComponentType<{ error: Error; reset: () => void }>
    }
    
    interface State {
      hasError: boolean
      error: Error | null
    }
    
    export class InfrastructureErrorBoundary extends React.Component<Props, State> {
      constructor(props: Props) {
        super(props)
        this.state = { hasError: false, error: null }
      }
    
      static getDerivedStateFromError(error: Error): State {
        return { hasError: true, error }
      }
    
      componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
        console.error('Infrastructure error:', error, errorInfo)
        // Log to Sentry if available
        if (typeof window !== 'undefined' && (window as any).Sentry) {
          (window as any).Sentry.captureException(error, { contexts: { react: errorInfo } })
        }
      }
    
      reset = () => {
        this.setState({ hasError: false, error: null })
      }
    
      render() {
        if (this.state.hasError) {
          if (this.props.fallback) {
            const Fallback = this.props.fallback
            return <Fallback error={this.state.error!} reset={this.reset} />
          }
    
          return (
            <Card className="m-4">
              <CardHeader>
                <div className="flex items-center gap-2">
                  <AlertTriangle className="h-5 w-5 text-red-400" />
                  <CardTitle>Something went wrong</CardTitle>
                </div>
                <CardDescription>
                  An error occurred while loading infrastructure data
                </CardDescription>
              </CardHeader>
              <CardContent>
                {process.env.NODE_ENV === 'development' && this.state.error && (
                  <pre className="text-sm text-red-400 mb-4">
                    {this.state.error.message}
                    {this.state.error.stack}
                  </pre>
                )}
                <Button onClick={this.reset}>Try Again</Button>
              </CardContent>
            </Card>
          )
        }
    
        return this.props.children
      }
    }
    
  2. Test error boundary

  3. Add to all page components


Step 1.10: Wrap Views with Error Boundary

Time: 30 minutes
Files: All page components

Implementation:

  1. Import InfrastructureErrorBoundary
  2. Wrap each view:
    export default function TopologyPage() {
      return (
        <InfrastructureErrorBoundary>
          <div className="container mx-auto py-8 px-4">
            <NetworkTopologyDocs />
          </div>
        </InfrastructureErrorBoundary>
      )
    }
    

Phase 2: Edit Mode Implementation (Weeks 2-3) - 60-80 hours

Step 2.1: Create Edit Compliance Form

Time: 3-4 hours
File: src/components/infrastructure/forms/EditComplianceForm.tsx

Implementation Steps:

  1. Create form component structure:

    'use client'
    
    import { useForm } from 'react-hook-form'
    import { zodResolver } from '@hookform/resolvers/zod'
    import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
    import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
    import { Input } from '@/components/ui/input'
    import { Button } from '@/components/ui/button'
    import { useUpdateComplianceRequirement } from '@/lib/graphql/hooks/useInfrastructure'
    import { useToast } from '@/components/ui/use-toast'
    import { complianceRequirementSchema } from '@/lib/validation/schemas/infrastructure'
    import type { ComplianceRequirement } from '@/lib/types/infrastructure'
    
    interface EditComplianceFormProps {
      open: boolean
      onOpenChange: (open: boolean) => void
      requirement: ComplianceRequirement
    }
    
    export function EditComplianceForm({ open, onOpenChange, requirement }: EditComplianceFormProps) {
      const { updateCompliance, loading, error } = useUpdateComplianceRequirement()
      const { toast } = useToast()
    
      const form = useForm<ComplianceRequirement>({
        resolver: zodResolver(complianceRequirementSchema),
        defaultValues: requirement
      })
    
      const onSubmit = async (data: ComplianceRequirement) => {
        try {
          await updateCompliance({
            variables: {
              country: requirement.country,
              input: {
                frameworks: data.frameworks,
                status: data.status,
                requirements: data.requirements,
                lastAuditDate: data.lastAuditDate,
                notes: data.notes
              }
            }
          })
    
          toast({
            title: 'Success',
            description: 'Compliance requirement updated',
            variant: 'success'
          })
    
          onOpenChange(false)
        } catch (err) {
          toast({
            title: 'Error',
            description: err instanceof Error ? err.message : 'Failed to update',
            variant: 'error'
          })
        }
      }
    
      return (
        <Dialog open={open} onOpenChange={onOpenChange}>
          <DialogContent className="max-w-2xl">
            <DialogHeader>
              <DialogTitle>Edit Compliance Requirement</DialogTitle>
            </DialogHeader>
    
            <Form {...form}>
              <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
                <FormField
                  control={form.control}
                  name="country"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>Country</FormLabel>
                      <FormControl>
                        <Input {...field} disabled />
                      </FormControl>
                    </FormItem>
                  )}
                />
    
                {/* Add all other fields similarly */}
    
                <DialogFooter>
                  <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
                    Cancel
                  </Button>
                  <Button type="submit" disabled={loading}>
                    {loading ? 'Saving...' : 'Save'}
                  </Button>
                </DialogFooter>
              </form>
            </Form>
          </DialogContent>
        </Dialog>
      )
    }
    
  2. Add all form fields with proper types

  3. Add multi-select for frameworks

  4. Add date picker for lastAuditDate

  5. Add textarea for requirements (one per line)

  6. Test form validation

  7. Test form submission


Step 2.2: Create Edit Milestone Form

Time: 4-5 hours
File: src/components/infrastructure/forms/EditMilestoneForm.tsx

Implementation Steps:

  1. Create form with all fields
  2. Add date range validation
  3. Add dependency selector (multi-select of other milestones)
  4. Add dependency validation (no circular refs)
  5. Add cost field with currency formatting
  6. Handle create vs edit modes
  7. Show dependency graph preview
  8. Test all validations

Step 2.3: Create Edit Cost Estimate Form

Time: 3-4 hours
File: src/components/infrastructure/forms/EditCostEstimateForm.tsx

Implementation Steps:

  1. Create form with cost fields
  2. Add auto-calculation (monthly * 12 = annual)
  3. Add breakdown fields
  4. Add validation (breakdown sum = monthly)
  5. Add currency selector
  6. Test calculations
  7. Test validation

Step 2.4: Create Edit Topology Node Form

Time: 3-4 hours
File: src/components/infrastructure/forms/EditTopologyNodeForm.tsx

Implementation Steps:

  1. Create form with node fields
  2. Add position X/Y fields
  3. Add metadata editor (JSON or key-value)
  4. Add validation (unique label, valid coordinates)
  5. Integrate with topology update
  6. Test node editing

Step 2.5: Wire Up Edit Forms

Time: 2-3 hours per component
Files: All infrastructure component files

Implementation Steps:

  1. Import form components
  2. Add state for dialog open/close
  3. Add state for item being edited
  4. Connect edit buttons:
    <Button onClick={() => {
      setEditingItem(requirement)
      setDialogOpen(true)
    }}>
      Edit
    </Button>
    
  5. Add form component:
    <EditComplianceForm
      open={dialogOpen}
      onOpenChange={setDialogOpen}
      requirement={editingItem!}
    />
    
  6. Refresh data after successful edit
  7. Test edit workflow

Step 2.6: Implement Topology Edit Mode

Time: 6-8 hours
File: src/components/infrastructure/NetworkTopologyDocs.tsx

Implementation Steps:

  1. Add edit mode state management

  2. Implement node dragging:

    const onNodeDrag = useCallback((event: NodeDragEvent, node: Node) => {
      // Update node position in state
      setTopology(prev => ({
        ...prev,
        nodes: prev.nodes.map(n => 
          n.id === node.id 
            ? { ...n, position: { x: node.position.x, y: node.position.y } }
            : n
        )
      }))
    }, [])
    
  3. Add "Add Node" button and dialog

  4. Implement edge creation (click source, then target)

  5. Add delete functionality

  6. Add context menu

  7. Implement bulk selection

  8. Add save button

  9. Integrate with mutation

  10. Add undo/redo


Step 2.7: Implement Timeline Drag-and-Drop

Time: 4-5 hours
File: src/components/infrastructure/DeploymentTimeline.tsx

Implementation Steps:

  1. Set up @dnd-kit:

    import { DndContext, DragEndEvent } from '@dnd-kit/core'
    import { SortableContext, useSortable } from '@dnd-kit/sortable'
    
  2. Make milestones draggable

  3. Implement drop handler

  4. Validate date constraints

  5. Check dependencies

  6. Auto-save on drop

  7. Show loading state


Phase 3: React Flow Integration (Week 3) - 20-25 hours

Step 3.1: Create React Flow Topology Component

Time: 4-5 hours
File: src/components/infrastructure/topology/ReactFlowTopology.tsx

Implementation Steps:

  1. Import React Flow:

    import ReactFlow, {
      Background,
      Controls,
      MiniMap,
      Node,
      Edge,
      Connection,
      useNodesState,
      useEdgesState,
      addEdge,
      onNodesChange,
      onEdgesChange
    } from 'reactflow'
    import 'reactflow/dist/style.css'
    
  2. Convert topology data:

    const nodes: Node[] = topology.nodes.map(node => ({
      id: node.id,
      type: node.type,
      position: node.position,
      data: { label: node.label, ...node.metadata }
    }))
    
    const edges: Edge[] = topology.edges.map(edge => ({
      id: edge.id,
      source: edge.source,
      target: edge.target,
      type: edge.type,
      label: edge.metadata.bandwidth || edge.type
    }))
    
  3. Set up React Flow component

  4. Add minimap and controls

  5. Add background grid

  6. Implement zoom/pan

  7. Add fit view


Step 3.2-3.4: Create Custom Nodes and Edges

Time: 8-10 hours
Files: Multiple files

Implementation Steps:

  1. Create base node component
  2. Create specialized nodes (5 files)
  3. Create custom edge component
  4. Register node/edge types
  5. Integrate into main component
  6. Test all node types
  7. Test edit mode with React Flow

Phase 4: Map Visualization (Week 4) - 8-10 hours

Step 4.1-4.3: Mapbox Integration

Time: 8-10 hours
Files: Multiple files

Implementation Steps:

  1. Set up Mapbox token
  2. Create ComplianceMapView component
  3. Load country coordinates
  4. Create markers
  5. Add popups
  6. Implement clustering
  7. Add layer controls
  8. Integrate into compliance component

Phase 5-8: Additional Phases

(Continue with similar detailed steps for remaining phases...)


Testing Checklist

After each phase, test:

  • All exports work correctly
  • Forms validate properly
  • Mutations succeed and update UI
  • Error handling works
  • Loading states display
  • Empty states display
  • Filters work correctly
  • No console errors
  • Responsive design works
  • Accessibility features work

Deployment Checklist

Before deploying:

  • All dependencies installed
  • Environment variables set
  • API routes tested
  • Error boundaries tested
  • Performance tested
  • Security reviewed
  • Documentation complete
  • Tests passing