Sets up the schema + service primitives the rest of the nested-
document-subfolders feature will build on (master UAT line 728+).
This commit is INFRASTRUCTURE ONLY — the upload-zone scope radio,
lifecycle hooks for outcome rename, aggregated-projection list
query, and backfill script are deferred to follow-up commits.
Schema (migration 0078_files_interest_id.sql):
- `files.interest_id` text REFERENCES interests(id) ON DELETE SET
NULL. Mirrors the existing documents.interest_id; lets file
uploads be scoped to a deal while still rolling up to the parent
client folder.
- idx_files_interest + idx_files_port_interest for the aggregated-
projection queries that will surface "This deal" vs "From
client" file lists.
Service:
- EntityType extended to include 'interest'. Interest folders parent
under the owning client's entity folder (not at a system root), so
the tree reads Clients/Acme/Deal A1-A3/ — nested.
- ensureEntityFolder recursively ensures the parent client folder
first when given an interest, guaranteeing the deal folder lands
inside the right client subfolder even when the first artifact on
the deal predates any client-level upload.
- resolveEntityDisplayName for interest: "Deal — <mooringNumber>"
(when a primary berth is linked) or "Deal <YYYY-MM-DD>" as the
stable fallback. Dynamic-import on getPrimaryBerth dodges the
circular dep between document-folders.service and
interest-berths.service.
Aggregated projection (files.ts):
- listFilesAggregatedByEntity SELECT now includes the new
interest_id column so AggregatedFileRow's structural type matches.
Downstream consumers gain access to the deal scope; the actual
"From this deal" subheading in InterestDocumentsTab is wired in
the follow-up.
Remaining work (tracked in master UAT line 728+, parked for next
session):
- UploadZone `scopeOptions` radio (single-option pickers hide the
radio entirely for client/yacht/company surfaces).
- Lifecycle hooks for interest outcome → folder rename ("Deal
A1-A3 (Won)") via soft-rescue per CLAUDE.md.
- listFilesAggregatedByEntity rewrite to surface "This deal" vs
"From client" subheadings on InterestDocumentsTab.
- Documents Hub tree rendering for nested interest folders.
- backfill script: existing files with entity_type='interest' +
entity_id but missing interest_id column → populate.
Verified: tsc clean, vitest 1448/1448 after dev-DB migration applied.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
31 lines
1.2 KiB
SQL
31 lines
1.2 KiB
SQL
-- Phase 1 of the nested document subfolders feature (master UAT line 728+):
|
|
--
|
|
-- 1. Add a nullable `interest_id` column to `files` so uploads scoped
|
|
-- to a deal can be filed under the interest subfolder while still
|
|
-- rolling up to the parent client folder. Mirrors the existing
|
|
-- `documents.interest_id` semantics: scoping FK that holds a
|
|
-- "from-interest" attribution even when the parent client/yacht/
|
|
-- company shifts.
|
|
--
|
|
-- 2. Index on `(port_id, interest_id)` for the aggregated-projection
|
|
-- queries that will surface "this-deal" files vs "from-client" files
|
|
-- in InterestDocumentsTab.
|
|
--
|
|
-- 3. Soft FK (`ON DELETE SET NULL`) so a hard-deleted interest doesn't
|
|
-- orphan the file — the audit trail stays intact and the file remains
|
|
-- findable under the parent client folder.
|
|
--
|
|
-- Apply in dev:
|
|
-- PGPASSWORD=changeme psql -h localhost -p 5434 -U crm -d port_nimara_crm \
|
|
-- -f src/lib/db/migrations/0078_files_interest_id.sql
|
|
|
|
ALTER TABLE files
|
|
ADD COLUMN IF NOT EXISTS interest_id text
|
|
REFERENCES interests(id) ON DELETE SET NULL;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_files_interest
|
|
ON files (interest_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_files_port_interest
|
|
ON files (port_id, interest_id);
|