Contacts Recently Changed
List Contacts
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| CON-001 | View contacts list with pagination | Logged in as Agent, org has 5 contacts | 1. Go to /contacts 2. Observe the contacts tab is active | List displays all 5 contacts in rows with columns: phone, name, email, tags, opted_in status, opted_out_at timestamp, lead indicator. Page size is 50 by default | High |
| CON-002 | Search contacts by name | Logged in as Agent, contacts exist: "John Smith", "Jane Doe", "Bob Johnson" | 1. Go to /contacts 2. Type "Jane" in search field 3. Press Enter or wait for auto-search | Only "Jane Doe" contact row appears in list. Phone, email, tags, opted_in status, opted_out_at, and lead indicator displayed | High |
| CON-003 | Search contacts by phone | Logged in as Agent, contact with phone "+1234567890" exists | 1. Go to /contacts 2. Type "+1234567890" in search field | Contact row appears with matching phone. Name, email, tags, opted_in status, opted_out_at, and lead indicator displayed | High |
| CON-004 | Filter contacts by tag | Logged in as Agent, contacts with tags: "vip", "lead", "customer" exist, other contacts have different tags | 1. Go to /contacts 2. Click tag filter dropdown 3. Select "vip" | Only contacts with "vip" tag appear in list. Tag dropdown shows all tags from org, including tags not on filtered results | High |
| CON-005 | Pagination: navigate to next page | Logged in as Agent, 120 contacts exist (page size 50) | 1. Go to /contacts 2. Observe page 1 shows contacts 1–50 3. Click next page arrow | Page 2 displays contacts 51–100. Previous/next buttons enabled. Page counter shows "Page 2" | High |
| CON-006 | Pagination: navigate to previous page | Logged in as Agent, on page 2 of contacts | 1. Click previous page arrow | Page 1 displays. Previous button disabled (if on page 1). Next button enabled | High |
| CON-007 | Empty contacts list | Logged in as Agent, no contacts created yet | 1. Go to /contacts | Empty state message displays: "No contacts yet. Create your first contact" or similar. Create Contact button visible | High |
| CON-008 | Search with no results | Logged in as Agent, contacts exist but search term has no matches | 1. Go to /contacts 2. Type "nonexistent" in search field | Empty state displays: "No contacts match your search". Search field retains input | Medium |
| CON-009 | Filter by tag with no results | Logged in as Agent, tag filter exists but no contacts have that tag | 1. Go to /contacts 2. Select tag that no contacts have | Empty state displays. Tag filter dropdown still shows the tag (all available tags always displayed) | Medium |
| CON-010 | Clear search field | Logged in as Agent, search field has text "Jane" | 1. Go to /contacts 2. Click X button on search field (if present) OR clear text | All contacts reappear in list | Medium |
| CON-011 | Clear tag filter | Logged in as Agent, tag filter "vip" is active | 1. Go to /contacts with tag filter active 2. Click X on tag filter or select "All tags" | All contacts appear regardless of tags | Medium |
| CON-012 | Search and filter combined | Logged in as Agent, contacts exist: phone "+1234" tagged "vip", "+5678" tagged "lead" | 1. Go to /contacts 2. Enter "+1234" in search 3. Select "vip" tag filter | Only contact with phone "+1234" and tag "vip" displays | Medium |
| CON-100 | Contact list shows lead indicator | Logged in as Agent, contact "John" is a lead, contact "Jane" is not a lead | 1. Go to /contacts 2. Observe contact rows | Row for "John" shows lead badge/icon (e.g., "Lead" label or star). Row for "Jane" has no lead indicator | High |
| CON-101 | Lead indicator shows tooltip on hover | Logged in as Agent, contact row visible with lead indicator | 1. Hover over lead badge/icon on contact row | Tooltip appears: "This contact has an associated lead" or "Click to view lead" | Medium |
| CON-124 | Tag dropdown always shows all available tags | Logged in as Agent, org has tags: "vip", "lead", "customer", "prospect". Only contacts with "vip" tag exist in list | 1. Go to /contacts 2. Click tag filter dropdown | Dropdown displays all 4 tags: "vip", "lead", "customer", "prospect". All tags shown regardless of whether they are assigned to contacts currently displayed | High |
| CON-125 | Tag dropdown populated on page load | Logged in as Agent, navigating to /contacts for first time in session | 1. Go to /contacts 2. Immediately click tag filter dropdown | Tag dropdown populates with all org tags. No delay or loading state (tags pre-loaded or fetched eagerly) | High |
| CON-126 | Tag dropdown updated after contact import | Logged in as Agent, tag dropdown was showing ["vip", "lead"], new contact imported with tag "prospect" | 1. Import CSV with contact tagged "prospect" 2. Success notification shown 3. Click tag filter dropdown | Tag dropdown now shows ["vip", "lead", "prospect"]. New tag "prospect" appears in dropdown | High |
| CON-127 | Tag dropdown persists after filter and search | Logged in as Agent, tag filter active on "vip", search filter active on "John" | 1. Go to /contacts with filters applied 2. Click tag filter dropdown 3. Scroll or observe | Tag dropdown shows all org tags, not just "vip". All available tags visible | High |
Create Contact
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| CON-013 | Create contact with required fields only | Logged in as Agent, at /contacts | 1. Click "New Contact" or "+" button 2. Enter phone "+1234567890" 3. Click Save | Success toast: "Contact created". Modal closes. New contact appears in list with phone "+1234567890", name/email/tags blank, is_opted_in=true (default), opted_out_at=null, no lead indicator | High |
| CON-014 | Create contact with all fields | Logged in as Agent, at /contacts | 1. Click "New Contact" 2. Enter phone "+1234567890", name "John Doe", email "john@example.com", tags "vip,lead" 3. Click Save | Success toast appears. Contact row displays all values: phone "+1234567890", name "John Doe", email "john@example.com", tags ["vip","lead"], is_opted_in=true (default), opted_out_at=null, no lead indicator | High |
| CON-015 | Create contact with JSON metadata | Logged in as Agent, at /contacts create modal | 1. Click "New Contact" 2. Enter phone "+1234567890" 3. Paste metadata JSON: `{"custom_field":"value"}` 4. Click Save | Contact created. Success toast shows. Metadata saved (visible when contact is edited) | Medium |
| CON-016 | Validation: phone field required | Logged in as Agent, at /contacts create modal | 1. Click "New Contact" 2. Leave phone empty 3. Click Save | Error message appears under phone field: "Phone number is required" or similar. Modal stays open | High |
| CON-017 | Validation: invalid phone format | Logged in as Agent, at /contacts create modal | 1. Click "New Contact" 2. Enter phone "invalid" (not a valid phone) 3. Click Save | Error message: "Invalid phone number format. Must start with + and contain digits" or similar | High |
| CON-018 | Validation: duplicate phone | Logged in as Agent, contact with phone "+1234567890" exists, at create modal | 1. Click "New Contact" 2. Enter phone "+1234567890" (existing) 3. Click Save | Error toast: "A contact with this phone number already exists" or 409 Conflict message | High |
| CON-019 | Validation: invalid email format | Logged in as Agent, at /contacts create modal | 1. Click "New Contact" 2. Enter phone "+1234567890", email "not-an-email" 3. Click Save | Error message under email field: "Invalid email format" or toast error. Contact not created | Medium |
| CON-020 | Create contact with tags (comma-separated input) | Logged in as Agent, at /contacts create modal | 1. Click "New Contact" 2. Enter phone "+1234567890", name "Jane" 3. In tags field, type "vip,lead,customer" 4. Click Save | Contact created with tags ["vip","lead","customer"]. Tags display as individual badges in list row | Medium |
| CON-021 | Create contact form: cancel without saving | Logged in as Agent, at create contact modal with form filled | 1. Click "New Contact" 2. Enter phone "+1234567890", name "John" 3. Click Cancel or X button | Modal closes. No contact created. List unchanged | Medium |
| CON-022 | Create contact: form validation on blur | Logged in as Agent, at create modal | 1. Click "New Contact" 2. Enter email "invalid-email" 3. Click outside email field (blur) | Error message appears under email field immediately (without clicking Save) | Low |
| CON-104 | Create contact with custom field values | Logged in as Agent, org has custom field "Company" defined, at create modal | 1. Click "New Contact" 2. Enter phone "+1234567890", name "John" 3. In custom field "Company", enter "Acme Corp" 4. Click Save | Contact created. Success toast shows. Custom field value "Acme Corp" is saved and displayed in contact detail | High |
| CON-105 | Create contact: custom field validation | Logged in as Agent, org has custom field "Age" (numeric, required), at create modal | 1. Click "New Contact" 2. Enter phone "+1234567890" 3. Leave "Age" custom field empty 4. Click Save | Error message under "Age" field: "This field is required" (if field is marked required). Modal stays open | High |
View / Edit Contact
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| CON-023 | Open contact details modal | Logged in as Agent, contact "John Doe" (+1234567890) exists | 1. Go to /contacts 2. Click on contact row for "John Doe" | Modal or detail view opens showing: phone, name, email, tags, is_opted_in toggle, opted_out_at timestamp, metadata, created_at, updated_at, assigned_user/team, lead_id (if contact is a lead) | High |
| CON-024 | Edit contact: change name | Logged in as Agent, contact "John Doe" exists, detail view open | 1. Click Edit or Pencil icon 2. Change name to "Jane Doe" 3. Click Save | Success toast: "Contact updated". Modal closes. List row now shows "Jane Doe" as name | High |
| CON-025 | Edit contact: change email | Logged in as Agent, editing contact with email "old@example.com" | 1. Click Edit 2. Change email to "new@example.com" 3. Click Save | Success toast. Contact email updated to "new@example.com" in list and detail view | High |
| CON-026 | Edit contact: add/remove tags | Logged in as Agent, editing contact with tags ["vip"] | 1. Click Edit 2. Add tag "customer" 3. Click Save | Success toast. Contact now shows tags ["vip","customer"]. Tag removed similarly | High |
| CON-027 | Edit contact: update metadata JSON | Logged in as Agent, editing contact with metadata | 1. Click Edit 2. Modify metadata JSON field 3. Click Save | Success toast. Metadata persisted (visible in detail view) | Medium |
| CON-028 | Edit contact: cannot change phone | Logged in as Agent, editing contact | 1. Click Edit 2. Try to change phone field | Phone field is disabled/read-only. Cannot edit phone number | High |
| CON-029 | Edit contact: toggle opted_in status | Logged in as Agent, editing contact with is_opted_in=false, opted_out_at=timestamp | 1. Click Edit 2. Click toggle switch for "Opted In" to enable 3. Click Save | Success toast. Contact is_opted_in toggled to true. opted_out_at cleared/set to null in detail view | Medium |
| CON-030 | Edit contact: validation on empty name | Logged in as Agent, editing contact, name is currently "John" | 1. Click Edit 2. Clear name field 3. Click Save | Contact saves successfully (name is optional). Name becomes null in display | Medium |
| CON-031 | Edit contact: validation on invalid email | Logged in as Agent, editing contact | 1. Click Edit 2. Enter email "invalid" 3. Click Save | Error message: "Invalid email format" under email field | Medium |
| CON-032 | Edit contact form: cancel without saving | Logged in as Agent, editing contact | 1. Click Edit 2. Change name to "New Name" 3. Click Cancel | Modal closes. Contact name remains unchanged in list | Medium |
| CON-033 | View contact: open detail view from list | Logged in as Agent, contact row visible | 1. Click contact row 2. Observe detail modal | Detail view shows all contact info: phone, name, email, tags, is_opted_in, opted_out_at, timestamps, assigned_user/team, lead_id (if lead) | High |
| CON-034 | View contact 404 | Logged in as Agent, contact ID "invalid-id" | 1. Manually navigate to /contacts/invalid-id OR contact was deleted 2. Observe | Error message: "Contact not found" or 404 page | Low |
| CON-102 | Contact detail shows lead ID and is_lead flag | Logged in as Agent, contact "John" is a lead, detail view open | 1. Open contact detail for "John" 2. Observe response data or UI | Detail view shows lead_id (UUID of associated lead) and is_lead=true. If not a lead, lead_id is null and is_lead=false | High |
| CON-106 | Edit contact: update custom field value | Logged in as Agent, contact with custom field "Company"="Acme Corp", editing | 1. Click Edit 2. Change custom field "Company" to "Beta Ltd" 3. Click Save | Success toast. Custom field value updated to "Beta Ltd". Displayed in contact detail and audit log | High |
| CON-107 | Edit contact: clear custom field value | Logged in as Agent, editing contact with custom field value | 1. Click Edit 2. Clear custom field value 3. Click Save | Success toast. Custom field value cleared/deleted. Audit log shows value was removed | Medium |
| CON-108 | Contact detail displays all custom fields | Logged in as Agent, org has 5 custom fields defined, contact detail open | 1. Open contact detail 2. Scroll to custom fields section | All 5 custom fields are displayed with their values (or empty if not set). Fields ordered by definition order | High |
| CON-109 | Contact detail: custom field with variable placeholder | Logged in as Agent, custom field has dynamic value like `{contact.name}`, detail open | 1. Open contact detail 2. Observe custom field rendering | Variable placeholder is resolved: if field value is "Hello {contact.name}" and contact is "John", displays "Hello John" (if interpolation enabled) | Medium |
Delete Contact
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| CON-035 | Delete contact | Logged in as Agent, contact "John Doe" exists | 1. Go to /contacts 2. Right-click or find delete option on contact row 3. Click Delete or Trash icon 4. Confirm delete | Success toast: "Contact deleted". Contact row disappears from list | High |
| CON-036 | Delete contact: confirmation modal | Logged in as Agent, attempting to delete contact | 1. Click Delete/Trash icon on contact 2. Observe confirmation modal | Confirmation modal appears: "Delete contact? This action cannot be undone." with Cancel and Confirm buttons | High |
| CON-037 | Delete contact: cancel delete | Logged in as Agent, confirmation modal open for delete | 1. Click Cancel button in confirmation modal | Modal closes. Contact remains in list | Medium |
| CON-038 | Delete contact: soft delete (audit trail) | Logged in as Agent, contact deleted | 1. Delete contact 2. Go to contact audit log | Audit log entry shows: event_type="contact_deleted", actor=current user, timestamp | Medium |
Assign Contact
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| CON-039 | Assign contact to user | Logged in as Agent, contact "John Doe" exists, org members include "Agent Bob" | 1. Go to /contacts 2. Open contact detail 3. Click "Assign to User" or assign icon 4. Select "Agent Bob" 5. Click Save | Success toast: "Contact assigned to Agent Bob". Detail view shows "Assigned to: Agent Bob". Audit log records actor_id who made the assignment | High |
| CON-040 | Assign contact to team | Logged in as Agent, contact exists, teams exist: "Sales Team" | 1. Open contact detail 2. Click assign option 3. Select team "Sales Team" 4. Click Save | Success toast: "Contact assigned to Sales Team". assigned_team_id populated. Audit log records actor_id of assigning user | High |
| CON-041 | Unassign contact from user | Logged in as Agent, contact assigned to "Agent Bob" | 1. Open contact detail 2. Click assign option 3. Clear/remove user assignment 4. Click Save | Success toast. Contact assigned_user_id becomes null. "Assigned to" no longer shown. Audit log records actor_id | Medium |
| CON-042 | Assign to user vs team priority | Logged in as Agent, contact can be assigned to both user and team | 1. Assign contact to both user "Agent Bob" and team "Sales Team" 2. Observe detail view | Both assigned_user_id and assigned_team_id are populated. Both shown in UI. Audit log records actor_id for assignment changes | Medium |
| CON-099 | Assign contact: audit log shows actor | Logged in as Agent A, Agent B assigns contact to Agent C | 1. Agent B assigns contact to "Agent C" 2. Agent A opens contact audit log | Audit log entry shows "Contact assigned to Agent C", actor_name="Agent B" (the person who made the assignment, recorded via actor_id) | High |
Import Contacts
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| CON-043 | Import contacts from CSV | Logged in as Agent, at /contacts | 1. Click Import or Upload button 2. Select CSV file with columns: phone, name, email, tags, opted_in 3. Observe progress | Upload modal shows. File accepted. "Processing..." or similar indicator | High |
| CON-044 | Import CSV: successful creation | Logged in as Agent, CSV file with 3 valid rows uploaded | 1. Upload CSV with rows: (+1111111111, John, john@ex.com, vip, true), (+2222222222, Jane, jane@ex.com, lead, true), (+3333333333, Bob, bob@ex.com, "", false) 2. Wait for completion | Success toast: "Imported 3 contacts (0 updated, 0 skipped, 0 errors)". All 3 contacts appear in list with is_opted_in and opted_out_at set appropriately | High |
| CON-045 | Import CSV: skip duplicate phones | Logged in as Agent, contact with phone "+1111111111" exists, CSV has same phone | 1. Upload CSV with duplicate phone "+1111111111" 2. Wait for completion | Result shows: "Imported 0 contacts (1 updated, 0 skipped)" or "1 skipped". Existing contact not duplicated | High |
| CON-046 | Import CSV: update existing contact | Logged in as Agent, contact "+1111111111" with name "Old Name" exists, CSV has same phone with name "New Name" | 1. Upload CSV with row: (+1111111111, New Name, newemail@ex.com, tag, true) 2. Wait | Result: "1 updated". Contact name changes to "New Name". is_opted_in set to true, opted_out_at cleared. Import result dialog shows updated count | High |
| CON-047 | Import CSV: validation errors | Logged in as Agent, CSV has invalid rows: no phone, invalid email | 1. Upload CSV with 3 rows: 1 valid, 1 no phone, 1 invalid email 2. Wait | Result: "Imported 1 (0 updated, 0 skipped, 2 errors)". Error details shown: row numbers and error messages | High |
| CON-048 | Import CSV: empty file | Logged in as Agent, empty CSV uploaded | 1. Upload empty CSV 2. Wait | Error message: "CSV file is empty or has no valid rows" or result shows "0 imported" | Medium |
| CON-049 | Import CSV: missing required column | Logged in as Agent, CSV missing "phone" column | 1. Upload CSV without phone column 2. Wait | Error message: "Missing required column: phone" or import fails with validation error | High |
| CON-050 | Import CSV: preview before import | Logged in as Agent, CSV file selected | 1. Click Import 2. Preview modal shows first 5-10 rows | Preview displays file content with column headers. User can confirm before final import | Medium |
| CON-051 | Import CSV: cancel import | Logged in as Agent, import in progress or preview open | 1. Click Cancel button | Upload canceled. No contacts imported. Return to contacts list | Medium |
| CON-110 | Import CSV: custom field columns included | Logged in as Agent, org has custom fields "Company" and "Industry", CSV has columns for both | 1. Select CSV file with columns: phone, name, email, tags, opted_in, Company, Industry 2. Upload | Preview shows all columns including custom fields. Import result: "Imported X contacts" with custom field values populated. Contacts display custom field values | High |
| CON-111 | Import CSV: custom field values mapped correctly | Logged in as Agent, CSV rows include custom field data: phone "+111", Company "Acme", Industry "Tech" | 1. Upload CSV 2. Wait for completion | Contact created with custom field values. Detail view shows Company="Acme", Industry="Tech". Audit log shows custom field values were set | High |
| CON-112 | Import CSV: missing custom field in some rows | Logged in as Agent, CSV has custom field "Company" but some rows leave it blank | 1. Upload CSV with mixed empty/filled custom field values | Import succeeds. Contacts with blank custom field have empty value. No validation error (custom fields optional unless marked required) | Medium |
| CON-113 | Import CSV: invalid custom field value type | Logged in as Agent, custom field "Age" is numeric, CSV has row with Age="not_a_number" | 1. Upload CSV with invalid custom field value | Result: Import with error on that row. Error message: "Row 5: Invalid value for 'Age' (expected number)" or similar. Other rows imported | Medium |
| CON-119 | Import CSV: opted_in and opted_out_at columns | Logged in as Agent, CSV has columns: phone, name, opted_in, and optional opted_out_at | 1. Upload CSV with row: (+111, John, false, 2026-06-01T10:00:00Z) 2. Wait | Contact created with is_opted_in=false, opted_out_at="2026-06-01T10:00:00Z". List row shows opted_out_at timestamp. Contact detail displays both fields | High |
| CON-120 | Import CSV: opted_out_at cleared when opted_in=true | Logged in as Agent, CSV row has opted_in=true and opted_out_at empty/null | 1. Upload CSV with row: (+111, John, true, null) 2. Wait | Contact created with is_opted_in=true, opted_out_at=null. List row shows no opted_out_at timestamp | Medium |
Export Contacts
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| CON-052 | Export all contacts to CSV | Logged in as Agent, 5 contacts exist | 1. Go to /contacts 2. Click Export or Download button | CSV file downloaded with filename "contacts.csv". Columns: phone, name, email, tags, opted_in, opted_out_at. All 5 contact rows present with opted_in/opted_out_at values | High |
| CON-053 | Export with search filter applied | Logged in as Agent, 5 contacts exist, search "John" returns 2 contacts | 1. Go to /contacts 2. Search "John" 3. Click Export | CSV downloaded contains only the 2 filtered contacts, not all 5 | High |
| CON-054 | Export with tag filter applied | Logged in as Agent, filter by tag "vip" shows 3 contacts | 1. Go to /contacts 2. Filter by tag "vip" 3. Click Export | CSV downloaded contains only the 3 "vip" tagged contacts | High |
| CON-055 | Export empty contacts list | Logged in as Agent, no contacts created | 1. Go to /contacts 2. Click Export | CSV file downloaded with headers only (phone, name, email, tags, opted_in, opted_out_at) and no data rows | Medium |
| CON-056 | Export file format validation | Logged in as Agent, contacts exported | 1. Export contacts 2. Open CSV file in text editor | File is valid CSV format: comma-separated, quoted strings, newlines between rows | Low |
| CON-114 | Export contacts: include custom field columns | Logged in as Agent, org has custom fields "Company" and "Industry", 3 contacts with values exist | 1. Go to /contacts 2. Click Export | CSV downloaded with columns: phone, name, email, tags, opted_in, opted_out_at, Company, Industry. All 3 contacts' custom field values included in rows | High |
| CON-115 | Export: custom field column order matches definition order | Logged in as Agent, custom fields defined in order: "Company", "Industry", "Website" | 1. Export contacts 2. Open CSV file | CSV header row shows columns in order: phone, name, email, tags, opted_in, opted_out_at, Company, Industry, Website (custom fields in definition order) | Medium |
| CON-116 | Export: empty custom field values shown as blank | Logged in as Agent, contact A has Company="Acme", contact B has Company blank | 1. Export contacts 2. Open CSV file | Row for contact A: "Acme". Row for contact B: blank cell (empty string or space) in Company column | Low |
| CON-121 | Export CSV: opted_out_at timestamp format | Logged in as Agent, contacts have opted_out_at values, exporting | 1. Go to /contacts 2. Click Export 3. Open CSV file | opted_out_at column shows ISO 8601 timestamp format (e.g., "2026-06-01T10:30:00Z") or null/empty for contacts with is_opted_in=true | Medium |
Contact Audit Log
| ID | Test Case | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| CON-057 | View contact audit log | Logged in as Agent, contact "John Doe" exists with history | 1. Open contact detail 2. Click "Audit Log" or "Activity" tab | Audit log displays list of events: "Contact created", "Tag added 'vip'", etc. Each entry shows timestamp, actor name, event description | High |
| CON-058 | Audit log: contact created event | Logged in as Agent, contact was just created | 1. Open contact detail 2. View audit log | First entry shows: event_type="contact_created", actor_name="current_user", text="Contact created via {source}", created_at=timestamp | Medium |
| CON-059 | Audit log: tag added event | Logged in as Agent, tag "vip" was added to contact | 1. Open contact detail 2. View audit log | Entry shows: event_type="tag_added", text="Tag 'vip' added", actor_name, timestamp | Medium |
| CON-060 | Audit log: tag removed event | Logged in as Agent, tag "vip" was removed from contact | 1. Open contact detail 2. View audit log | Entry shows: event_type="tag_removed", text="Tag 'vip' removed", actor_name, timestamp | Medium |
| CON-061 | Audit log: contact updated event | Logged in as Agent, contact name was changed from "John" to "Jane" | 1. Open contact detail 2. View audit log | Entry shows: event_type="contact_updated" or similar, text contains old/new values, actor_name, timestamp | Medium |
| CON-062 | Audit log: pagination/load more | Logged in as Agent, contact has 100+ audit log entries | 1. Open contact detail 2. View audit log 3. Scroll to bottom | "Load More" button appears at bottom. Click to fetch next 50 entries. New entries append to list | Medium |
| CON-063 | Audit log: time formatting | Logged in as Agent, viewing audit log entries | 1. Open contact detail 2. View audit log | Recent entries show "just now", "5m ago", "2h ago". Older entries show date format "Mar 15, 2026" | Low |
| CON-064 | Add note to contact | Logged in as Agent, contact detail open, audit log visible | 1. In audit log section, click "Add Note" or text area 2. Type "Follow up next week" 3. Click Save Note | Success toast. New entry appears at top of audit log: event_type="note_added", text="Follow up next week", actor_name=current_user | High |
| CON-065 | Edit note on contact | Logged in as Agent, contact has note "Old text" in audit log | 1. Find note entry in audit log 2. Click Edit or Pencil icon on note 3. Change text to "New text" 4. Click Save | Success toast. Note text updated. Entry shows "(edited)" or timestamp updated_at differs from created_at | High |
| CON-066 | Delete note on contact | Logged in as Agent, contact has note in audit log | 1. Find note entry 2. Click Delete or Trash icon 3. Confirm delete | Success toast. Note entry removed from audit log | High |
| CON-067 | Audit log: actor names | Logged in as Agent, audit log has entries from multiple users | 1. Open contact detail 2. View audit log | Each entry shows actor_name (user's full name), not user ID. If actor is system, shows "System" or similar | Medium |
| CON-068 | Audit log: lead-related events | Logged in as Agent, contact was converted to lead | 1. Open contact audit log 2. Observe events | Log shows events like "lead_created", "lead_stage_changed", "lead_converted" with relevant data | Medium |
| CON-069 | Audit log: conversation events | Logged in as Agent, contact has messages | 1. Open contact audit log 2. Observe events | Log shows "conversation_created", "conversation_resolved", "conversation_assigned to {user}" events | Medium |
| CON-070 | Audit log: reminder events | Logged in as Agent, reminders were set on contact | 1. Open contact audit log 2. Observe events | Log shows "reminder_scheduled for {due_at}", "reminder_sent to {notified_user}" events with formatted date/time | Medium |
| CON-117 | Audit log: custom field updated event | Logged in as Agent, custom field "Company" was changed from "Acme" to "Beta Corp" | 1. Open contact audit log 2. View events | Entry shows: event_type="custom_field_updated", text="Company: Acme → Beta Corp", actor_name, timestamp | Medium |