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

16 KiB

Scratchpad — Resume Draft Contract

  • Plan slug: resume-draft-contract
  • Created: 2026-01-23

What This Is

Keep a lightweight, continuously-updated log of discoveries and decisions made while implementing this plan.

Prefer short bullets. Append new entries as you learn things, and also update earlier notes when a decision changes or an open question is resolved.

Decisions

  • (2026-01-23) Resume flow starts at Step 1 — user confirmed they want to review from the beginning when resuming a draft.
  • (2026-01-23) Drafts in separate tab — dedicated "Drafts" tab alongside Templates and Client Contracts.
  • (2026-01-23) No auto-save — manual save only via "Save Draft" button.
  • (2026-01-23) Discard with confirmation — explicit "Discard Draft" action with confirmation dialog.

Discoveries / Constraints

  • (2026-01-23) Draft functionality already exists at DB level:

    • contracts.status has 'draft' value
    • createClientContractFromWizard accepts isDraft option
    • handleSaveDraft() exists in ContractWizard.tsx (lines 381-420)
    • is_active: false + status: 'draft' for draft contracts
  • (2026-01-23) Key files:

    • Contract Wizard: packages/billing/src/components/billing-dashboard/contracts/ContractWizard.tsx
    • Contracts List: packages/billing/src/components/billing-dashboard/contracts/Contracts.tsx
    • Wizard Actions: packages/billing/src/actions/contractWizardActions.ts
    • Contract Actions: packages/billing/src/actions/contractActions.ts
    • Contract Model: packages/billing/src/models/contract.ts
  • (2026-01-23) Current wizard state interface ContractWizardData already has:

    • contract_id?: string — set when draft is saved
    • is_draft?: boolean — draft indicator
    • template_id?: string — template reference
  • (2026-01-23) Contracts.tsx has two tabs: Templates and Client Contracts — we'll add a third "Drafts" tab

  • (2026-01-23) Existing contract update blocks setting status to 'draft' if contract has invoices (contractActions.ts:293-299)

  • (2026-01-24) Contracts page now has third subtab drafts:

    • Added URL mapping for subtab=drafts and a placeholder Drafts tab view in packages/billing/src/components/billing-dashboard/contracts/Contracts.tsx
    • Next: wire Drafts tab to real data/actions + resume/discard UX
  • (2026-01-24) Draft count badge added to Drafts tab:

    • Uses existing loaded clientContracts and counts status === 'draft'
    • Badge is hidden when count is 0 (will align with later acceptance/test expectations)
  • (2026-01-24) Added server action getDraftContracts():

    • Location: packages/billing/src/actions/contractActions.ts
    • Query joins contracts + client_contracts + clients (and template name), filters by tenant + status='draft', orders by updated_at desc
  • (2026-01-24) Drafts tab now renders a Drafts DataTable with Contract Name column:

    • Draft data fetched via getDraftContracts() in packages/billing/src/components/billing-dashboard/contracts/Contracts.tsx
    • Added search input + localized date rendering (will be broken out into checklist items as we complete them)
  • (2026-01-24) Drafts table includes Client column (client_name).

  • (2026-01-24) Drafts table includes Created column (localized date).

  • (2026-01-24) Drafts table includes Last Modified column (localized date).

  • (2026-01-24) Drafts table includes Actions dropdown with Resume/Discard placeholders.

  • (2026-01-24) Drafts table default-sorts by Last Modified (updated_at desc) and supports header sorting via DataTable.

  • (2026-01-24) Drafts tab search filters by contract name.

  • (2026-01-24) Drafts tab search filters by client name.

  • (2026-01-24) Drafts tab uses standard DataTable pagination + items-per-page controls.

  • (2026-01-24) Drafts tab shows empty state when there are no draft contracts.

  • (2026-01-24) Added getDraftContractForResume(contractId) to load full wizard data from a draft:

    • Location: packages/billing/src/actions/contractWizardActions.ts
    • Validates contracts.status === 'draft', then maps contract + client_contract + contract lines/services/configs into DraftContractWizardData
  • (2026-01-24) Resume flow wired from Drafts table:

    • Drafts Actions → Resume calls getDraftContractForResume(), then opens ContractWizard with editingContract data.
    • Updated ContractWizard to re-initialize state when open/editingContract change (was previously only using initial props once).
  • (2026-01-24) Resume always opens wizard on Step 1 (Contract Basics) by resetting currentStep to 0 on open.

  • (2026-01-24) Resumed drafts pre-populate all wizard steps using getDraftContractForResume() mapping (lines/services/configs).

  • (2026-01-24) Wizard now upserts drafts instead of creating duplicates:

    • ClientContractWizardSubmission includes optional contract_id
    • createClientContractFromWizard() updates existing draft when contract_id is provided (clears old lines/configs + rewrites in a transaction)
  • (2026-01-24) Completing a resumed draft activates in-place:

    • createClientContractFromWizard() transitions updated draft to status='active' / is_active=true when called without isDraft
  • (2026-01-24) Discard draft flow now uses a confirmation dialog including contract + client names.

  • (2026-01-24) Discard confirmation includes Cancel + Discard actions (ConfirmationDialog).

  • (2026-01-24) Discard confirmation calls deleteContract(contractId) and shows toast success/error.

  • (2026-01-24) Drafts list refreshes after discard by calling fetchContracts() (badge count updates too).

  • (2026-01-24) Draft queries are tenant-scoped:

    • getDraftContracts() and getDraftContractForResume() filter by { tenant }
    • Draft updates via createClientContractFromWizard() update path are tenant-scoped
  • (2026-01-24) Permissions:

    • Resuming drafts (getDraftContractForResume) requires billing:create + billing:update (unless E2E_AUTH_BYPASS=true)
    • Discarding drafts (deleteContract) requires billing:delete (unless E2E_AUTH_BYPASS=true)

Commands / Runbooks

  • Run billing package tests: npm test -w packages/billing

  • Run server dev: npm run dev -w server

  • (2026-01-24) Implemented T052: discard confirmation dialog message includes permanent deletion + cannot-undo warning (unit test).

  • (2026-01-24) Implemented T053: discard confirmation dialog renders a Cancel button (unit test).

  • (2026-01-24) Implemented T054: discard confirmation dialog renders a Discard button (unit test).

  • (2026-01-24) Implemented T055: Cancel closes discard dialog without calling deleteContract (unit test).

  • (2026-01-24) Implemented T056: Discard confirmation calls deleteContract(contractId) (unit test).

  • (2026-01-24) Implemented T057: confirming discard removes the draft from Drafts list (unit test via refreshed fetch).

  • (2026-01-24) Implemented T058: successful discard shows toast.success('Draft discarded') (unit test with mocked react-hot-toast).

  • (2026-01-24) Implemented T059: discard errors show toast.error(message) (unit test with deleteContract throwing).

  • (2026-01-24) Implemented T060: discard triggers fetchContracts() refresh (assert getDraftContracts called again).

  • (2026-01-24) Implemented T061: Drafts badge count clears after discard (unit test by rendering tabs icon).

  • (2026-01-24) Implemented T062: getDraftContracts tenant filter prevents cross-tenant draft visibility (unit test with configurable tenant).

  • (2026-01-24) Implemented T063: getDraftContractForResume rejects when user lacks billing:create permission.

  • (2026-01-24) Implemented T064: deleteContract rejects when user lacks billing:delete permission.

  • (2026-01-24) Stabilized jsdom test isolation: added cleanup() + body scroll-lock/style reset in wizard/drafts tests to prevent cross-file DOM leakage and pointer-events issues when running the full billing test suite.

  • (2026-01-24) Tests:

    • Added packages/billing/tests/contractsTabs.test.ts to assert the contracts tabs config includes Templates / Client Contracts / Drafts.
    • Extracted tab label mapping into packages/billing/src/components/billing-dashboard/contracts/contractsTabs.ts for unit testing without DOM.
    • Extended packages/billing/tests/contractsTabs.test.ts to assert Drafts label maps to subtab=drafts.
    • Added getDraftTabBadgeCount() helper + test to assert Drafts badge appears when count > 0.
    • Added a test asserting badge behavior as count changes (0 → 1) to represent badge updates after draft creation.
    • Added a test asserting badge hides when count returns to 0 (represents update after discard).
    • Badge hidden at 0 is asserted via getDraftTabBadgeCount(0) === null.
    • Added packages/billing/tests/draftContractsActions.test.ts to unit test getDraftContracts() query constraints (status/tenant/select/order).
    • (2026-01-24) Added packages/billing/tests/draftContractsTable.test.tsx and implemented T012 to assert draft contract names render in the Drafts DataTable (forces DataTable width in jsdom so non-Actions columns are visible).
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T013 to assert client names render in the Drafts DataTable.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T014 to assert created dates render via toLocaleDateString().
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T015 to assert updated dates render via toLocaleDateString().
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T016 to assert each draft row renders an actions menu trigger.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T017 to assert the actions menu includes a Resume option.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T018 to assert the actions menu includes a Discard option.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T019 to assert Drafts table headers trigger sorting (initial updated_at desc then contract_name asc on click).
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T020 to assert draft search filters by contract name.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T021 to assert draft search filters by client name.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T022 to assert draft search is case-insensitive.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T023 to assert Drafts table pagination renders (multiple pages when > page size).
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T024 to assert Drafts pagination can navigate to page 2.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T025 to assert Drafts empty state renders when there are no drafts.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T026 to assert Drafts empty state messaging prompts saving drafts.
    • (2026-01-24) Added packages/billing/tests/draftContractForResumeActions.test.ts and implemented T027 to unit test getDraftContractForResume() returns core wizard data for a draft contract.
    • (2026-01-24) Refactor to keep unit tests lightweight:
      • Added @alga-psa/auth/withAuth export + Vitest alias so billing actions can import withAuth without importing the full @alga-psa/auth entrypoint (which pulls in NextAuth/UI modules in source form).
      • Updated packages/billing/src/actions/contractActions.ts + packages/billing/src/actions/contractWizardActions.ts to import withAuth from @alga-psa/auth/withAuth.
      • In resume tests, mocked @alga-psa/auth/getCurrentUser to avoid NextAuth session code paths when loading billing models.
    • (2026-01-24) Extended packages/billing/tests/draftContractForResumeActions.test.ts and implemented T028 to assert contract lines map into wizard service arrays.
    • (2026-01-24) Extended packages/billing/tests/draftContractForResumeActions.test.ts and implemented T029 to assert service configuration fields (rates, unit_of_measure, bucket overlays) map into resume payload.
    • (2026-01-24) Extended packages/billing/tests/draftContractForResumeActions.test.ts and implemented T030 to assert non-draft contracts cannot be resumed.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T031 to assert clicking Resume opens the wizard dialog (via mocked ContractWizard).
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T032 to assert the Resume flow passes draft data into the wizard (contract_id/client_id/contract_name).
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T049 to assert clicking Discard opens the confirmation dialog.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T050 to assert discard confirmation includes the contract name.
    • (2026-01-24) Extended packages/billing/tests/draftContractsTable.test.tsx and implemented T051 to assert discard confirmation includes the client name.
    • (2026-01-24) Added packages/billing/tests/contractWizardResume.test.tsx and implemented T033 to assert resuming a draft starts the wizard on Step 1 (Contract Basics) without rendering later steps.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T034 to assert Contract Basics step receives the draft client selection.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T035 to assert Contract Basics step receives the draft contract name.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T036 to assert Contract Basics step receives the draft dates.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T037 to assert Fixed Fee step receives pre-populated fixed services when navigating forward from basics.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T038 to assert Products step receives pre-populated product services when navigating forward.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T039 to assert Hourly step receives pre-populated hourly services when navigating forward.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T040 to assert Usage step receives pre-populated usage services when navigating forward.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T041 to assert Review step receives the full draft payload when navigating forward.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T042 to assert Save Draft submits contract_id + {isDraft:true} when resuming.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T043 to assert Save Draft keeps the existing contract_id (preventing duplicate creation).
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T044 to assert Save Draft reports the same contract_id via onComplete.
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T045 to assert finishing a resumed draft calls createClientContractFromWizard without {isDraft:true} (activation path).
    • (2026-01-24) Extended packages/billing/tests/contractWizardResume.test.tsx and implemented T046 to assert finishing a resumed draft does not pass {isDraft:true} (meaning is_active is set true in the action).
    • (2026-01-24) Added packages/billing/tests/contractsActivationFlow.test.tsx and implemented T047 to assert a resumed/activated contract disappears from Drafts after wizard completion + refresh.
    • (2026-01-24) Extended packages/billing/tests/contractsActivationFlow.test.tsx and implemented T048 to assert activated contracts appear in Client Contracts after switching tabs.
  • PRD: ./PRD.md
  • Contract Wizard: packages/billing/src/components/billing-dashboard/contracts/ContractWizard.tsx
  • Contracts List: packages/billing/src/components/billing-dashboard/contracts/Contracts.tsx
  • Wizard Actions: packages/billing/src/actions/contractWizardActions.ts

Open Questions

  • (None currently — key decisions made during planning)