# Project Manager Dashboard - API Reference **Version:** 1.0 **Last Updated:** 2025-11-19 **Audience:** Frontend Development Team --- ## Overview This document provides all API endpoints needed to build a comprehensive Project Manager (PM) dashboard. The PM role requires visibility across sales, operations, finance, and resources to orchestrate daily field service operations. ### PM Core Responsibilities - Convert sales orders to work tickets - Assign tickets to field agents - Approve expenses and manage finances - Track inventory and resources - Generate payroll and invoices - Monitor team performance --- ## Dashboard Layout Structure ### Recommended Sections 1. **Today's Overview** - Key metrics at a glance 2. **Financial Snapshot** - Cash position and pending payments 3. **Quick Actions** - Common PM tasks 4. **Pending Sales Orders** - Orders awaiting conversion 5. **Active Tickets** - Work in progress 6. **Pending Approvals** - Expenses, payroll, invoices 7. **Inventory Status** - Stock levels and alerts 8. **Map View** - Geographic visualization 9. **Team Status** - Agent availability --- ## API Endpoints by Feature ### 1. Dashboard Overview Statistics #### Get Platform Dashboard Stats ``` GET /api/v1/analytics/platform-admin/dashboard Authorization: Bearer {token} ``` **Response Fields:** - `users` - User statistics (total, active, by role) - `organizations` - Client and contractor counts - `tickets` - Ticket counts by status and type - `projects` - Project statistics - `assignments` - Assignment statistics - `recent_activity` - Latest 10 audit logs - `system_health` - 30-day trends **Use Case:** Main dashboard metrics widget --- ### 2. Sales Order Management #### List Sales Orders ``` GET /api/v1/sales-orders Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50, max: 100) - project_id (UUID, optional) - status (string, optional) - "pending", "processed", "cancelled", "duplicate" - project_region_id (UUID, optional) - sales_agent_id (UUID, optional) - from_date (date, optional) - to_date (date, optional) - pending_processing (boolean, optional) - Show only pending orders - customer_phone (string, optional) ``` **Response:** - `items[]` - Array of sales orders with computed properties - `total` - Total count - `skip`, `limit` - Pagination info **Key Fields per Item:** - `id`, `order_number`, `customer_name`, `customer_preferred_package` - `package_price`, `installation_address_line1`, `installation_latitude`, `installation_longitude` - `status`, `project_title`, `region_name`, `sales_agent_name` - `is_pending`, `has_ticket`, `can_promote_to_ticket` #### Get Sales Order Statistics ``` GET /api/v1/sales-orders/stats Authorization: Bearer {token} Query Parameters: - project_id (UUID, optional) - project_region_id (UUID, optional) - sales_agent_id (UUID, optional) ``` **Response:** Metrics on orders by status, region, agent #### Assign Sales Order to Region ``` POST /api/v1/sales-orders/{sales_order_id}/assign-region Authorization: Bearer {token} Content-Type: application/json Body: { "project_region_id": "uuid" } ``` #### Promote Sales Order to Ticket (Single) ``` POST /api/v1/sales-orders/{sales_order_id}/promote Authorization: Bearer {token} Content-Type: application/json Body: { "priority": "normal", "scheduled_date": "2025-01-20", "scheduled_time_slot": "morning", "work_description": "Install fiber", "notes": "Customer prefers morning" } ``` **Response:** - `message`, `sales_order_id`, `ticket_id`, `ticket_name`, `status` #### Bulk Promote Sales Orders to Tickets ``` POST /api/v1/sales-orders/bulk-promote Authorization: Bearer {token} Content-Type: application/json Body: { "sales_order_ids": ["uuid1", "uuid2"], "priority": "normal", "notes": "Batch promotion" } ``` **Response:** - `total`, `successful`, `failed`, `errors[]`, `created_ticket_ids[]` --- ### 3. Ticket Management #### List Tickets ``` GET /api/v1/tickets Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50, max: 100) - project_id (UUID, optional) - status (enum, optional) - "open", "assigned", "in_progress", "pending_review", "completed", "cancelled" - ticket_type (enum, optional) - "installation", "support", "infrastructure" - priority (enum, optional) - "low", "normal", "high", "urgent" - project_region_id (UUID, optional) - source (enum, optional) - "sales_order", "incident", "task" - from_date (date, optional) - to_date (date, optional) - is_overdue (boolean, optional) ``` **Response:** - `tickets[]` - Array of tickets - `total`, `skip`, `limit` **Key Fields per Ticket:** - `id`, `ticket_name`, `status`, `priority`, `ticket_type` - `work_description`, `scheduled_date`, `scheduled_time_slot` - `project_title`, `region_name`, `customer_name` - `is_overdue`, `can_be_assigned` #### Get Ticket Statistics ``` GET /api/v1/tickets/stats Authorization: Bearer {token} Query Parameters: - project_id (UUID, optional) - project_region_id (UUID, optional) ``` **Response:** - Counts by status, type, priority - `overdue_tickets`, `avg_completion_time_hours`, `completion_rate` #### Update Ticket ``` PUT /api/v1/tickets/{ticket_id} Authorization: Bearer {token} Content-Type: application/json Body: { "priority": "urgent", "work_description": "Updated description", "notes": "Additional notes" } ``` #### Reschedule Ticket ``` POST /api/v1/tickets/{ticket_id}/reschedule Authorization: Bearer {token} Content-Type: application/json Body: { "scheduled_date": "2025-01-25", "scheduled_time_slot": "afternoon", "reason": "Customer requested later date" } ``` #### Cancel Ticket ``` POST /api/v1/tickets/{ticket_id}/cancel Authorization: Bearer {token} Content-Type: application/json Body: { "reason": "Customer cancelled service" } ``` --- ### 4. Ticket Assignment & Dispatch #### List Ticket Assignments ``` GET /api/v1/ticket-assignments Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50) - ticket_id (UUID, optional) - user_id (UUID, optional) - status (string, optional) - project_id (UUID, optional) - from_date (date, optional) - to_date (date, optional) ``` #### Get Available Agents for Assignment ``` GET /api/v1/ticket-assignments/available-agents Authorization: Bearer {token} Query Parameters: - ticket_id (UUID, required) ``` **Response:** List of agents available to be assigned to the ticket #### Assign Ticket to Agent ``` POST /api/v1/ticket-assignments Authorization: Bearer {token} Content-Type: application/json Body: { "ticket_id": "uuid", "user_id": "uuid", "notes": "Assigned to John" } ``` #### Reassign Ticket ``` POST /api/v1/ticket-assignments/{assignment_id}/reassign Authorization: Bearer {token} Content-Type: application/json Body: { "new_user_id": "uuid", "reason": "Original agent unavailable" } ``` --- ### 5. Expense Management #### List Expenses ``` GET /api/v1/expenses Authorization: Bearer {token} Query Parameters: - page (int, default: 1) - page_size (int, default: 50, max: 100) - ticket_id (UUID, optional) - assignment_id (UUID, optional) - approval_status (string, optional) - "pending", "approved", "rejected" - payment_status (string, optional) - "pending", "paid" - from_date (date, optional) - to_date (date, optional) ``` **Response:** - `items[]` - Array of expenses - `total`, `page`, `page_size` **Key Fields per Expense:** - `id`, `expense_type`, `amount`, `currency` - `description`, `receipt_url` - `approval_status`, `payment_status` - `submitted_by_name`, `approved_by_name` #### Get Expense Statistics ``` GET /api/v1/expenses/stats Authorization: Bearer {token} Query Parameters: - ticket_id (UUID, optional) - assignment_id (UUID, optional) ``` **Response:** Total expenses, pending approvals, payment status breakdown #### Approve/Reject Expense ``` POST /api/v1/expenses/{expense_id}/approve Authorization: Bearer {token} Content-Type: application/json Body: { "approved": true, "notes": "Approved - receipt verified" } ``` #### Mark Expense as Paid ``` POST /api/v1/expenses/{expense_id}/mark-paid Authorization: Bearer {token} Content-Type: application/json Body: { "payment_date": "2025-01-20", "payment_method": "mobile_money", "payment_reference": "TXN123456", "notes": "Paid via M-Pesa" } ``` --- ### 6. Financial Management #### List Financial Transactions ``` GET /api/v1/finance Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50, max: 100) - project_id (UUID, optional) - transaction_type (string, optional) - "inflow", "outflow" - status (string, optional) - "pending", "approved", "paid", "rejected", "cancelled" - category (string, optional) - from_date (date, optional) - to_date (date, optional) - pending_approval (boolean, optional) ``` #### Get Cash Flow Summary ``` GET /api/v1/finance/cash-flow/{project_id} Authorization: Bearer {token} Query Parameters: - from_date (date, optional) - to_date (date, optional) ``` **Response:** - Total inflows, outflows, net cash flow - Breakdown by category - Pending transactions #### Create Financial Transaction ``` POST /api/v1/finance Authorization: Bearer {token} Content-Type: application/json Body: { "project_id": "uuid", "transaction_type": "outflow", "amount": 50000, "currency": "KES", "category": "equipment_purchase", "description": "Purchased 50 ONTs", "transaction_date": "2025-01-20" } ``` #### Approve Transaction ``` POST /api/v1/finance/{transaction_id}/approve Authorization: Bearer {token} Content-Type: application/json Body: { "notes": "Approved by PM" } ``` --- ### 7. Contractor Invoice Management #### List Contractor Invoices ``` GET /api/v1/invoices Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 100, max: 500) - contractor_id (UUID, optional) - client_id (UUID, optional) - project_id (UUID, optional) - status (string, optional) - "draft", "sent", "partially_paid", "paid", "cancelled" - overdue_only (boolean, optional) - unpaid_only (boolean, optional) - latest_versions_only (boolean, default: true) ``` **Response:** - `invoices[]` - Array of invoices - `total`, `skip`, `limit` **Key Fields per Invoice:** - `id`, `invoice_number`, `contractor_name`, `client_name` - `subtotal_amount`, `tax_amount`, `total_amount` - `amount_paid`, `amount_due` - `status`, `due_date`, `is_overdue` #### Create Contractor Invoice ``` POST /api/v1/invoices Authorization: Bearer {token} Content-Type: application/json Body: { "contractor_id": "uuid", "client_id": "uuid", "project_id": "uuid", "invoice_date": "2025-01-20", "due_date": "2025-02-20", "currency": "KES", "tax_rate": 16.0, "notes": "Monthly invoice" } ``` **Response:** Created invoice with auto-generated invoice number #### Add Line Item to Invoice ``` POST /api/v1/invoices/{invoice_id}/line-items Authorization: Bearer {token} Content-Type: application/json Body: { "line_item_type": "ticket", "ticket_id": "uuid", "description": "Installation service", "quantity": 1, "unit_price": 5000, "amount": 5000 } ``` **Note:** Creates new invoice version #### Record Payment ``` POST /api/v1/invoices/{invoice_id}/payments Authorization: Bearer {token} Content-Type: application/json Body: { "amount": 50000, "payment_date": "2025-01-20", "payment_method": "bank_transfer", "payment_reference": "TXN123456", "notes": "Partial payment" } ``` **Response:** Updated invoice with new payment recorded --- ### 8. Inventory Management #### List Main Office Inventory ``` GET /api/v1/inventory Authorization: Bearer {token} Query Parameters: - page (int, default: 1) - page_size (int, default: 50, max: 100) - project_id (UUID, optional) - item_type (string, optional) - "tool", "equipment", "consumable", "ppe" - status (string, optional) - is_active (boolean, optional) - search (string, optional) ``` **Response:** - `items[]` - Inventory batches - `total`, `page`, `page_size` **Key Fields per Item:** - `id`, `equipment_type`, `equipment_name`, `item_type` - `quantity_received`, `quantity_available`, `quantity_distributed` - `status`, `received_date` #### Create Inventory Batch ``` POST /api/v1/inventory Authorization: Bearer {token} Content-Type: application/json Body: { "project_id": "uuid", "equipment_type": "ONT", "equipment_name": "Huawei HG8145V5", "item_type": "equipment", "quantity_received": 100, "received_date": "2025-01-20", "supplier_name": "TechSupply Ltd", "unit_cost": 2500, "currency": "KES" } ``` #### List Regional Distributions ``` GET /api/v1/inventory/distributions Authorization: Bearer {token} Query Parameters: - page (int, default: 1) - page_size (int, default: 50) - project_id (UUID, optional) - region_id (UUID, optional) - inventory_id (UUID, optional) - item_type (string, optional) - is_active (boolean, optional) ``` **Response:** Inventory distributed to regional hubs #### Distribute Inventory to Regional Hub ``` POST /api/v1/inventory/distributions Authorization: Bearer {token} Content-Type: application/json Body: { "inventory_id": "uuid", "project_region_id": "uuid", "quantity_distributed": 20, "distributed_date": "2025-01-20", "notes": "Distribution to Nairobi hub" } ``` #### List Inventory Assignments (Issued to Agents) ``` GET /api/v1/inventory/assignments Authorization: Bearer {token} Query Parameters: - page (int, default: 1) - page_size (int, default: 50) - user_id (UUID, optional) - region_id (UUID, optional) - status (string, optional) - is_returned (boolean, optional) - ticket_id (UUID, optional) ``` **Response:** Equipment issued to field agents --- ### 9. Payroll Management #### List Payroll Records ``` GET /api/v1/payroll Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50, max: 100) - user_id (UUID, optional) - project_id (UUID, optional) - period_start (date, optional) - period_end (date, optional) - status (string, optional) ``` **Response:** - `items[]` - Payroll records - `total`, `skip`, `limit` **Key Fields per Record:** - `id`, `user_name`, `period_start`, `period_end` - `total_earnings`, `deductions`, `net_pay` - `status`, `payment_status` #### Generate Payroll ``` POST /api/v1/payroll/generate Authorization: Bearer {token} Content-Type: application/json Body: { "project_id": "uuid", "period_start": "2025-01-01", "period_end": "2025-01-31", "user_ids": ["uuid1", "uuid2"] } ``` **Response:** Generated payroll records #### Approve Payroll ``` POST /api/v1/payroll/{payroll_id}/approve Authorization: Bearer {token} Content-Type: application/json Body: { "notes": "Approved for payment" } ``` --- ### 10. Timesheet Management #### List Timesheets ``` GET /api/v1/timesheets Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50, max: 100) - user_id (UUID, optional) - project_id (UUID, optional) - from_date (date, optional) - to_date (date, optional) - status (string, optional) - "present", "absent", "on_leave", "sick_leave", "half_day" ``` #### Get Timesheet Statistics ``` GET /api/v1/timesheets/stats Authorization: Bearer {token} Query Parameters: - user_id (UUID, optional) - project_id (UUID, optional) - from_date (date, optional) - to_date (date, optional) ``` **Response:** Attendance metrics, days worked, absences --- ### 11. User Management #### List Users ``` GET /api/v1/users Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50, max: 100) - role (string, optional) - client_id (UUID, optional) - contractor_id (UUID, optional) - is_active (boolean, optional) - status (string, optional) - search (string, optional) ``` **Response:** - `users[]` - Array of users - `total`, `skip`, `limit` #### Search Users ``` GET /api/v1/users/search Authorization: Bearer {token} Query Parameters: - q (string, required) - Search query - role (string, optional) - limit (int, default: 20) ``` **Response:** Matching users (useful for assignment dropdowns) #### Invite New User ``` POST /api/v1/invitations Authorization: Bearer {token} Content-Type: application/json Body: { "email": "john@example.com", "name": "John Doe", "phone": "+254712345678", "role": "field_agent", "contractor_id": "uuid" } ``` --- ### 12. Map & Location Services #### Get Map Entities ``` GET /api/v1/map/entities Authorization: Bearer {token} Query Parameters: - project_id (UUID, required) - entity_types (array, optional) - ["customers", "tickets", "sales_orders", "agents", "regions"] - status (string, optional) ``` **Response:** - `customers[]` - Customer locations - `tickets[]` - Ticket locations - `sales_orders[]` - Sales order locations - `agents[]` - Agent current locations - `regions[]` - Regional hub locations **Use Case:** Render all entities on map view #### Get Regional Hub Locations ``` GET /api/v1/map/regions/{project_id} Authorization: Bearer {token} Query Parameters: - include_inactive (boolean, default: false) ``` **Response:** Regional hubs with coordinates, coverage radius, active tickets/agents count #### Get Journey Details ``` GET /api/v1/map/journey/{assignment_id} Authorization: Bearer {token} ``` **Response:** - Timeline (start, arrival, completion times) - GPS breadcrumb trail (array of coordinates with timestamps) - Journey statistics (distance, duration, speed) - Location information **Use Case:** Track agent movement during work assignment #### Find Nearest Regional Hub ``` GET /api/v1/map/nearest-region Authorization: Bearer {token} Query Parameters: - project_id (UUID, required) - latitude (float, required) - longitude (float, required) - max_distance_km (float, optional) ``` **Response:** Nearest regional hub with distance --- ### 13. Task Management #### List Tasks ``` GET /api/v1/tasks Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50, max: 100) - project_id (UUID, optional) - status (string, optional) - "pending", "assigned", "in_progress", "completed", "cancelled", "blocked" - assigned_to (UUID, optional) - priority (string, optional) - from_date (date, optional) - to_date (date, optional) ``` #### Create Task ``` POST /api/v1/tasks Authorization: Bearer {token} Content-Type: application/json Body: { "project_id": "uuid", "task_type": "installation", "title": "Install fiber cable", "description": "Install cable from pole A to B", "priority": "high", "due_date": "2025-01-25", "assigned_to": "uuid" } ``` --- ### 14. Customer Management #### List Customers ``` GET /api/v1/customers Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50, max: 100) - client_id (UUID, optional) - search (string, optional) - phone (string, optional) ``` #### Get Customer Details ``` GET /api/v1/customers/{customer_id} Authorization: Bearer {token} ``` **Response:** Customer profile with all related data (subscriptions, tickets, sales orders) --- ### 15. Notifications #### List Notifications ``` GET /api/v1/notifications Authorization: Bearer {token} Query Parameters: - skip (int, default: 0) - limit (int, default: 50) - is_read (boolean, optional) - type (string, optional) ``` #### Get Unread Count ``` GET /api/v1/notifications/unread-count Authorization: Bearer {token} ``` **Response:** `{ "count": 5 }` #### Mark Notification as Read ``` PUT /api/v1/notifications/{notification_id}/read Authorization: Bearer {token} ``` --- ## Authentication All endpoints require Bearer token authentication: ``` Authorization: Bearer {access_token} ``` **Token Refresh:** ``` POST /api/v1/auth/refresh Content-Type: application/json Body: { "refresh_token": "your_refresh_token" } ``` **Response:** ```json { "access_token": "new_access_token", "refresh_token": "new_refresh_token", "token_type": "bearer", "expires_in": 3600 } ``` --- ## Error Handling All endpoints follow standard HTTP status codes: - `200 OK` - Success - `201 Created` - Resource created - `204 No Content` - Success with no response body - `400 Bad Request` - Invalid input - `401 Unauthorized` - Missing or invalid token - `403 Forbidden` - Insufficient permissions - `404 Not Found` - Resource not found - `409 Conflict` - Duplicate or conflicting resource - `422 Unprocessable Entity` - Validation error - `500 Internal Server Error` - Server error **Error Response Format:** ```json { "detail": "Error message here" } ``` --- ## Pagination List endpoints use offset-based pagination: **Request:** - `skip` - Number of items to skip (default: 0) - `limit` - Number of items to return (default: 50, max varies by endpoint) **Response:** - `items[]` or `users[]` or similar - Array of results - `total` - Total count of items - `skip` - Current offset - `limit` - Current limit --- ## Date/Time Formats - **Dates:** ISO 8601 format `YYYY-MM-DD` (e.g., `2025-01-20`) - **DateTimes:** ISO 8601 with timezone `YYYY-MM-DDTHH:MM:SSZ` (e.g., `2025-01-20T14:30:00Z`) - **Time Slots:** `"morning"`, `"afternoon"`, `"evening"`, `"any"` --- ## Filtering Best Practices 1. **Combine filters** - Most list endpoints support multiple filters simultaneously 2. **Use pagination** - Always paginate large result sets 3. **Cache where appropriate** - Dashboard stats can be cached for 1-5 minutes 4. **Real-time updates** - Use polling or WebSockets for live data (tickets, assignments) --- ## Common UI Patterns ### Dashboard Widgets - Fetch stats on page load - Refresh every 30-60 seconds for live data - Show loading skeletons during fetch ### List Views - Implement infinite scroll or traditional pagination - Add filters in sidebar or top bar - Show total count and current range ### Map Views - Lazy load entities based on viewport - Cluster markers when zoomed out - Show details on marker click ### Approval Workflows - Show pending count badge - Provide bulk approve/reject actions - Confirm destructive actions (reject, cancel) --- ## Performance Recommendations 1. **Lazy load** - Don't fetch all data at once 2. **Debounce searches** - Wait 300ms before searching 3. **Cache reference data** - Roles, statuses, regions rarely change 4. **Optimize map rendering** - Use clustering for large datasets 5. **Batch operations** - Use bulk endpoints when available --- ## Support & Questions For API questions or issues: - Backend Team Lead: [Contact Info] - API Documentation: `/api/v1/docs` (Swagger UI) - Base URL: `https://api.swiftops.example.com` --- **End of Document**