swiftops-backend / docs /api /platform-admin /PLATFORM_ADMIN_FRONTEND_GUIDE.md
kamau1's picture
feat: add audit logs and dashboard analytics endpoints
c1ba75f

Platform Admin Frontend Development Guide

Last Updated: November 17, 2025
Target Audience: Frontend Developers
Backend Version: SwiftOps v1.0


Table of Contents

  1. Overview
  2. Authentication & Authorization
  3. Dashboard & Analytics
  4. Organization Management
  5. User Management
  6. System Monitoring
  7. Audit Logs
  8. Complete API Reference
  9. UI/UX Recommendations
  10. Error Handling

Overview

What is a Platform Admin?

Platform Admin is the highest-level administrative role with full system access. They manage:

  • Organizations (Clients & Contractors)
  • Platform-wide settings
  • System health monitoring
  • Audit logs and compliance
  • Billing and usage metrics (planned)

Key Responsibilities:

  • Create and manage client organizations (telecom operators)
  • Create and manage contractor organizations (field service providers)
  • Monitor platform usage across all organizations
  • View comprehensive audit trails
  • Manage platform-wide configurations
  • Access all system data for oversight

Authorization Level:

{
  role: "platform_admin",
  permissions: ["*"], // All permissions
  access_scope: "global" // Access to all organizations and projects
}

Authentication & Authorization

1. Registration Flow

Platform admins register through a secure 2-step OTP verification process.

Step 1: Request OTP

POST /api/v1/auth/send-admin-otp
Content-Type: application/json

{
  "email": "admin@swiftops.com",
  "first_name": "John",
  "last_name": "Doe",
  "phone": "+254700123456"
}

Response:

{
  "message": "βœ… Registration request received! An OTP code has been sent to admin@swiftops.com..."
}

Step 2: Complete Registration

POST /api/v1/auth/register
Content-Type: application/json

{
  "email": "admin@swiftops.com",
  "first_name": "John",
  "last_name": "Doe",
  "phone": "+254700123456",
  "password": "SecurePassword123!",
  "otp_code": "613281"
}

Response:

{
  "message": "βœ… Platform admin account created successfully!",
  "user_id": "uuid-here"
}

2. Login

POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "admin@swiftops.com",
  "password": "SecurePassword123!"
}

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "user": {
    "id": "uuid",
    "email": "admin@swiftops.com",
    "first_name": "John",
    "last_name": "Doe",
    "role": "platform_admin",
    "is_active": true,
    "phone": "+254700123456"
  }
}

3. Authentication Headers

All subsequent requests must include:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

4. Get Current User

GET /api/v1/auth/me
Authorization: Bearer {token}

Response:

{
  "id": "uuid",
  "email": "admin@swiftops.com",
  "role": "platform_admin",
  "first_name": "John",
  "last_name": "Doe",
  "is_active": true,
  "created_at": "2025-11-17T10:00:00Z"
}

Dashboard & Analytics

Dashboard Overview Page

The platform admin dashboard should display high-level metrics and system health.

1. System-Wide Statistics

Endpoint: GET /api/v1/tickets/stats

GET /api/v1/tickets/stats
Authorization: Bearer {token}

Response:

{
  "total_tickets": 1500,
  "by_status": {
    "open": 120,
    "assigned": 350,
    "in_progress": 280,
    "completed": 700,
    "cancelled": 50
  },
  "by_type": {
    "installation": 800,
    "support": 500,
    "infrastructure": 200
  },
  "by_priority": {
    "urgent": 45,
    "high": 200,
    "normal": 900,
    "low": 355
  },
  "overdue_tickets": 25,
  "sla_violations": 12,
  "avg_completion_time_hours": 18.5,
  "completion_rate": 0.87
}

UI Component Suggestion:

// Dashboard Overview Cards
<div className="grid grid-cols-4 gap-4">
  <StatCard
    title="Total Tickets"
    value={stats.total_tickets}
    icon="ticket"
  />
  <StatCard
    title="In Progress"
    value={stats.by_status.in_progress}
    icon="clock"
  />
  <StatCard
    title="Completed Today"
    value={stats.by_status.completed}
    icon="check"
    trend="+12%"
  />
  <StatCard
    title="SLA Violations"
    value={stats.sla_violations}
    icon="alert"
    variant="danger"
  />
</div>

2. Organization Metrics

Clients Overview:

GET /api/v1/clients?limit=100
Authorization: Bearer {token}

Response:

[
  {
    "id": "uuid",
    "name": "Airtel Kenya",
    "industry": "Telecommunications",
    "is_active": true,
    "default_sla_days": 3,
    "created_at": "2025-01-01T00:00:00Z",
    "main_email": "contact@airtel.co.ke",
    "main_phone": "+254700000000"
  }
]

Contractors Overview:

GET /api/v1/contractors?limit=100
Authorization: Bearer {token}

Response:

[
  {
    "id": "uuid",
    "name": "TechInstall Ltd",
    "competencies": ["FTTH", "Fixed Wireless", "Fiber Splicing"],
    "is_active": true,
    "onboarding_status": "completed",
    "created_at": "2025-01-15T00:00:00Z",
    "main_email": "info@techinstall.co.ke"
  }
]

UI Component Suggestion:

// Organizations Summary
<div className="grid grid-cols-2 gap-6">
  <Card>
    <CardHeader>
      <h3>Clients</h3>
      <Badge>{clients.length} Active</Badge>
    </CardHeader>
    <CardBody>
      <List>
        {clients.map(client => (
          <ListItem key={client.id}>
            <Avatar name={client.name} />
            <div>
              <p className="font-semibold">{client.name}</p>
              <p className="text-sm text-gray-500">{client.industry}</p>
            </div>
            <StatusBadge active={client.is_active} />
          </ListItem>
        ))}
      </List>
    </CardBody>
  </Card>

  <Card>
    <CardHeader>
      <h3>Contractors</h3>
      <Badge>{contractors.length} Active</Badge>
    </CardHeader>
    <CardBody>
      {/* Similar list for contractors */}
    </CardBody>
  </Card>
</div>

3. Active Projects

GET /api/v1/projects?is_active=true&limit=100
Authorization: Bearer {token}

Response:

[
  {
    "id": "uuid",
    "title": "Nairobi FTTH Expansion",
    "client_id": "uuid",
    "contractor_id": "uuid",
    "client_name": "Airtel Kenya",
    "contractor_name": "TechInstall Ltd",
    "status": "active",
    "start_date": "2025-01-01",
    "end_date": "2025-12-31",
    "total_tickets": 500,
    "completed_tickets": 320
  }
]

UI Component Suggestion:

// Active Projects Table
<DataTable
  columns={[
    { key: "title", label: "Project Name" },
    { key: "client_name", label: "Client" },
    { key: "contractor_name", label: "Contractor" },
    { key: "progress", label: "Progress", render: (row) => (
      <ProgressBar 
        value={row.completed_tickets} 
        max={row.total_tickets} 
      />
    )},
    { key: "status", label: "Status" }
  ]}
  data={projects}
/>

4. Recent Activity Feed

GET /api/v1/audit-logs?limit=20
Authorization: Bearer {token}

Response:

[
  {
    "id": "uuid",
    "action": "create",
    "entity_type": "client",
    "description": "Client organization created: Safaricom Ltd",
    "user_email": "admin@swiftops.com",
    "created_at": "2025-11-17T10:30:00Z",
    "ip_address": "192.168.1.1"
  }
]

Organization Management

1. Create Client Organization

Purpose: Onboard new telecom operators or clients to the platform.

Endpoint:

POST /api/v1/clients
Authorization: Bearer {token}
Content-Type: application/json

{
  "name": "Safaricom Kenya",
  "description": "Leading telecommunications provider in Kenya",
  "industry": "Telecommunications",
  "main_email": "contact@safaricom.co.ke",
  "main_phone": "+254700000000",
  "website": "https://safaricom.co.ke",
  "default_sla_days": 3
}

Response:

{
  "id": "uuid",
  "name": "Safaricom Kenya",
  "description": "Leading telecommunications provider in Kenya",
  "industry": "Telecommunications",
  "main_email": "contact@safaricom.co.ke",
  "main_phone": "+254700000000",
  "website": "https://safaricom.co.ke",
  "is_active": true,
  "default_sla_days": 3,
  "created_at": "2025-11-17T10:00:00Z",
  "updated_at": "2025-11-17T10:00:00Z"
}

Validation Rules:

  • name: Required, unique, 3-100 characters
  • industry: Optional, suggested values: "Telecommunications", "Utilities", "ISP", "Construction"
  • main_email: Required, unique, valid email format
  • main_phone: Optional, valid phone format
  • default_sla_days: Optional, integer 1-30, defaults to 3

UI Form Fields:

<Form onSubmit={handleCreateClient}>
  <TextField
    name="name"
    label="Client Name"
    required
    placeholder="e.g., Safaricom Kenya"
  />
  
  <TextArea
    name="description"
    label="Description"
    rows={3}
    placeholder="Brief description of the organization"
  />
  
  <Select
    name="industry"
    label="Industry"
    options={[
      { value: "Telecommunications", label: "Telecommunications" },
      { value: "Utilities", label: "Utilities" },
      { value: "ISP", label: "Internet Service Provider" },
      { value: "Construction", label: "Construction" }
    ]}
  />
  
  <TextField
    name="main_email"
    label="Primary Email"
    type="email"
    required
    placeholder="contact@example.com"
  />
  
  <TextField
    name="main_phone"
    label="Primary Phone"
    type="tel"
    placeholder="+254700000000"
  />
  
  <TextField
    name="website"
    label="Website"
    type="url"
    placeholder="https://example.com"
  />
  
  <NumberField
    name="default_sla_days"
    label="Default SLA (Days)"
    min={1}
    max={30}
    defaultValue={3}
  />
  
  <Button type="submit">Create Client</Button>
</Form>

2. List Clients

Endpoint:

GET /api/v1/clients?skip=0&limit=100&is_active=true
Authorization: Bearer {token}

Query Parameters:

  • skip: Number of records to skip (pagination), default: 0
  • limit: Max records to return (1-100), default: 100
  • is_active: Filter by active status (optional)

Response: Array of client objects (see Create Client response for structure)

UI Component:

// Clients List/Table View
<PageHeader title="Clients" action={<Button onClick={openCreateModal}>+ New Client</Button>} />

<DataTable
  columns={[
    { key: "name", label: "Name", sortable: true },
    { key: "industry", label: "Industry" },
    { key: "main_email", label: "Email" },
    { key: "main_phone", label: "Phone" },
    { key: "is_active", label: "Status", render: (row) => (
      <StatusBadge active={row.is_active} />
    )},
    { key: "created_at", label: "Created", render: (row) => (
      formatDate(row.created_at)
    )},
    { key: "actions", label: "", render: (row) => (
      <DropdownMenu>
        <MenuItem onClick={() => viewClient(row.id)}>View Details</MenuItem>
        <MenuItem onClick={() => editClient(row.id)}>Edit</MenuItem>
        <MenuItem onClick={() => deactivateClient(row.id)} variant="danger">
          Deactivate
        </MenuItem>
      </DropdownMenu>
    )}
  ]}
  data={clients}
  pagination={{
    page: currentPage,
    pageSize: 100,
    total: totalClients,
    onPageChange: setCurrentPage
  }}
/>

3. View Client Details

Endpoint:

GET /api/v1/clients/{client_id}
Authorization: Bearer {token}

Response: Single client object

UI Page Structure:

// Client Details Page
<PageLayout>
  <PageHeader
    title={client.name}
    subtitle={client.industry}
    actions={[
      <Button onClick={editClient}>Edit</Button>,
      <Button variant="secondary" onClick={viewProjects}>View Projects</Button>
    ]}
  />
  
  <Tabs>
    <Tab label="Overview">
      <div className="grid grid-cols-2 gap-6">
        <Card title="Contact Information">
          <dl>
            <dt>Email</dt>
            <dd>{client.main_email}</dd>
            <dt>Phone</dt>
            <dd>{client.main_phone}</dd>
            <dt>Website</dt>
            <dd><a href={client.website}>{client.website}</a></dd>
          </dl>
        </Card>
        
        <Card title="Settings">
          <dl>
            <dt>Default SLA</dt>
            <dd>{client.default_sla_days} days</dd>
            <dt>Status</dt>
            <dd><StatusBadge active={client.is_active} /></dd>
            <dt>Created</dt>
            <dd>{formatDate(client.created_at)}</dd>
          </dl>
        </Card>
      </div>
    </Tab>
    
    <Tab label="Projects">
      {/* List projects associated with this client */}
    </Tab>
    
    <Tab label="Users">
      {/* List users belonging to this client organization */}
    </Tab>
    
    <Tab label="Activity Log">
      {/* Audit logs for this client */}
    </Tab>
  </Tabs>
</PageLayout>

4. Update Client

Endpoint:

PUT /api/v1/clients/{client_id}
Authorization: Bearer {token}
Content-Type: application/json

{
  "description": "Updated description",
  "is_active": true,
  "default_sla_days": 5,
  "main_phone": "+254711222333"
}

Response: Updated client object

Note: Only include fields you want to update. Name and email cannot be changed after creation.

5. Deactivate Client

Endpoint:

DELETE /api/v1/clients/{client_id}
Authorization: Bearer {token}

Response:

{
  "message": "Client soft-deleted successfully"
}

Note: This performs a soft delete (sets deleted_at timestamp). The client is hidden from default queries but data is retained.

6. Create Contractor Organization

Purpose: Onboard field service contractors who will execute work for clients.

Endpoint:

POST /api/v1/contractors
Authorization: Bearer {token}
Content-Type: application/json

{
  "name": "FieldTech Solutions",
  "description": "Professional FTTH installation services",
  "website": "https://fieldtech.co.ke",
  "main_email": "ops@fieldtech.co.ke",
  "main_phone": "+254722333444",
  "competencies": ["FTTH", "Fixed Wireless", "Fiber Splicing", "FTTB"]
}

Response:

{
  "id": "uuid",
  "name": "FieldTech Solutions",
  "description": "Professional FTTH installation services",
  "website": "https://fieldtech.co.ke",
  "main_email": "ops@fieldtech.co.ke",
  "main_phone": "+254722333444",
  "is_active": true,
  "competencies": ["FTTH", "Fixed Wireless", "Fiber Splicing", "FTTB"],
  "onboarding_status": "started",
  "onboarding_completed_at": null,
  "created_at": "2025-11-17T10:00:00Z",
  "updated_at": "2025-11-17T10:00:00Z"
}

Validation Rules:

  • name: Required, unique, 3-100 characters
  • competencies: Required array, available options:
    • "FTTH" (Fiber to the Home)
    • "FTTB" (Fiber to the Building)
    • "Fixed Wireless"
    • "Fiber Splicing"
    • "Infrastructure"
    • "Maintenance"
    • "Support"

UI Form Fields:

<Form onSubmit={handleCreateContractor}>
  <TextField
    name="name"
    label="Contractor Name"
    required
    placeholder="e.g., FieldTech Solutions"
  />
  
  <TextArea
    name="description"
    label="Description"
    rows={3}
    placeholder="Brief description of services offered"
  />
  
  <TextField
    name="main_email"
    label="Primary Email"
    type="email"
    required
    placeholder="ops@example.com"
  />
  
  <TextField
    name="main_phone"
    label="Primary Phone"
    type="tel"
    placeholder="+254700000000"
  />
  
  <TextField
    name="website"
    label="Website"
    type="url"
    placeholder="https://example.com"
  />
  
  <CheckboxGroup
    name="competencies"
    label="Competencies"
    required
    options={[
      { value: "FTTH", label: "Fiber to the Home (FTTH)" },
      { value: "FTTB", label: "Fiber to the Building (FTTB)" },
      { value: "Fixed Wireless", label: "Fixed Wireless" },
      { value: "Fiber Splicing", label: "Fiber Splicing" },
      { value: "Infrastructure", label: "Infrastructure" },
      { value: "Maintenance", label: "Maintenance" },
      { value: "Support", label: "Support" }
    ]}
  />
  
  <Button type="submit">Create Contractor</Button>
</Form>

7. List Contractors

Endpoint:

GET /api/v1/contractors?skip=0&limit=100&is_active=true
Authorization: Bearer {token}

Query Parameters: Same as clients

Response: Array of contractor objects

8. View Contractor Details

Endpoint:

GET /api/v1/contractors/{contractor_id}
Authorization: Bearer {token}

Response: Single contractor object

9. Update Contractor

Endpoint:

PUT /api/v1/contractors/{contractor_id}
Authorization: Bearer {token}
Content-Type: application/json

{
  "description": "Updated description",
  "is_active": true,
  "competencies": ["FTTH", "Fixed Wireless", "Fiber Splicing", "FTTB"],
  "onboarding_status": "completed"
}

Response: Updated contractor object

Note: Setting onboarding_status to "completed" automatically sets onboarding_completed_at timestamp.

Onboarding Status Values:

  • "started" - Initial state
  • "documents_pending" - Awaiting compliance documents
  • "training" - Undergoing training
  • "completed" - Fully onboarded, ready for work

User Management

1. List All Users

Endpoint:

GET /api/v1/users?skip=0&limit=100&role=field_agent
Authorization: Bearer {token}

Query Parameters:

  • skip: Pagination offset
  • limit: Max records (1-100)
  • role: Filter by role (optional)
    • platform_admin
    • client_admin
    • contractor_admin
    • project_manager
    • dispatcher
    • field_agent
    • sales_agent
    • sales_manager
  • is_active: Filter by active status (optional)
  • client_id: Filter by client organization (optional)
  • contractor_id: Filter by contractor organization (optional)

Response:

[
  {
    "id": "uuid",
    "email": "agent@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "role": "field_agent",
    "is_active": true,
    "phone": "+254700123456",
    "client_id": null,
    "contractor_id": "uuid",
    "created_at": "2025-11-17T10:00:00Z"
  }
]

2. View User Details

Endpoint:

GET /api/v1/users/{user_id}
Authorization: Bearer {token}

Response: Single user object

3. Invite New User

Endpoint:

POST /api/v1/invitations
Authorization: Bearer {token}
Content-Type: application/json

{
  "email": "newuser@example.com",
  "first_name": "Jane",
  "last_name": "Smith",
  "role": "project_manager",
  "client_id": "uuid",
  "notification_channel": "email"
}

Response:

{
  "id": "uuid",
  "email": "newuser@example.com",
  "role": "project_manager",
  "status": "pending",
  "expires_at": "2025-11-24T10:00:00Z",
  "created_at": "2025-11-17T10:00:00Z"
}

Notification Channels:

  • "email" - Send invitation via email
  • "whatsapp" - Send invitation via WhatsApp (default, saves email credits)
  • "both" - Send via both channels

4. Update User

Endpoint:

PUT /api/v1/users/{user_id}
Authorization: Bearer {token}
Content-Type: application/json

{
  "first_name": "Jane",
  "last_name": "Smith",
  "phone": "+254711222333",
  "is_active": true
}

Response: Updated user object

5. Change User Role

Endpoint:

PUT /api/v1/users/{user_id}/role
Authorization: Bearer {token}
Content-Type: application/json

{
  "role": "dispatcher",
  "reason": "Promoted due to excellent performance"
}

Response:

{
  "message": "User role updated successfully",
  "user": { /* updated user object */ }
}

Note: Role changes are logged in audit trail with reason.

6. Deactivate User

Endpoint:

POST /api/v1/users/{user_id}/deactivate
Authorization: Bearer {token}
Content-Type: application/json

{
  "reason": "Employee left the company"
}

Response:

{
  "message": "User deactivated successfully"
}

System Monitoring

1. Assignment Statistics

Endpoint:

GET /api/v1/assignments/stats?project_id={uuid}
Authorization: Bearer {token}

Response:

{
  "total_assignments": 500,
  "active_assignments": 120,
  "completed_assignments": 350,
  "dropped_assignments": 20,
  "rejected_assignments": 10,
  "avg_travel_time_minutes": 45.5,
  "avg_work_time_minutes": 120.3,
  "avg_journey_distance_km": 15.2
}

UI Component:

<Card title="Assignment Performance">
  <div className="grid grid-cols-3 gap-4">
    <Metric
      label="Avg Travel Time"
      value={`${stats.avg_travel_time_minutes} min`}
      icon="car"
    />
    <Metric
      label="Avg Work Time"
      value={`${stats.avg_work_time_minutes} min`}
      icon="clock"
    />
    <Metric
      label="Avg Distance"
      value={`${stats.avg_journey_distance_km} km`}
      icon="map"
    />
  </div>
  
  <DonutChart
    data={[
      { label: "Completed", value: stats.completed_assignments },
      { label: "Active", value: stats.active_assignments },
      { label: "Dropped", value: stats.dropped_assignments }
    ]}
  />
</Card>

2. Timesheet Statistics

Endpoint:

GET /api/v1/timesheets/stats?start_date=2025-11-01&end_date=2025-11-30
Authorization: Bearer {token}

Query Parameters:

  • start_date: Start date (required, YYYY-MM-DD)
  • end_date: End date (required, YYYY-MM-DD)
  • user_id: Filter by specific user (optional)
  • project_id: Filter by specific project (optional)

Response:

{
  "total_days": 30,
  "present_days": 25,
  "absent_days": 3,
  "on_leave_days": 2,
  "sick_leave_days": 0,
  "attendance_rate": 83.33,
  "absence_rate": 10.0,
  "total_hours_worked": 200.5,
  "average_hours_per_day": 8.02,
  "total_tickets_completed": 150,
  "average_tickets_per_day": 6.0
}

3. Notification Statistics

Endpoint:

GET /api/v1/notifications/stats
Authorization: Bearer {token}

Response:

{
  "total": 500,
  "unread": 45,
  "read": 455,
  "by_type": {
    "ticket_assigned": 200,
    "ticket_completed": 150,
    "payment_received": 50,
    "system_alert": 100
  },
  "by_channel": {
    "in_app": 500,
    "email": 300,
    "whatsapp": 200
  }
}

4. Export Data

Platform admins can export data for all organizations.

Tickets Export:

GET /api/v1/export/tickets?project_id={uuid}&format=csv
Authorization: Bearer {token}

Response: CSV file download

Available Export Endpoints:

  • /api/v1/export/tickets - All tickets
  • /api/v1/export/customers - All customers
  • /api/v1/export/sales-orders - All sales orders
  • /api/v1/export/agents - All field agents
  • /api/v1/export/subscriptions - All subscriptions

Query Parameters:

  • format: csv or excel (default: csv)
  • project_id: Filter by project (optional)
  • start_date: Filter by date range (optional)
  • end_date: Filter by date range (optional)

Audit Logs

1. View Audit Logs

Endpoint:

GET /api/v1/audit-logs?skip=0&limit=100&action=create
Authorization: Bearer {token}

Query Parameters:

  • skip: Pagination offset
  • limit: Max records (1-100)
  • action: Filter by action type (optional)
    • create, update, delete
    • login, logout, login_failed
    • register, password_change
    • status_change, role_change
  • entity_type: Filter by entity (optional)
    • user, client, contractor, project, ticket
  • user_id: Filter by user who performed action (optional)
  • start_date: Filter by date range (optional)
  • end_date: Filter by date range (optional)

Response:

[
  {
    "id": "uuid",
    "user_id": "uuid",
    "user_email": "admin@swiftops.com",
    "user_role": "platform_admin",
    "action": "create",
    "entity_type": "client",
    "entity_id": "uuid",
    "description": "Client organization created: Safaricom Ltd by platform_admin",
    "changes": {
      "new": {
        "name": "Safaricom Ltd",
        "industry": "Telecommunications"
      }
    },
    "ip_address": "192.168.1.1",
    "user_agent": "Mozilla/5.0...",
    "created_at": "2025-11-17T10:30:00Z"
  }
]

2. View Audit Logs for Specific Entity

Endpoint:

GET /api/v1/audit-logs/entity/{entity_type}/{entity_id}
Authorization: Bearer {token}

Example:

GET /api/v1/audit-logs/entity/client/uuid-here

Response: Array of audit log objects filtered for that entity

3. View Audit Logs for Specific User

Endpoint:

GET /api/v1/audit-logs/user/{user_id}
Authorization: Bearer {token}

Response: Array of audit log objects for that user's actions

UI Component:

// Audit Log Table
<DataTable
  columns={[
    { key: "created_at", label: "Timestamp", render: (row) => (
      formatDateTime(row.created_at)
    )},
    { key: "user_email", label: "User" },
    { key: "action", label: "Action", render: (row) => (
      <Badge variant={getActionColor(row.action)}>{row.action}</Badge>
    )},
    { key: "entity_type", label: "Entity" },
    { key: "description", label: "Description" },
    { key: "ip_address", label: "IP Address" },
    { key: "actions", label: "", render: (row) => (
      <Button size="sm" onClick={() => viewDetails(row)}>Details</Button>
    )}
  ]}
  data={auditLogs}
  pagination={true}
/>

// Audit Log Detail Modal
<Modal isOpen={detailsOpen} onClose={() => setDetailsOpen(false)}>
  <ModalHeader>Audit Log Details</ModalHeader>
  <ModalBody>
    <dl>
      <dt>Action</dt>
      <dd>{selectedLog.action}</dd>
      
      <dt>User</dt>
      <dd>{selectedLog.user_email} ({selectedLog.user_role})</dd>
      
      <dt>Entity</dt>
      <dd>{selectedLog.entity_type} - {selectedLog.entity_id}</dd>
      
      <dt>Timestamp</dt>
      <dd>{formatDateTime(selectedLog.created_at)}</dd>
      
      <dt>IP Address</dt>
      <dd>{selectedLog.ip_address}</dd>
      
      <dt>User Agent</dt>
      <dd>{selectedLog.user_agent}</dd>
      
      <dt>Description</dt>
      <dd>{selectedLog.description}</dd>
      
      {selectedLog.changes && (
        <>
          <dt>Changes</dt>
          <dd>
            <pre className="bg-gray-100 p-4 rounded">
              {JSON.stringify(selectedLog.changes, null, 2)}
            </pre>
          </dd>
        </>
      )}
    </dl>
  </ModalBody>
</Modal>

4. Export Audit Logs

Endpoint:

POST /api/v1/audit-logs/export
Authorization: Bearer {token}
Content-Type: application/json

{
  "start_date": "2025-11-01",
  "end_date": "2025-11-30",
  "format": "csv"
}

Response: CSV file download with all audit logs in date range


Complete API Reference

Authentication Endpoints

Method Endpoint Description
POST /api/v1/auth/send-admin-otp Request OTP for admin registration
POST /api/v1/auth/register Complete admin registration with OTP
POST /api/v1/auth/login Login and get access token
POST /api/v1/auth/logout Logout (invalidate token)
GET /api/v1/auth/me Get current user info
PUT /api/v1/auth/me Update current user profile
POST /api/v1/auth/change-password Change password
POST /api/v1/auth/forgot-password Request password reset
POST /api/v1/auth/reset-password Reset password with token

Client Management Endpoints

Method Endpoint Description
POST /api/v1/clients Create new client organization
GET /api/v1/clients List all clients (paginated)
GET /api/v1/clients/{client_id} Get client details
PUT /api/v1/clients/{client_id} Update client
DELETE /api/v1/clients/{client_id} Deactivate client (soft delete)

Contractor Management Endpoints

Method Endpoint Description
POST /api/v1/contractors Create new contractor organization
GET /api/v1/contractors List all contractors (paginated)
GET /api/v1/contractors/{contractor_id} Get contractor details
PUT /api/v1/contractors/{contractor_id} Update contractor
DELETE /api/v1/contractors/{contractor_id} Deactivate contractor (soft delete)

User Management Endpoints

Method Endpoint Description
GET /api/v1/users List all users (paginated)
GET /api/v1/users/{user_id} Get user details
PUT /api/v1/users/{user_id} Update user
PUT /api/v1/users/{user_id}/role Change user role
POST /api/v1/users/{user_id}/deactivate Deactivate user
POST /api/v1/users/{user_id}/activate Activate user

Invitation Endpoints

Method Endpoint Description
POST /api/v1/invitations Create user invitation
GET /api/v1/invitations List all invitations
GET /api/v1/invitations/{invitation_id} Get invitation details
POST /api/v1/invitations/{invitation_id}/resend Resend invitation
DELETE /api/v1/invitations/{invitation_id} Cancel invitation

Project Endpoints

Method Endpoint Description
GET /api/v1/projects List all projects
GET /api/v1/projects/{project_id} Get project details
POST /api/v1/projects Create project (as client/contractor)
PUT /api/v1/projects/{project_id} Update project
DELETE /api/v1/projects/{project_id} Delete project

Ticket Endpoints

Method Endpoint Description
GET /api/v1/tickets List all tickets
GET /api/v1/tickets/{ticket_id} Get ticket details
GET /api/v1/tickets/stats Get ticket analytics
PUT /api/v1/tickets/{ticket_id} Update ticket
DELETE /api/v1/tickets/{ticket_id} Delete ticket

Assignment Endpoints

Method Endpoint Description
GET /api/v1/assignments/stats Get assignment statistics
GET /api/v1/tickets/{ticket_id}/assignments Get ticket assignments

Timesheet Endpoints

Method Endpoint Description
GET /api/v1/timesheets List timesheets
GET /api/v1/timesheets/stats Get timesheet statistics

Finance Endpoints

Method Endpoint Description
GET /api/v1/contractor-invoices List contractor invoices
GET /api/v1/contractor-invoices/{invoice_id} Get invoice details
POST /api/v1/contractor-invoices Create invoice
PUT /api/v1/contractor-invoices/{invoice_id} Update invoice
POST /api/v1/contractor-invoices/{invoice_id}/payments Record payment

Audit Log Endpoints

Method Endpoint Description
GET /api/v1/audit-logs List all audit logs (paginated)
GET /api/v1/audit-logs/user/{user_id} Get logs for specific user
GET /api/v1/audit-logs/entity/{entity_type}/{entity_id} Get logs for specific entity
POST /api/v1/audit-logs/export Export audit logs to CSV

Export Endpoints

Method Endpoint Description
GET /api/v1/export/tickets Export tickets to CSV/Excel
GET /api/v1/export/customers Export customers to CSV/Excel
GET /api/v1/export/sales-orders Export sales orders to CSV/Excel
GET /api/v1/export/agents Export field agents to CSV/Excel
GET /api/v1/export/subscriptions Export subscriptions to CSV/Excel

Notification Endpoints

Method Endpoint Description
GET /api/v1/notifications List user notifications
GET /api/v1/notifications/stats Get notification statistics
GET /api/v1/notifications/unread-count Get unread count (for badge)
PUT /api/v1/notifications/{notification_id}/read Mark notification as read
PUT /api/v1/notifications/mark-all-read Mark all as read

UI/UX Recommendations

Page Structure

1. Dashboard (Home Page)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Platform Admin Dashboard                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                     β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”               β”‚
β”‚ β”‚ 1.5K β”‚ β”‚ 320  β”‚ β”‚ 87%  β”‚ β”‚ 12   β”‚               β”‚
β”‚ β”‚Ticketβ”‚ β”‚ Activeβ”‚ β”‚ SLA  β”‚ β”‚Alert β”‚               β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜               β”‚
β”‚                                                     β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚ β”‚ Clients              β”‚ β”‚ Contractors          β”‚  β”‚
β”‚ β”‚ ==================== β”‚ β”‚ ==================== β”‚  β”‚
β”‚ β”‚ β€’ Airtel Kenya       β”‚ β”‚ β€’ TechInstall Ltd    β”‚  β”‚
β”‚ β”‚ β€’ Safaricom          β”‚ β”‚ β€’ FieldTech Co       β”‚  β”‚
β”‚ β”‚ β€’ Zuku               β”‚ β”‚ β€’ NetConnect Pro     β”‚  β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                     β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚ β”‚ Active Projects                               β”‚  β”‚
β”‚ β”‚ ============================================= β”‚  β”‚
β”‚ β”‚ Nairobi FTTH Expansion    [β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘] 80%   β”‚  β”‚
β”‚ β”‚ Mombasa Fixed Wireless    [β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘] 50%   β”‚  β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                     β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚ β”‚ Recent Activity                               β”‚  β”‚
β”‚ β”‚ ============================================= β”‚  β”‚
β”‚ β”‚ 10:30 - Client "Safaricom" created            β”‚  β”‚
β”‚ β”‚ 10:15 - User role changed: John β†’ Dispatcher  β”‚  β”‚
β”‚ β”‚ 09:45 - Project "Nairobi FTTH" completed      β”‚  β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Organizations Page

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Organizations          [Clients] [Contractors]      β”‚
β”‚                                    [+ New Client]   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                     β”‚
β”‚ πŸ” Search: [________________]  Filters: [v]        β”‚
β”‚                                                     β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚ β”‚ Name         β”‚ Industry  β”‚ Status β”‚ Created    β”‚β”‚
β”‚ │──────────────┼───────────┼────────┼───────────││
β”‚ β”‚ Airtel Kenya β”‚ Telecom   β”‚ Active β”‚ Jan 2025  β”‚β”‚
β”‚ β”‚ Safaricom    β”‚ Telecom   β”‚ Active β”‚ Feb 2025  β”‚β”‚
β”‚ β”‚ Zuku         β”‚ ISP       β”‚ Active β”‚ Mar 2025  β”‚β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                     β”‚
β”‚ Showing 3 of 15 clients          [1] [2] [3] >    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

3. Users Page

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Users                                 [+ Invite]     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                     β”‚
β”‚ Filters:  Role: [All β–Ό]  Organization: [All β–Ό]    β”‚
β”‚           Status: [Active β–Ό]                       β”‚
β”‚                                                     β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚ β”‚ Name      β”‚ Email        β”‚ Role      β”‚ Org     β”‚β”‚
β”‚ │───────────┼──────────────┼───────────┼─────────││
β”‚ β”‚ John Doe  β”‚ john@ex.com  β”‚ Agent     β”‚ Tech Ltdβ”‚β”‚
β”‚ β”‚ Jane Smithβ”‚ jane@ex.com  β”‚ Dispatcherβ”‚ Tech Ltdβ”‚β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

4. Audit Logs Page

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Audit Logs                         [Export CSV]     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                     β”‚
β”‚ Filters:  Action: [All β–Ό]  Entity: [All β–Ό]        β”‚
β”‚           Date Range: [2025-11-01] to [2025-11-30] β”‚
β”‚                                                     β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚ β”‚ Time   β”‚ User        β”‚ Action β”‚ Entity β”‚ IP    β”‚β”‚
β”‚ │────────┼─────────────┼────────┼────────┼───────││
β”‚ β”‚ 10:30  β”‚ admin@...   β”‚ CREATE β”‚ Client β”‚ 192...β”‚β”‚
β”‚ β”‚ 10:15  β”‚ admin@...   β”‚ UPDATE β”‚ User   β”‚ 192...β”‚β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Navigation Structure

Sidebar Menu:

SwiftOps
─────────────
🏠 Dashboard
πŸ“Š Analytics

Organizations
─────────────
🏒 Clients
πŸ”§ Contractors

Users & Access
─────────────
πŸ‘₯ Users
πŸ“¨ Invitations

Operations
─────────────
πŸ“‹ Projects
🎫 Tickets
πŸ“¦ Inventory

Finance
─────────────
πŸ’° Invoices
πŸ’³ Payments

System
─────────────
πŸ“œ Audit Logs
βš™οΈ Settings
πŸ“€ Exports

Color Scheme

Status Colors:

  • Active/Success: #10b981 (Green)
  • Pending/Warning: #f59e0b (Amber)
  • Inactive/Error: #ef4444 (Red)
  • Info: #3b82f6 (Blue)

Role Colors:

  • Platform Admin: #8b5cf6 (Purple)
  • Client Admin: #3b82f6 (Blue)
  • Contractor Admin: #10b981 (Green)
  • Manager: #f59e0b (Amber)
  • Agent: #6b7280 (Gray)

Responsive Design

Breakpoints:

  • Mobile: < 640px
  • Tablet: 640px - 1024px
  • Desktop: > 1024px

Mobile Considerations:

  • Collapsible sidebar menu
  • Stacked card layouts
  • Horizontal scrolling for tables
  • Bottom navigation bar for key actions

Error Handling

HTTP Status Codes

Code Meaning Action
200 Success Display data
201 Created Show success message, redirect
400 Bad Request Show validation errors
401 Unauthorized Redirect to login
403 Forbidden Show "Access denied" message
404 Not Found Show "Resource not found"
409 Conflict Show "Already exists" error
500 Server Error Show generic error, report to support

Error Response Format

{
  "detail": "Email already registered"
}

Or for validation errors:

{
  "detail": [
    {
      "loc": ["body", "email"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}

Error Handling Example

// API Service with Error Handling
async function createClient(clientData) {
  try {
    const response = await fetch('/api/v1/clients', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${getAccessToken()}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(clientData)
    });
    
    if (!response.ok) {
      if (response.status === 401) {
        // Token expired, redirect to login
        redirectToLogin();
        return;
      }
      
      if (response.status === 409) {
        // Conflict - client already exists
        const error = await response.json();
        showError(error.detail);
        return;
      }
      
      if (response.status === 400) {
        // Validation errors
        const errors = await response.json();
        showValidationErrors(errors.detail);
        return;
      }
      
      // Generic error
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    const client = await response.json();
    showSuccess('Client created successfully!');
    return client;
    
  } catch (error) {
    console.error('Error creating client:', error);
    showError('Failed to create client. Please try again.');
  }
}

// Show error notification
function showError(message) {
  // Use your notification system
  toast.error(message);
}

// Show success notification
function showSuccess(message) {
  toast.success(message);
}

// Show validation errors
function showValidationErrors(errors) {
  if (Array.isArray(errors)) {
    errors.forEach(error => {
      const field = error.loc[error.loc.length - 1];
      toast.error(`${field}: ${error.msg}`);
    });
  } else {
    showError(errors);
  }
}

Token Refresh Strategy

// Intercept 401 responses and refresh token
async function fetchWithAuth(url, options = {}) {
  const token = getAccessToken();
  
  const response = await fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': `Bearer ${token}`
    }
  });
  
  if (response.status === 401) {
    // Try to refresh token
    const refreshed = await refreshAccessToken();
    
    if (refreshed) {
      // Retry original request with new token
      const newToken = getAccessToken();
      return fetch(url, {
        ...options,
        headers: {
          ...options.headers,
          'Authorization': `Bearer ${newToken}`
        }
      });
    } else {
      // Refresh failed, redirect to login
      redirectToLogin();
      throw new Error('Session expired');
    }
  }
  
  return response;
}

async function refreshAccessToken() {
  try {
    const refreshToken = getRefreshToken();
    const response = await fetch('/api/v1/auth/refresh-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refresh_token: refreshToken })
    });
    
    if (response.ok) {
      const data = await response.json();
      setAccessToken(data.access_token);
      return true;
    }
    
    return false;
  } catch (error) {
    console.error('Token refresh failed:', error);
    return false;
  }
}

Implementation Checklist

Phase 1: Core Setup

  • Setup authentication (login/register)
  • Implement token storage and refresh
  • Create API service layer
  • Setup error handling

Phase 2: Dashboard

  • Create dashboard layout
  • Implement statistics cards
  • Add organization summary widgets
  • Add recent activity feed

Phase 3: Organization Management

  • Create clients list page
  • Implement create client form
  • Create client details page
  • Implement client update/delete
  • Create contractors list page
  • Implement create contractor form
  • Create contractor details page
  • Implement contractor update/delete

Phase 4: User Management

  • Create users list page
  • Implement user invitation form
  • Create user details page
  • Implement user update/deactivate
  • Add role change functionality

Phase 5: Monitoring & Logs

  • Create audit logs page
  • Implement log filtering
  • Add export functionality
  • Create system monitoring dashboard

Phase 6: Polish

  • Add loading states
  • Implement pagination
  • Add search functionality
  • Optimize performance
  • Add accessibility features
  • Write unit tests

Best Practices

1. State Management

// Use React Context or Redux for global state
const AuthContext = React.createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    // Check if user is logged in on mount
    const checkAuth = async () => {
      const token = getAccessToken();
      if (token) {
        const userData = await fetchCurrentUser();
        setUser(userData);
      }
      setLoading(false);
    };
    
    checkAuth();
  }, []);
  
  return (
    <AuthContext.Provider value={{ user, setUser, loading }}>
      {children}
    </AuthContext.Provider>
  );
}

2. API Client

// Create a centralized API client
class ApiClient {
  constructor(baseURL) {
    this.baseURL = baseURL;
  }
  
  async request(endpoint, options = {}) {
    const url = `${this.baseURL}${endpoint}`;
    const token = getAccessToken();
    
    const config = {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        ...options.headers,
        ...(token && { 'Authorization': `Bearer ${token}` })
      }
    };
    
    const response = await fetch(url, config);
    
    if (!response.ok) {
      throw await this.handleError(response);
    }
    
    return response.json();
  }
  
  async handleError(response) {
    const error = await response.json();
    return new Error(error.detail || 'An error occurred');
  }
  
  // Convenience methods
  get(endpoint, options) {
    return this.request(endpoint, { ...options, method: 'GET' });
  }
  
  post(endpoint, data, options) {
    return this.request(endpoint, {
      ...options,
      method: 'POST',
      body: JSON.stringify(data)
    });
  }
  
  put(endpoint, data, options) {
    return this.request(endpoint, {
      ...options,
      method: 'PUT',
      body: JSON.stringify(data)
    });
  }
  
  delete(endpoint, options) {
    return this.request(endpoint, { ...options, method: 'DELETE' });
  }
}

const api = new ApiClient('http://localhost:8000/api/v1');
export default api;

3. Custom Hooks

// useClients hook
function useClients(filters = {}) {
  const [clients, setClients] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const fetchClients = async () => {
      try {
        setLoading(true);
        const data = await api.get('/clients', { params: filters });
        setClients(data);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };
    
    fetchClients();
  }, [JSON.stringify(filters)]);
  
  return { clients, loading, error };
}

// Usage
function ClientsPage() {
  const { clients, loading, error } = useClients({ is_active: true });
  
  if (loading) return <Spinner />;
  if (error) return <ErrorMessage error={error} />;
  
  return <ClientsList clients={clients} />;
}

4. Performance Optimization

// Debounce search input
function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);
  
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    
    return () => clearTimeout(timer);
  }, [value, delay]);
  
  return debouncedValue;
}

// Usage
function SearchableList() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearch = useDebounce(searchTerm, 500);
  
  const { data } = useSearch(debouncedSearch);
  
  return (
    <>
      <input 
        value={searchTerm} 
        onChange={(e) => setSearchTerm(e.target.value)} 
      />
      <List data={data} />
    </>
  );
}

Support & Resources

Backend Documentation:

  • API Docs: http://localhost:8000/api/docs (Swagger UI)
  • ReDoc: http://localhost:8000/api/redoc
  • Health Check: http://localhost:8000/health

Related Documentation:

  • User Management: /docs/USER_MANAGEMENT_IMPLEMENTATION.md
  • Organizations API: /docs/dev/ORGANIZATIONS_API_GUIDE.md
  • Auth API: /docs/dev/AUTH_API_GUIDE.md
  • Audit Logging: /docs/agent/PASSWORD_RESET_COMPLETE.md

Contact:


Document Version: 1.0
Last Updated: November 17, 2025
Maintained By: SwiftOps Backend Team