100 lines
3.4 KiB
Markdown
100 lines
3.4 KiB
Markdown
# Plan: Rename Cascade + Duplicate Check
|
|
|
|
## Features
|
|
1. After renaming a person/project/company, update all @mentions in existing text
|
|
2. On rename: prevent duplicate names of the same type
|
|
|
|
---
|
|
|
|
## Affected Files
|
|
|
|
| File | Change |
|
|
|------|--------|
|
|
| `client/src/lib/utils/mentionReplace.ts` | NEW — `renameMentions(text, oldName, newName, type)` |
|
|
| `client/src/lib/db/repositories.ts` | ADD `renameMentionCascade(oldName, newName, type)` |
|
|
| `client/src/lib/components/ContextHeader.svelte` | UPDATE `saveEdit()` — duplicate check + cascade; add warning UI |
|
|
|
|
---
|
|
|
|
## New File: `mentionReplace.ts`
|
|
|
|
Pure function, no Dexie dependency.
|
|
|
|
```typescript
|
|
export function renameMentions(
|
|
text: string,
|
|
oldName: string,
|
|
newName: string,
|
|
type: 'person' | 'project' | 'company'
|
|
): string
|
|
```
|
|
|
|
**Regex patterns to replace:**
|
|
|
|
| Type | Unquoted | Quoted |
|
|
|------|----------|--------|
|
|
| person | `@OldName` | `@"Old Name"` |
|
|
| project | `@P:OldName` / `@p:OldName` | `@P:"Old Name"` |
|
|
| company | `@F:OldName` / `@f:OldName` | `@F:"Old Name"` |
|
|
| assignment (person only) | `-> OldName` | n/a (not supported by extractors) |
|
|
|
|
**Replacement:** use `quoteMention`-logic — add quotes if newName contains spaces.
|
|
|
|
**Boundary condition:** match must end at `[\w"]` boundary to avoid partial matches (e.g. `@Alice` must not match `@AliceBob`).
|
|
|
|
---
|
|
|
|
## `renameMentionCascade` in repositories.ts
|
|
|
|
Run in a single Dexie transaction (`rw` on historyEntries, topics, contexts, ratings).
|
|
|
|
**Fields to scan:**
|
|
|
|
| Table | Field | Notes |
|
|
|-------|-------|-------|
|
|
| `historyEntries` | `text` | Primary mention location |
|
|
| `topics` | `title` | Inline mentions possible |
|
|
| `contexts` | `meta.notes` | PersonMeta / ProjectMeta / CompanyMeta only |
|
|
| `ratings` | `comment` | Optional, may have @mentions |
|
|
| `ratings` | `personName` | Structured field, person rename only (exact match) |
|
|
|
|
For each changed record: increment `version`, update `updatedAt`.
|
|
|
|
Pattern to follow: `renameTitleInLinks()` already exists in repositories.ts (line ~541).
|
|
|
|
---
|
|
|
|
## `ContextHeader.svelte` — `saveEdit()` changes
|
|
|
|
```
|
|
1. trimmed = nameInput.trim()
|
|
2. if trimmed === editableName() → return (no-op)
|
|
3. if type is person/project/company:
|
|
- check contextNameExists(fullName(trimmed), type)
|
|
- if exists AND id !== context.id → show duplicate warning, revert, return
|
|
4. oldName = editableName()
|
|
5. await upsertContext({ id, name: fullName(trimmed) })
|
|
6. if type is person/project/company:
|
|
- await renameMentionCascade(oldName, trimmed, type)
|
|
```
|
|
|
|
Add `duplicateWarning` state + small error message below input.
|
|
|
|
---
|
|
|
|
## Sequencing
|
|
|
|
1. `mentionReplace.ts` — implement + manual regex tests
|
|
2. `repositories.ts` — add `renameMentionCascade`
|
|
3. `ContextHeader.svelte` — wire up
|
|
|
|
---
|
|
|
|
## Open Questions
|
|
|
|
1. **`-> Name` with spaces:** `extractors.ts` arrow pattern only matches `[\w]+`. If renamed from single-word to multi-word, arrow assignments break silently. Extend extractors to support `-> "First Last"`, or accept as edge case?
|
|
2. **`EventMeta.participants`:** Plain `string[]`, not mention syntax. Update on person rename?
|
|
3. **Case-only rename** (Alice → ALICE): `contextNameExists` is case-insensitive — will incorrectly block. Need to exclude self from duplicate check.
|
|
4. **Performance:** Full table scan (no text index). Acceptable for local-first small datasets — add a comment.
|
|
5. **Sync conflicts:** Cascade causes many `version++`. Last-write-wins acceptable, or need special marker?
|