Some checks failed
✅ Check / 🔬 ESLint (push) Failing after 21s
✅ Check / 🏷️ Typecheck (push) Failing after 11s
✅ Check / 🔎 Test (push) Failing after 11s
✅ Check / e2e-test (push) Failing after 11s
✅ Check / 🔨 Build (push) Failing after 11s
🚀 Deploy / 🚀 Deploy (doc) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (demo) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (supabase) (push) Failing after 18s
6.0 KiB
6.0 KiB
Deleting contacts
The spine of the CRM and the widest deletion of the five. Notes attach to contacts | deals, tasks belong only to contacts, tags is a field on Contact, companies aggregate nb_contacts, deals link contact_ids, and the activity-log + JSON-import + merge subsystems all revolve around it. Removing it orphans notes/tasks/tags.
Confirm cascade scope first
Ask the user how far to cascade — the answer changes the whole job:
- contacts only — leave notes/tasks/tags as compiling shells (narrowing the notes union to
"deals"keeps notes valid as long as deals exists; tasks/tags become orphaned shells), or - also delete the now-orphaned subsystems.
- And whether to touch the backend.
Frontend specifics
- Shared subsystems — narrow the notes union to
"deals"and the activity-log union to drop"contact". DeleteActivityLogContactCreated.tsx+ActivityLogContactNoteCreated.tsxoutright; dropgetNewContactsAndNotesinproviders/commons/activity.ts. - Typing landmine:
DealNote.statusis typedundefined(a deliberate ContactNote-compat marker intypes.ts), so retypingNoteInputs'suseFormContext<ContactNote | DealNote>down to<DealNote>collapsesstatustoneverandtscrejects the status-hydrationsetValue("status", …). Widen it back withuseFormContext<Omit<DealNote, "status"> & { status?: string }>(). - Contact-only mobile note components (dead once contacts is gone) — delete outright:
NoteCreateSheet.tsx,NoteEditSheet.tsx,NoteShowPage.tsx,NotesIteratorMobile.tsx,NoteInputsMobile.tsx(+ their stories/tests). Drop theNotesIteratorMobilere-export fromnotes/index.tsand clean importers (MobileNavigation.tsx, the mobile<Resource name="contacts">note route inCRM.tsx). - Links to contacts:
deals.contact_ids(a selector inDealInputs.tsx, a per-deal<ContactList/>inDealShow.tsx+ the standalonedeals/ContactList.tsxto delete, the "create a contact first" gate inDealEmpty.tsx) andtasks.contact_id(the whole task contact-linking UI:AddTask,Task,TaskCreateSheet,TaskEditSheet,TaskFormContent'sselectContact, theshowContact/filterByContactthreading inTasksIterator/TasksListFilter/TasksListByDueDate). Decide per-link: orphaned shell (contact_ids: Identifier[], default[]) vs drop. - Custom method:
mergeContacts(sourceId, targetId)in both providers (FakeRest →providers/commons/mergeContacts.ts, delete it; supabase → themerge_contactsedge function). AlsoprocessContactAvatar/getContactAvatar(deleteproviders/commons/getContactAvatar.ts+ test), thecontacts/contact_notes+tasksnb_taskscallbacks, and the supabasecontacts -> contacts_summaryrouting +contact_note -> contactNoteremap. - Wrapper components outside the folder, delete outright:
dashboard/HotContacts.tsx(contacts widget),dashboard/DashboardStepper.tsx(contact-onboarding wizard).Dashboard.tsx/MobileDashboard.tsxgate the whole render on contact/contactNote counts via the stepper — drop that gating.LatestNotes.tsxmixes contact_notes + a<Contact>renderer;dashboard/TasksList.tsxhas an<AddTask selectContact/>. - JSON importer is heavily contact-coupled (
importContact/importNoteforcontact_notes,contactGender, stats/idsMaps); alsoimport-sample.json(drop the contacts + notes sections).ContactOption.tsxis a shared option renderer — delete it. - FakeRest generators: delete
contacts.ts,contactNotes.ts,finalize.tsoutright; cleanindex.ts/types.ts;deals.ts(contact_ids),tasks.ts(contact_id + nb_tasks),companies.ts(nb_contacts seed). - Layout/login:
Header.tsx,MobileNavigation.tsx,SignupPage.tsx(redirectTo: "/contacts"),StoryWrapper.tsx(buildContact+ the contacts/contact_notes db keys).
Backend
- Drop the
contacts+contact_notestables, thetasks.contact_idcolumn (FK + index), thecontacts_summaryview, and the functionsmerge_contacts,handle_contact_saved,handle_contact_note_created_or_updatedoutright — plus their triggers/policies/grants and thecontacts_id_seq/"contactNotes_id_seq"grants. - Orphaned helpers:
get_avatar_for_email(only called byhandle_contact_saved) andlowercase_email_jsonb(only wired to the contact email trigger) — remove them + grants. activity_logview carries the contact pattern (acontact+contact_notecolumn on every branch and two dedicated branchescontact.created/contactNote.created) — same shape as deals.- Edge functions (deepest of the five): delete the whole
merge_contacts/function (+ its[functions.merge_contacts]block inconfig.toml) and the wholepostmark/inbound-email function (+ its[functions.postmark]block) — this leaves the inbound-email settings UI inProfilePage/SettingsPageMobileand thePOSTMARK_*/VITE_INBOUND_EMAILenv vars hollow; flag that to the user. In_shared/db.tsdropContactsTable/ContactNotesTable, thetasks.contact_idfield, and theirDatabaseregistrations. Inmcp/:contacts(not tasks) drivesCRM_BASE_URL— thedisplay_task_listcontact_name/contact_idschema fields andtaskListUi.ts's contact-link rendering exist only to deep-link task→contact, so removing them makesCRM_BASE_URL(inmcp/index.ts, the__CRM_BASE_URL__HTML placeholder, andsupabase/functions/.env) dead — drop it. Rewrite thecontacts/contacts_summarymentions in theget_schema/query/mutateprompt examples and swap the table name inmcp/validateSql.test.tsfixtures for a surviving table.
grep — benign matches to leave (intentional)
resources.companies.field_categories.contact(a company "Company info" section label inCompanyInputs.tsx/CompanyAside.tsx, not the contacts resource), theresource="contacts"JSDoc examples inmisc/CreateSheet.tsx/EditSheet.tsx, and the now-unusedresources.contacts.*i18n keys (optional/large — flag, don't force).supabase/migrations/keeps historical references — never edit them.