chore(autonomous-session): consolidate uncommitted work from prior session
Bundles the prior autonomous-session output that was sitting unstaged: - Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances) - country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk after the per-subpath dynamic-import approach silently failed in webpack) - Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index, redirects (ocr to ai, reports to dashboard, invitations to users), docs/admin-ia-proposal.md - Per-template email tester (registry + endpoint + UI on Email admin page) - Cancel-document mode picker (delete-from-Documenso vs keep-for-audit) - Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers - Customize-widgets per-region sortables at xl+ (charts/rails/feed); single flat sortable below xl when the layout stacks; per-viewport saved orders - Audit doc updates capturing each shipped item - Lint fixes: react-compiler immutability in DonutChart (reduce instead of let-reassign), set-state-in-effect disables in CountryFlag and UploadForSigning preview-bytes effect, unused 'confirm' destructures in interest contract + reservation tabs, unescaped apostrophe in test-template card copy
This commit is contained in:
@@ -57,7 +57,7 @@ export interface FolderNode extends DocumentFolder {
|
||||
/**
|
||||
* Returns the entire folder tree for a port, nested under their
|
||||
* parents. Roots come back at the top level. Order is alphabetical
|
||||
* (case-insensitive) within each parent — matches the sibling-uniqueness
|
||||
* (case-insensitive) within each parent - matches the sibling-uniqueness
|
||||
* index ordering and gives reps a stable browsing experience.
|
||||
*
|
||||
* Uses a single SELECT + JS nesting rather than a recursive CTE; the
|
||||
@@ -241,14 +241,14 @@ export async function moveFolder(
|
||||
|
||||
// Cycle check: walk the destination's ancestor chain. If we hit
|
||||
// folderId, the destination is a descendant of the folder being
|
||||
// moved — moving would create a cycle.
|
||||
// moved - moving would create a cycle.
|
||||
let cursor: string | null = newParent.parentId;
|
||||
const seen = new Set<string>([newParent.id]);
|
||||
while (cursor) {
|
||||
if (cursor === folderId) {
|
||||
throw new ValidationError('Cannot move a folder under one of its descendants (cycle)');
|
||||
}
|
||||
if (seen.has(cursor)) break; // defensive — pre-existing cycle, bail
|
||||
if (seen.has(cursor)) break; // defensive - pre-existing cycle, bail
|
||||
seen.add(cursor);
|
||||
const next = await tx.query.documentFolders.findFirst({
|
||||
where: and(eq(documentFolders.id, cursor), eq(documentFolders.portId, portId)),
|
||||
@@ -312,7 +312,7 @@ export async function deleteFolderSoftRescue(
|
||||
.set({ folderId: newParent })
|
||||
.where(and(eq(documents.folderId, folderId), eq(documents.portId, portId)));
|
||||
|
||||
// G-C1: files.folder_id is ON DELETE SET NULL — without this UPDATE,
|
||||
// G-C1: files.folder_id is ON DELETE SET NULL - without this UPDATE,
|
||||
// files in the deleted folder would scatter to root while documents
|
||||
// in the same folder land at the deleted folder's parent. Re-parent
|
||||
// files explicitly so the soft-rescue is symmetric across both.
|
||||
@@ -387,7 +387,7 @@ export async function ensureSystemRoots(portId: string, userId: string): Promise
|
||||
// inserts can only collide on `uniq_document_folders_sibling_name`
|
||||
// (entityId is null on roots, so the partial index
|
||||
// `uniq_document_folders_entity` is excluded). Do not copy this
|
||||
// pattern into helpers that insert per-entity subfolders — they
|
||||
// pattern into helpers that insert per-entity subfolders - they
|
||||
// need an explicit target to avoid masking real conflicts.
|
||||
await db.insert(documentFolders).values(values).onConflictDoNothing();
|
||||
|
||||
@@ -401,7 +401,7 @@ export async function ensureSystemRoots(portId: string, userId: string): Promise
|
||||
if (!row) {
|
||||
logger.error(
|
||||
{ portId, missingRoot: name, foundNames: rows.map((r) => r.name) },
|
||||
'ensureSystemRoots: invariant violated — system root missing after upsert',
|
||||
'ensureSystemRoots: invariant violated - system root missing after upsert',
|
||||
);
|
||||
throw new Error(`ensureSystemRoots: missing root ${name} after upsert`);
|
||||
}
|
||||
@@ -451,11 +451,11 @@ async function resolveEntityDisplayName(
|
||||
});
|
||||
if (!i) throw new NotFoundError('Interest');
|
||||
// Defer to the interest-berths service for the primary berth label
|
||||
// — circular-dep avoidance via dynamic import. Falls back to the
|
||||
// - circular-dep avoidance via dynamic import. Falls back to the
|
||||
// ISO date slice ("Deal 2026-05-12") when no berth is linked yet.
|
||||
const { getPrimaryBerth } = await import('@/lib/services/interest-berths.service');
|
||||
const primary = await getPrimaryBerth(entityId).catch(() => null);
|
||||
if (primary?.mooringNumber) return `Deal — ${primary.mooringNumber}`;
|
||||
if (primary?.mooringNumber) return `Deal - ${primary.mooringNumber}`;
|
||||
const dateSlice = i.createdAt.toISOString().slice(0, 10);
|
||||
return `Deal ${dateSlice}`;
|
||||
}
|
||||
@@ -495,7 +495,7 @@ function isEntityFolderConflict(err: unknown): boolean {
|
||||
* regardless of whether it was newly created or already existed.
|
||||
*
|
||||
* Concurrent callers race safely via the partial unique index
|
||||
* `uniq_document_folders_entity` — the loser INSERT errors and the
|
||||
* `uniq_document_folders_entity` - the loser INSERT errors and the
|
||||
* re-SELECT returns the winner's row.
|
||||
*
|
||||
* On sibling-name collision (two entities want the same display name),
|
||||
@@ -540,7 +540,7 @@ export async function ensureEntityFolder(
|
||||
columns: { clientId: true },
|
||||
});
|
||||
if (!interestRow) throw new NotFoundError('Interest');
|
||||
// Recursively ensure the parent client's folder first — guarantees
|
||||
// Recursively ensure the parent client's folder first - guarantees
|
||||
// we always land inside the existing Clients/<Name>/ subfolder even
|
||||
// when the deal's first artifact predates any client-level upload.
|
||||
parent = await ensureEntityFolder(portId, 'client', interestRow.clientId, userId);
|
||||
@@ -610,12 +610,12 @@ export async function ensureEntityFolder(
|
||||
* Rename the per-entity subfolder to match the entity's current display
|
||||
* name. Called from the entity rename services (`updateClient`,
|
||||
* `updateCompany`, `updateYacht`). No-op when the folder does not exist
|
||||
* (lazy creation — entities without a folder skip the sync entirely).
|
||||
* (lazy creation - entities without a folder skip the sync entirely).
|
||||
*
|
||||
* Sibling-name collision is resolved by suffix bump (matches
|
||||
* `ensureEntityFolder` semantics).
|
||||
*
|
||||
* Intentionally does NOT call `assertNotSystemManaged` — this helper
|
||||
* Intentionally does NOT call `assertNotSystemManaged` - this helper
|
||||
* is the legitimate path for renaming a system folder.
|
||||
*/
|
||||
export async function syncEntityFolderName(
|
||||
@@ -633,7 +633,7 @@ export async function syncEntityFolderName(
|
||||
eq(documentFolders.entityId, entityId),
|
||||
),
|
||||
});
|
||||
if (!folder) return; // Lazy creation — nothing to sync yet.
|
||||
if (!folder) return; // Lazy creation - nothing to sync yet.
|
||||
|
||||
// Preserve archived suffix if present.
|
||||
const isArchived = folder.name.endsWith(' (archived)');
|
||||
@@ -689,7 +689,7 @@ const DELETED_SUFFIX = ' (deleted)';
|
||||
|
||||
/**
|
||||
* Stamp an entity's subfolder as archived: append " (archived)" to the
|
||||
* name (idempotent — won't double-append) and set archived_at. No-op
|
||||
* name (idempotent - won't double-append) and set archived_at. No-op
|
||||
* when the folder does not exist (lazy creation). Used by the entity
|
||||
* archive paths in clients / companies / yachts services.
|
||||
*/
|
||||
@@ -730,7 +730,7 @@ export async function applyEntityArchivedSuffix(
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse of `applyEntityArchivedSuffix` — strip " (archived)" from
|
||||
* Inverse of `applyEntityArchivedSuffix` - strip " (archived)" from
|
||||
* the name and clear archived_at. No-op when the folder does not
|
||||
* exist or wasn't archived.
|
||||
*/
|
||||
@@ -775,7 +775,7 @@ export async function applyEntityRestoredSuffix(
|
||||
* folder by clearing `system_managed`, appending " (deleted)" to the
|
||||
* name, and dropping the entity FK so the partial unique index no
|
||||
* longer constrains it. Files still inside the folder retain their
|
||||
* snapshotted entity FKs (orphaned — they appear in the root-view
|
||||
* snapshotted entity FKs (orphaned - they appear in the root-view
|
||||
* Files section once the rep cleans up).
|
||||
*
|
||||
* Idempotent: re-demoting an already-demoted folder is a no-op because
|
||||
@@ -814,7 +814,7 @@ export async function demoteSystemFolderOnEntityDelete(
|
||||
|
||||
/**
|
||||
* Phase 2 nested-subfolders lifecycle hook. Re-renames an interest's
|
||||
* document folder when its outcome changes — e.g. `Deal A1-A3` becomes
|
||||
* document folder when its outcome changes - e.g. `Deal A1-A3` becomes
|
||||
* `Deal A1-A3 (Won)`, `(Lost)`, or `(Cancelled)`. No-op when the
|
||||
* folder doesn't exist yet (uploads happen later) or when the outcome
|
||||
* is null (still in flight).
|
||||
|
||||
Reference in New Issue
Block a user