Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

7.7 KiB

PRD: Mobile App — Documents, Products, and Avatars

Problem Statement

The Alga PSA mobile app is missing three capabilities that exist in the web app:

  1. Documents — No way to view, download, or attach files to tickets from mobile. Technicians in the field need to photograph issues and attach documentation.
  2. Products/Materials — No way to add inventory items (products) to tickets. Technicians need to record parts used during on-site work.
  3. Contact & Client Avatars — Ticket detail shows contact and client names as plain text without avatars/logos, making it harder to quickly identify entities.

Goals

  • Allow mobile users to view attached documents and upload new files (photos + files) to tickets
  • Allow mobile users to add products from the catalog to tickets as materials
  • Show contact avatar and client logo in the ticket detail view

Non-Goals

  • Document editing/renaming from mobile
  • Document preview/inline viewing (just list + download + open)
  • Multi-currency product pricing (use default rate)
  • Product creation from mobile (only pick existing products)
  • Deleting materials from mobile (can be added later)

Target Users

Field technicians and support staff who use the mobile app to manage tickets on the go.


Feature 15: Contact & Client Avatars in Ticket Detail

Overview

Add contact_avatar_url and client_logo_url to the ticket detail API response, then display them in the mobile ticket detail UI next to the contact and client names.

Server Changes

  1. In TicketService.getById(), after fetching the ticket, call:
    • getContactAvatarUrl(ticket.contact_name_id, context.tenant) if contact exists
    • getClientLogoUrl(ticket.client_id, context.tenant) if client exists
  2. Return both URLs in the response as contact_avatar_url and client_logo_url

Mobile Changes

  1. In TicketDetailScreen.tsx, render <Avatar> next to the contact name in the KeyValue component
  2. Render <Avatar> next to the client name
  3. No type changes needed (TicketDetail uses Record<string, unknown>)

Acceptance Criteria

  • Contact avatar shows next to contact name (or initials fallback)
  • Client logo shows next to client name (or initials fallback)
  • No avatar/logo fields = graceful fallback (no crash, just initials)

Feature 10: Documents on Tickets

Overview

Show document list in ticket detail with count, allow downloading, and allow uploading new files (camera photos and file picker).

Server Changes

  1. New endpoint: POST /api/v1/tickets/{id}/documents

    • Accepts multipart/form-data with file
    • Uses StorageService.uploadFile() to store
    • Creates document record + association with ticket
    • Returns the created IDocument
    • Follow pattern from uploadDocument() server action
  2. Existing endpoint: GET /api/v1/tickets/{id}/documents

    • Already exists and works — returns IDocument[]

Mobile Changes

  1. API layer (src/api/documents.ts):

    • getTicketDocuments(ticketId) — GET endpoint
    • uploadTicketDocument(ticketId, file) — POST with FormData
  2. Documents section in ticket detail:

    • New DocumentsSection component below description
    • Shows document count badge
    • Lists documents: name, type icon, size, upload date
    • Tap document to download/open via Linking.openURL or expo-sharing
    • "Attach" button with options: Camera (expo-image-picker) or File (expo-document-picker)
    • Upload progress indicator
  3. Dependencies: expo-image-picker, expo-document-picker, expo-file-system

Data Model (from server)

IDocument {
  document_id, document_name, type_name, type_icon,
  mime_type, file_size, created_by_full_name, updated_at,
  file_id (for download URL)
}

Acceptance Criteria

  • Document list visible in ticket detail with count
  • Tap to download/open a document
  • Upload photo from camera
  • Upload file from file picker
  • Upload shows progress, refreshes list on completion
  • Empty state when no documents

Feature 11: Products/Materials on Tickets

Overview

Allow mobile users to add products (inventory items) to tickets as materials, recording quantity and rate.

Server Changes

  1. New endpoint: GET /api/v1/tickets/{id}/materials

    • Returns ITicketMaterial[] for the ticket
    • Joins service_catalog for service_name and sku
  2. New endpoint: POST /api/v1/tickets/{id}/materials

    • Accepts: { service_id, quantity, rate, currency_code, description? }
    • Requires ticket's client_id (fetched internally)
    • Returns created ITicketMaterial
  3. Existing endpoint: GET /api/v1/products

    • Already exists — used for product search/listing

Mobile Changes

  1. API layer (src/api/materials.ts):

    • getTicketMaterials(ticketId) — GET
    • addTicketMaterial(ticketId, data) — POST
    • Product listing already available via GET /api/v1/products
  2. Materials section in ticket detail:

    • New MaterialsSection component
    • Lists existing materials: product name, SKU, quantity, rate, billed status
    • "Add Product" button opens product picker (EntityPickerModal with search)
    • After selecting product, prompt for quantity (default 1) and rate (pre-filled from product default_rate)
    • Submit creates material
  3. Product picker:

    • Reuse EntityPickerModal with search
    • Show product name + SKU as subtitle
    • After selection, show quantity/rate input modal

Data Model

ITicketMaterial {
  ticket_material_id, service_id, service_name, sku,
  quantity, rate (cents), currency_code, description,
  is_billed, created_at
}

Acceptance Criteria

  • Materials list visible in ticket detail
  • Add product via searchable picker
  • Set quantity and rate before adding
  • Billed/unbilled status badge shown
  • Empty state when no materials

Risks

  • Document upload endpoint doesn't exist yet — needs server-side implementation following the existing uploadDocument pattern
  • Materials endpoints don't exist yet — need two new API routes
  • expo-image-picker and expo-document-picker may need Expo plugin configuration in app.json
  • Large file uploads on mobile networks may timeout — consider reasonable file size limits

Testing Requirements

Each feature must include actual test files that are written and verified to pass. The mobile app uses Vitest with React Test Renderer. Run tests with npx vitest run from ee/mobile/.

Feature 15 Tests

  • ee/mobile/src/screens/TicketDetailScreen.avatars.test.ts — Verify Avatar components render with contact/client URLs from ticket data, and fallback to initials when URLs are null

Feature 10 Tests

  • ee/mobile/src/api/documents.test.ts — Unit tests for getTicketDocuments() and uploadTicketDocument() API wrappers (mock API client)
  • ee/mobile/src/features/ticketDetail/components/DocumentsSection.test.ts — Verify document list renders items, empty state, upload trigger

Feature 11 Tests

  • ee/mobile/src/api/materials.test.ts — Unit tests for getTicketMaterials(), addTicketMaterial(), listProducts() API wrappers (mock API client)
  • ee/mobile/src/features/ticketDetail/components/MaterialsSection.test.ts — Verify materials list renders items with product name/SKU/quantity/rate, empty state, add product flow

Test Execution

After implementing each feature, run npx vitest run from ee/mobile/ and verify all tests pass (both new and existing). Fix any failures before moving to the next feature. The existing test suite has 126 tests that must continue to pass.

Implementation Order

  1. Feature 15 (avatars) — smallest, server + mobile UI only, no new endpoints
  2. Feature 10 (documents) — medium, needs new upload endpoint
  3. Feature 11 (materials) — medium, needs two new endpoints + product picker