* docs: add contacts feature implementation plan
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8dd82cae-8bc6-4eaa-9b90-95073720c577
* feat(api): add Contact entity and ContactProperties jsonb type
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(api): wrap contact property errors
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(api): add ContactDetails to message threads
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(api): add ContactRepository with GORM implementation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(api): add contact store/update/index requests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(api): keep contact sanitization ordered
Move sanitizeUniqueStrings into the shared requests helper.
Preserve first-seen ordering after normalization.
Add a direct ToContacts nil-properties regression test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(api): add contact validator
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(api): accept common CSV MIME types
Allow .csv extension only; accept common browser MIME fallbacks for uploads.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(api): add ContactService with cached contact-map resolution
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(api): opt in contact resolution
Add contacts query parsing and optional ContactDetails enrichment through a contact map provider.
DI passes nil until Task 8 wires ContactService.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(api): add ContactHandler and wire contacts into DI container
- Add ContactHandler with authenticated /v1/contacts routes:
GET/POST /v1/contacts, POST /v1/contacts/upload,
PUT/DELETE /v1/contacts/:contactID.
- Load user-scoped contact before Update/Delete so a missing or
cross-user contact returns 404 (avoids the repo's zero-row Delete
returning 204 while advertising 404).
- Support one-and-many JSON create via ContactStoreRequest and
reuse ContactService.CreateMany so cache invalidation stays
single-shot for Store and CSV upload alike.
- Add DI getters ContactRepository, ContactService,
ContactHandlerValidator, ContactHandler and RegisterContactRoutes;
register the routes alongside RegisterMessageThreadRoutes.
- Finalise Task 7 by injecting container.ContactService() into
MessageThreadService, replacing the temporary nil dependency.
- Cover all five routes with real Fiber requests plus a shared
ContactRepository fake that verifies service/repository effects,
and pin the DI wiring with a compile-time test that constructs
MessageThreadService with the ContactService.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore(api): regenerate contacts swagger
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(api): mark contact fields optional
Make emails/properties optional in contact request DTOs.
Regenerate Swagger docs and add request JSON regression tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore(web): regenerate API models with Contact types
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(web): add contacts Pinia store
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(web): avoid false contact reload errors
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(web): add contacts management page with add/edit/delete/import dialogs
Add the /contacts page grounded in the existing Vuetify theme.
Header uses text-display-large; debounced search drives the API ?query= param.
VDataTable columns: Name, Phone Numbers, Emails, Created, Updated, Actions.
Rows show avatar initials, phone chips and relative timestamps via humanizeTime.
Add/edit/delete/import-CSV dialogs use opacity 0.9 and warning-colored Close controls.
Add/Edit has repeatable phone numbers, emails and free-form properties, preserved on edit.
CSV import surfaces row-indexed API validation errors inline, not only via a toast.
loadContacts forwards the trimmed search value as the query param, keeping Task 11's contract.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(web): guard stale contacts loads
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(web): display contact names on threads
Preserve contact details across mark-read updates.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(api): return accurate contacts total for server pagination
Extend the contact repository/service/handler so GET /v1/contacts returns
a top-level total count for the same user/query filter, independent of
skip/limit, enabling true server-side pagination.
- add ContactRepository.Count reusing a shared scopedContactQuery helper
so Index and Count filters can never drift; Count ignores limit/offset
- add ContactService.Count and handler responseOKWithTotal; Index returns total
- add Total to responses.ContactsResponse and regenerate Swagger
- sanitize parsed CSV rows before validation so CSV and JSON accept the
same phone/email formats; drop the now-redundant re-sanitize on upload
- assert Scan error behaviour instead of the unexported stacktrace type
- tests for count filter parity, total propagation, and CSV sanitization
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(web): server-side pagination for the contacts page
- store total now reflects the server count; loadContacts accepts skip and
limit and remembers the current window so mutation refreshes stay in place
- contacts page uses VDataTableServer with items-length bound to the server
total; page/size changes fetch the correct skip/limit and a debounced
query resets to page 1 without duplicate requests
- delete bumps the load generation so an in-flight load cannot resurrect a
deleted contact, without leaving loading stuck or hiding delete errors
- remove the dead filteredContacts computed and add defensive null
coalescing for emails/phone_numbers in the table display
- regenerate API models with the new contacts total field
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(web): mark contacts headers unsortable
Remove misleading sort affordances from the contacts table.
Keep the subtitle aligned with the active search state.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: add contacts integration coverage
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1f3addc9-71dd-4cb7-bfae-e6fdcc8511af
* fix(web): prefix Vuetify composables
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(web): define contacts header styling
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(web): refine contacts table
Improve the contacts table layout, actions, relative timestamps, and header emphasis.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(web): define contact phone input
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(web): improve contact dialog fields
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: display of buttons on contacts page
* feat(api): limit contacts by plan
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: updated mcp.json to remove axiom and playwright
* fix(web): update reactive page composables
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a93bfbb-739a-414c-8eb4-48b15a69fb9d
* fix: align contacts sorting and caching
Propagate contact table sorting through the API and repository while
keeping contact lookups on the process-local cache.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dec094f9-5386-4607-998a-1701ac10b7cf
* Update api/pkg/repositories/gorm_contact_repository.go
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix(api): delete contacts with user account
Subscribe contact cleanup to account deletion events so user contact data
is removed from storage and invalidated from the contact-map cache.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dec094f9-5386-4607-998a-1701ac10b7cf
* fix(api): restore contact repository build
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dec094f9-5386-4607-998a-1701ac10b7cf
* perf(contacts): bound thread contact lookups
Fetch and cache only phone numbers present in the current thread page.
Keep cache entries consistent across mutations and include the staged
contact form UX updates.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ffe6b904-8f76-4e89-a786-d07a0cc9096d
* fix(contacts): address review regressions
Stabilize asynchronous integration flows.
Keep contact pagination, resolution, and generated API contracts deterministic.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 58beb14a-b8b6-4d9c-a27d-5e70118cef95
* feat(contacts): add MongoDB repository
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 47a18375-9f8b-4f08-9014-ef78dcb72780
* feat(contacts): manage contacts from threads
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 369472a4-40f8-4604-81c2-c4942b88f0b6
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Copilot-Session: 8dd82cae-8bc6-4eaa-9b90-95073720c577
Copilot-Session: 1f3addc9-71dd-4cb7-bfae-e6fdcc8511af
Copilot-Session: 4a93bfbb-739a-414c-8eb4-48b15a69fb9d
Copilot-Session: dec094f9-5386-4607-998a-1701ac10b7cf
Copilot-Session: ffe6b904-8f76-4e89-a786-d07a0cc9096d
Copilot-Session: 58beb14a-b8b6-4d9c-a27d-5e70118cef95
Copilot-Session: 47a18375-9f8b-4f08-9014-ef78dcb72780
Copilot-Session: 369472a4-40f8-4604-81c2-c4942b88f0b6