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

239 lines
8.2 KiB
Markdown

# PRD — Resume Draft Contract
- Slug: `resume-draft-contract`
- Date: `2026-01-23`
- Status: Draft
## Summary
Enable users to save client contracts as drafts during the wizard flow and later resume configuring them. Add a dedicated "Drafts" tab in the contracts management area that displays all draft contracts with clear actions to resume editing or discard them.
## Problem
Currently, users creating complex client contracts through the 6-step wizard must complete the entire process in one session. If they:
- Get interrupted and close the browser
- Need to gather more information before completing
- Want to save work-in-progress for later
...they lose all their progress. While the backend already supports saving contracts with `status: 'draft'`, there's no clear UI path to find and resume these draft contracts. Drafts are mixed in with active contracts and there's no obvious "Resume" action.
## Goals
- Provide a dedicated "Drafts" tab in the Contracts management area to list all draft contracts
- Allow users to resume editing a draft contract, starting from Step 1 of the wizard
- Allow users to discard (delete) a draft contract with confirmation
- Show clear visual indication that contracts in the drafts tab are incomplete
- Preserve the existing "Save Draft" button functionality in the wizard
## Non-goals
- Auto-save functionality (manual save only)
- Smart resume to a specific step (always start at Step 1)
- Draft versioning or history
- Draft sharing or collaboration features
- Draft expiration or auto-cleanup
- Draft contracts for templates (templates already have their own draft system via `template_status`)
## Users and Primary Flows
### Persona: MSP Billing Administrator
**Flow 1: Saving a Draft**
1. User starts creating a new client contract via the wizard
2. User completes some steps but isn't ready to finalize
3. User clicks "Save Draft" button
4. System saves contract with `status: 'draft'` and confirms success
5. User can close the wizard
**Flow 2: Viewing Drafts**
1. User navigates to Contracts management area
2. User clicks on "Drafts" tab (alongside Templates and Client Contracts)
3. User sees list of all draft contracts with:
- Contract name
- Client name
- Created date
- Last modified date
- Actions (Resume, Discard)
**Flow 3: Resuming a Draft**
1. User clicks "Resume" action on a draft contract
2. Wizard opens at Step 1 (Contract Basics) with all saved data pre-populated
3. User can navigate through steps, reviewing/modifying as needed
4. User either:
- Saves as draft again (overwrites existing draft)
- Completes and activates the contract
**Flow 4: Discarding a Draft**
1. User clicks "Discard" action on a draft contract
2. Confirmation dialog appears: "Are you sure you want to discard this draft? This cannot be undone."
3. User confirms
4. Draft is deleted from the system
## UX / UI Notes
### Contracts Page Tabs
Add third tab to existing layout:
```
[Templates] [Client Contracts] [Drafts]
```
### Drafts Tab Content
- DataTable with columns:
- Contract Name
- Client
- Created
- Last Modified
- Actions (dropdown menu)
- Actions menu items:
- "Resume" - opens wizard with draft data
- "Discard" - shows confirmation, then deletes
- Empty state: "No draft contracts. Start creating a new contract to save as draft."
### Drafts Tab Visual Indicators
- Badge/count on tab showing number of drafts: `Drafts (3)`
- Optional: "Draft" badge next to contract name in list
### Resume Experience
- When resuming, wizard opens as a dialog/drawer (same as creating new)
- Step 1 pre-populated with saved draft data
- All subsequent steps also pre-populated
- "Save Draft" button updates the existing draft (not creating new)
- "Cancel" button closes without saving (confirmation if changes made)
- Completing wizard converts draft to active contract
### Discard Confirmation Dialog
```
Discard Draft Contract?
This will permanently delete the draft "{contract_name}" for {client_name}.
This action cannot be undone.
[Cancel] [Discard]
```
## Requirements
### Functional Requirements
**FR1: Drafts Tab**
- Add "Drafts" tab to the Contracts management area (third tab after Templates and Client Contracts)
- Tab shows count of draft contracts as badge
- Tab contains DataTable listing all draft contracts for the tenant
**FR2: Drafts List**
- Display contract name, client name, created date, last modified date
- Include actions dropdown with Resume and Discard options
- Support sorting by columns (default: last modified, descending)
- Support search/filter by contract name or client name
- Respect existing pagination patterns
**FR3: Resume Draft**
- "Resume" action opens the ContractWizard dialog
- Wizard loads with all saved draft data pre-populated
- User starts at Step 1 (Contract Basics)
- Existing "Save Draft" functionality updates the same draft
- Completing wizard activates the contract (changes `status` from `'draft'` to `'active'`)
**FR4: Discard Draft**
- "Discard" action shows confirmation dialog
- Confirming deletes the contract and all associated data
- Deletion uses existing `deleteContract` action
- List refreshes after successful deletion
**FR5: Backend Support**
- Create action to fetch draft contracts only: `getDraftContracts()`
- Ensure `updateContract` properly handles draft → active transition
- Ensure existing draft save in wizard properly updates (not creates new)
### Non-functional Requirements
- Drafts tab loads within acceptable time (<500ms for typical list sizes)
- Resume action loads wizard with data within acceptable time
- Delete operation completes within acceptable time with proper error handling
## Data / API / Integrations
### New Action Functions
```typescript
// In contractActions.ts or new draftContractActions.ts
// Fetch all draft contracts for the current tenant
export const getDraftContracts = withAuth(async (
user,
{ tenant }
): Promise<ContractWithClient[]> => {
const { knex } = await createTenantKnex();
return knex('contracts')
.join('client_contracts', 'contracts.contract_id', 'client_contracts.contract_id')
.join('companies', 'client_contracts.client_id', 'companies.company_id')
.where('contracts.tenant', tenant)
.where('contracts.status', 'draft')
.select(
'contracts.*',
'companies.company_name as client_name',
'client_contracts.client_id'
)
.orderBy('contracts.updated_at', 'desc');
});
// Get a single draft contract with full data for resuming
export const getDraftContractForResume = withAuth(async (
user,
{ tenant },
contractId: string
): Promise<ContractWizardData> => {
// Fetch contract, lines, services, configurations
// Transform to ContractWizardData format for wizard consumption
});
```
### Interface Updates
```typescript
// Add to contract.interfaces.ts if not present
interface ContractWithClient extends IContract {
client_name: string;
client_id: string;
}
```
### Wizard Updates
The ContractWizard already accepts `editingContract` prop this will be used for resuming drafts. May need to enhance to handle full draft data reload.
## Security / Permissions
- Drafts follow existing contract permissions
- Users who can create contracts can save/resume drafts
- Users who can delete contracts can discard drafts
- All queries filter by tenant to maintain multi-tenant isolation
## Rollout / Migration
- No database migration required `status: 'draft'` already exists
- Feature is additive no breaking changes
- Existing drafts (if any) will appear in the new Drafts tab immediately
## Open Questions
None key decisions resolved during planning phase.
## Acceptance Criteria (Definition of Done)
- [ ] "Drafts" tab appears in Contracts management alongside Templates and Client Contracts
- [ ] Drafts tab displays count badge showing number of drafts
- [ ] Drafts list shows contract name, client, created date, modified date, and actions
- [ ] "Resume" action opens wizard with draft data pre-populated at Step 1
- [ ] Saving draft again updates the existing draft (no duplicate creation)
- [ ] Completing the wizard from a resumed draft activates the contract
- [ ] "Discard" action shows confirmation dialog
- [ ] Confirming discard deletes the draft contract
- [ ] Empty state displays appropriately when no drafts exist
- [ ] List supports sorting and search/filter