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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
5.2 KiB
5.2 KiB
PRD: Project Materials Drawer
Problem Statement
Projects currently have a placeholder "Materials" button that opens a drawer displaying only an alert message: "Project materials are now owned by Billing." Users cannot manage materials (products) for projects, even though:
- The backend infrastructure already exists (
listProjectMaterials,addProjectMaterial,deleteProjectMaterial) - The data model is complete (
IProjectMaterialinterface,project_materialstable) - Tickets have a fully functional materials card that users expect to work similarly for projects
This creates an inconsistent UX where materials work for tickets but not for projects.
Goals
- Implement fully functional Project Materials Drawer - Allow users to view, add, and delete materials for projects
- Maintain consistency with Ticket Materials - Same interaction patterns, multi-currency support, and validation rules
- Use drawer layout - As specified, materials management should be in a slide-out drawer (not an inline card like tickets)
Non-Goals
- Bulk material operations (add multiple at once)
- Material editing (only add/delete, same as tickets)
- Export/import materials
- Material templates or presets
- Project-specific pricing overrides (uses service catalog prices)
Architecture Compliance
Package Location
Keep in @alga-psa/projects/components - Following the established pattern where TicketMaterialsCard lives in @alga-psa/tickets.
Required Dependency Addition
Add @alga-psa/billing to packages/projects/package.json:
"dependencies": {
"@alga-psa/billing": "*",
// ... existing deps
}
Import Pattern (Same as TicketMaterialsCard)
import {
listProjectMaterials,
addProjectMaterial,
deleteProjectMaterial,
searchServiceCatalogForPicker,
getServicePrices,
} from '@alga-psa/billing/actions';
Target Users
- MSP Administrators - Managing project-based work for clients
- Project Managers - Tracking materials used on projects for billing
User Flow
Primary Flow: View Project Materials
- User navigates to a project detail page
- User clicks "Materials" button in project header
- Drawer slides in from right showing materials list or empty state
Secondary Flow: Add Material
- User clicks "Add" button
- Selects product, currency (if multi-currency), quantity
- Optionally adds description
- Clicks "Add Material" → material appears in list
Secondary Flow: Delete Material
- User clicks delete (trash icon) on unbilled material
- Material removed, toast shown
UX/UI Specification
Drawer Layout
- Width: 560px (standard detail drawer width)
- Position: Right side slide-in
Drawer Structure
┌─────────────────────────────────────┐
│ Materials [Add] │
├─────────────────────────────────────┤
│ [Add Form - when visible] │
├─────────────────────────────────────┤
│ Product | Qty | Rate | Total | Stat │
│ ─────────────────────────────────── │
│ Item 1 | 2 | $50 | $100 | Pend │
├─────────────────────────────────────┤
│ Unbilled (USD): $100.00 │
└─────────────────────────────────────┘
Data Model (Existing)
interface IProjectMaterial {
project_material_id: string;
project_id: string;
client_id: string;
service_id: string;
service_name?: string;
sku?: string | null;
quantity: number;
rate: number; // In cents
currency_code: string; // ISO 4217
description?: string | null;
is_billed: boolean;
// ... timestamps
}
API/Actions (Existing in @alga-psa/billing/actions)
| Action | Purpose |
|---|---|
listProjectMaterials(projectId) |
Fetch materials with service_name/sku joined |
addProjectMaterial({...}) |
Create material (rate in cents) |
deleteProjectMaterial(id) |
Delete if not billed |
searchServiceCatalogForPicker({...}) |
Get products for dropdown |
getServicePrices(serviceId) |
Get multi-currency prices |
Acceptance Criteria
- Drawer opens when "Materials" button clicked
- Materials displayed in table with all fields
- Add form with product picker, currency, quantity, description
- Successfully adds material and refreshes list
- Delete button removes unbilled materials
- Unbilled totals grouped by currency
- Warning when project has no client
- Error handling with toast notifications
Files to Modify
packages/projects/package.json- Add@alga-psa/billingdependencypackages/projects/src/components/ProjectMaterialsDrawer.tsx- Complete implementation
Reference Implementation
packages/tickets/src/components/ticket/TicketMaterialsCard.tsx - Follow this pattern exactly, adapted for drawer layout.