Skip to content

CRM UI

Frontend screens, components, and user flows for the CRM module.

Route Structure

/crm                                → Main CRM view (Kanban default)
/crm?type=prospect&id=42            → Opens sidepanel for prospect #42
/crm?type=client&id=100             → Opens sidepanel for client (company) #100

The CRM is a single-page view at /crm with three switchable sub-views. Deep-linking to a specific record is supported via query parameters.

Main View (index.vue)

The entry point orchestrates all CRM sub-views and shared components.

Layout:

┌──────────────────────────────────────────────────────────────────┐
│  Section Title: "CRM Beta"                                       │
│  [Kanban | List | Calendar]    [+ Add] [Import] [Settings]       │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│              Active View (Kanban / List / Calendar)               │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

Components:

ElementComponentDescription
HeaderSectionTitleTitle with "Beta" badge
View SwitchSwitchThree-option toggle: Kanban, List, Calendar
Add ButtonButtonOpens AddProspect modal
Import ButtonButtonOpens CsvUpload modal
Settings ButtonButtonDelegates to active view's showSettings()

View Switching:

  • activeView ref controls which view is rendered via v-if / v-else-if
  • Default view: kanban
  • Only one view is mounted at a time to minimize memory usage

Provide/Inject:

The index view provides four refs to all child components:

KeyRefPurpose
$kambankambanAccess Kanban reload and settings
$sidepanelsidepanelOpen sidepanel from any view
$amodaladdOpen Add Prospect modal
$excelexcelOpen CSV Import modal

Kanban View (Kamban.vue)

Displays prospects organized into columns by follow-up status, with drag-and-drop to move cards between statuses.

Layout:

┌─────────────────────────────────────────────────────────────────────┐
│  [Filter Bar]  search | follow | onboarding | ecommerce | type ... │
├──────────┬──────────┬──────────┬──────────┬──────────┬──────────────┤
│ New Lead │Contacted │Qualified │Negotiat. │  Won     │  ...         │
│ KPI: $5k │ KPI: $12k│ KPI: $8k │ KPI: $25k│ KPI: $3k│              │
├──────────┼──────────┼──────────┼──────────┼──────────┼──────────────┤
│ ┌──────┐ │ ┌──────┐ │ ┌──────┐ │          │ ┌──────┐ │              │
│ │Card 1│ │ │Card 3│ │ │Card 5│ │          │ │Card 7│ │              │
│ └──────┘ │ └──────┘ │ └──────┘ │          │ └──────┘ │              │
│ ┌──────┐ │ ┌──────┐ │          │          │          │              │
│ │Card 2│ │ │Card 4│ │          │          │          │              │
│ └──────┘ │ └──────┘ │          │          │          │              │
│  ...     │  ...     │          │          │          │              │
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────────┘

Data Source

  • API: GET /prospects/grouped via api.prospects.getOnGroup(filters)
  • KPIs: GET /prospects/kpis via api.prospects.getKpis(filters)
  • Columns: Dynamically generated from api.catalog.getFollowUpCategoriesGrouped()

Card Content

Each card displays:

ElementDescription
Company namePrimary identifier
BalanceCurrent account balance (formatted currency)
Account valueEstimated account value
Last contactDate of most recent interaction
BadgesVerified status, tier, international flag, shops count, follow status, monthly shipments
ServicesRequested service badges (national, international, FTL, LTL, fulfillment, etc.)

Interactions

ActionBehavior
Click cardOpens sidepanel with prospect/client details
Drag card to another columnOpens sidepanel on followup tab with target status pre-selected
Scroll to bottom of columnInfinite scroll loads more records (v-infinite-scroll)

Drag-and-Drop

Implemented with vuedraggable. When a card is dropped into a new column:

  1. Sidepanel opens with the followup tab active
  2. The target follow-up status is pre-selected in the NoteForm
  3. User adds a note explaining the status change
  4. On save, the follow-up status is updated and the Kanban reloads

Filters

Uses useTablePreferences('crm-prospects') for filter visibility and ordering.

Default visible filters:

FilterTypeDescription
SearchText inputFree-text search across company/contact name
FollowSelectFilter by follow-up status
OnboardingSelectFilter by onboarding status
EcommerceSelectFilter by ecommerce platform
TypeSelectFilter by prospect type

Additional filters (visible via Settings):

Campaign, Salesman, KAE, KAE LTL, CSR, CSR LTL, Fulfillment, WMS, Ecartpay, Parapaquetes

Permission-gated filters:

When all-prospects permission is active:

  • Date range filter (startDate/endDate) — defaults to last 2 months
  • Salesman filter
  • Role-based filters (KAE, CSR, etc.) — each requires both all-prospects and the role's specific permission

Table/List View (ListView.vue)

A paginated data table with extensive column customization and server-side sorting.

Layout:

┌──────────────────────────────────────────────────────────────────┐
│  [Filter Bar]  search | follow | onboarding | ecommerce | ...    │
├──────────────────────────────────────────────────────────────────┤
│  Company  │ Follow │ Status │ Balance │ Acct Val │ Last    │ ... │
│───────────┼────────┼────────┼─────────┼──────────┼─────────┼─────│
│  Acme     │ New    │ Active │ $5,000  │ $10,000  │ 3d ago  │     │
│  Beta Co  │ Qual.  │ Hot    │ $12,000 │ $25,000  │ Today   │     │
│  ...      │        │        │         │          │         │     │
├──────────────────────────────────────────────────────────────────┤
│  Showing 1-25 of 1,250                    [< 1 2 3 ... 50 >]    │
└──────────────────────────────────────────────────────────────────┘

Data Source

  • API: GET /prospects/list via api.prospects.getList(filters)
  • Pagination: Server-side with start and length parameters
  • Sorting: Server-side by column name

Columns

All columns are configurable via useTablePreferences. Default configuration from CRM_TABLE_DEFAULTS:

ColumnKeyDefault VisibleDescription
Companycompany_nameYesCompany name with avatar/image
Follow-upfollow_nameYesCurrent follow-up status badge
Lead Statuslead_statusYesLead qualification status
Salesmansalesman_nameNoAssigned sales executive
BalancebalanceYesAccount balance (currency)
Account Valueaccount_valueYesEstimated account value
First Contactfirst_contactNoDate of first interaction
Last Contactlast_contactYesDate of most recent interaction
Days UCdays_ucNoDays since user creation
Days Without Contactdays_without_contactYesDays since last interaction
Next Contactnext_contactYesScheduled next follow-up
Contentcontent_nameNoPackage content type
Plan Typeplan_type_nameNoService plan type
Monthly Shipmentsmonthly_shipments_descNoEstimated monthly volume
Weight Rangeweight_rangeNoPackage weight category
CampaigncampaignNoAcquisition campaign
First Meetingfirst_meetingNoFirst meeting date
Second Meetingsecond_meetingNoSecond meeting date
Commercial Offercommercial_offer_dateNoDate of commercial offer
Integration Typeintegration_typeNoE-commerce integration
Est. Close Dateestimated_close_dateNoExpected deal close
Est. Close Monthestimated_close_monthNoExpected close month
Sourcecompany_sourceNoLead acquisition source
Last Rechargelast_recharge_dateNoLast account recharge
Actions(always visible)Yes"View info" button

Custom Column Slots

Several columns use custom rendering:

  • Company: Shows company image/avatar alongside name
  • Follow-up: Colored badge based on status
  • Balance / Account Value: Formatted currency with locale
  • Dates: Relative formatting ("3 days ago", "Today")
  • Days Without Contact: Color-coded (green < 7d, yellow < 14d, red > 14d)

Filters

All Kanban filters plus additional table-specific filters:

FilterKeyDefault VisibleDescription
Weight RangeweightRangeNoPackage weight category
ContentcontentIdNoContent type
Plan TypeplanTypeIdNoService plan
Min ShipmentsminShipmentsNoMinimum monthly shipments
Lead StatusleadStatusNoLead qualification status
Next ContactnextContactNoDate range for next contact
Commercial OffercommercialOfferDateNoDate range
Est. Close DateestimatedCloseDateNoDate range
Est. Close MonthestimatedCloseMonthNoMonth picker
Has RechargehasRechargeNoYes/No toggle

Row Actions

ActionBehavior
Click "View info"Opens sidepanel with prospect/client details

Calendar View (CalendarView.vue)

Displays follow-up activities as calendar events using FullCalendar.

Layout:

┌──────────────────────────────────────────────────────────────────┐
│  [Filter Bar]  search | follow | onboarding | ecommerce | ...    │
├──────────────────────────────────────────────────────────────────┤
│  [All] [Overdue] [Today] [This Month] [Upcoming]                 │
├──────────────────────────────────────────────────────────────────┤
│  < March 2026 >                                [Month] [Year]    │
├──────────────────────────────────────────────────────────────────┤
│  Mon  │  Tue  │  Wed  │  Thu  │  Fri  │  Sat  │  Sun            │
│───────┼───────┼───────┼───────┼───────┼───────┼──────           │
│       │  📞   │       │  📧   │       │       │                  │
│       │ Acme  │       │ Beta  │       │       │                  │
│       │       │  🤝   │       │  📱   │       │                  │
│       │       │ Corp  │       │ Delta │       │                  │
└───────┴───────┴───────┴───────┴───────┴───────┴──────────────────┘

Data Source

  • API: GET /prospects/activities via api.prospects.getActivities(params)
  • Required params: startDate, endDate (calendar visible range)

Calendar Configuration

FeatureValue
LibraryFullCalendar (Vue 3)
PluginsdayGrid, timeGrid, interaction, list
ViewsdayGridMonth (default), listYear (for period filters)
Initial viewdayGridMonth

Event Display

Events are color-coded by action type:

ActionIconColor
CallPhoneBlue
WhatsAppMessageGreen
EmailEnvelopeOrange
MeetingHandshakePurple
SMSMobileTeal
OtherCircleGray

Overdue events (past scheduled date without completion) receive a distinct style.

Period Filters

Quick-access buttons that switch the calendar view:

PeriodBehavior
AllShows all activities in the visible range
OverdueFilters to past-due activities only
TodayFilters to today's activities
This MonthFilters to current month
UpcomingFilters to future activities

Interactions

ActionBehavior
Click eventOpens sidepanel for the associated prospect or client
Navigate monthFetches new activities for the visible date range

Sidepanel (Sidepanel.vue)

An offcanvas panel that shows full details for a selected prospect or client. Opens from any view when a record is clicked.

Layout:

┌────────────────────────────────────────────────────────────────────────┐
│  Title: "Prospect Details"  ·  Lead Status Badge                       │
├──────────────┬──────────────────────────┬──────────────────────────────┤
│   LEFT 30%   │       CENTER 40%         │        RIGHT 30%             │
├──────────────┼──────────────────────────┼──────────────────────────────┤
│              │                          │                              │
│ ContactCard  │ [Followup] [Contacts]    │  Details Accordion           │
│ ┌──────────┐ │ [Locations] [Mail]       │  ├─ Ecommerce type           │
│ │ Company  │ │ [Activity]               │  ├─ Channel                  │
│ │ #ID      │ │                          │  ├─ Monthly shipments        │
│ │ Badge    │ │ ┌──────────────────────┐ │  ├─ Account value            │
│ │ Exec.    │ │ │  Tab Content         │ │  ├─ Weight range             │
│ │ Email    │ │ │                      │ │  ├─ Content type             │
│ │ Phone    │ │ │  Notes timeline      │ │  ├─ Account type             │
│ │ Balance  │ │ │  - or -              │ │  ├─ Est. close date          │
│ │ Follow   │ │ │  Contacts list       │ │  └─ Annotations              │
│ │ date     │ │ │  - or -              │ │                              │
│ └──────────┘ │ │  Locations list      │ │  Metrics Accordion           │
│              │ │  - or -              │ │  ├─ Last contact              │
│ [Reminder]   │ │  Mail inbox          │ │  ├─ Next contact              │
│              │ │                      │ │  ├─ Created date              │
│ Onboarding   │ └──────────────────────┘ │  ├─ Source                    │
│ Accordion    │                          │  └─ Campaign                  │
│              │ ┌──────────────────────┐ │                              │
│ Lead Summary │ │  Form Area           │ │  Integrations Accordion      │
│ Accordion    │ │  (NoteForm,          │ │  └─ E-commerce integrations  │
│ ├─ Origin    │ │   ContactForm,       │ │                              │
│ ├─ Created   │ │   LocationForm)      │ │  Marketing Accordion         │
│ ├─ 1st cont. │ │                      │ │  └─ Marketing fields         │
│ └─ Last cont.│ └──────────────────────┘ │                              │
└──────────────┴──────────────────────────┴──────────────────────────────┘

Data Loading

  • Prospects: GET /prospects/{id} via api.prospects.getById(id)
  • Clients: GET /clients/{id} via api.clients.getById(companyId)

The sidepanel determines which API to call based on itemOptions:

  • { has_prospect: true, id } — loads prospect data
  • { has_prospect: false, company } — loads client data

Left Column (30%)

SectionDescription
ContactCardCompany name, follow-up badge, assigned executive, email (clickable to mail tab), phone (WhatsApp link), balance
Follow-up dateEditable datetime input for the latest scheduled follow-up
Edit salesmanButton to reassign executive (requires crm-edit-salesman-prospect, only for prospects without company)
Add reminderOpens TodoForm to create a task/reminder linked to this prospect
Onboarding accordionDisplays onboarding questionnaire answers (for clients)
Lead summary accordionOrigin, creation date, first contact, last contact

Center Column (40%)

Tabs:

TabComponentDescription
FollowupNoteForm + Notes listChat-style timeline of notes. NoteForm at the bottom to add new notes with status, action, and optional scheduling
ContactsContactForm + listList of extra contacts with add/edit/delete. Each contact has name, email, phone, position
LocationsLocationForm + listList of locations with add/edit/delete. Each has name, type, and map link
MailMailingTabEmail inbox/outbox for crm_prospects or crm_clients module
ActivityPlaceholder"Coming soon" placeholder

Right Column (30%)

AccordionFields
DetailsEcommerce type, channel, locale, monthly shipments, account value, weight range, content type, account type, estimated close date, annotations. Editable via DetailsForm
MetricsLast contact, next contact, created date, source, campaign (read-only)
IntegrationsE-commerce platform integrations (read-only)
MarketingMarketing-related fields (read-only)

Exposed API

javascript
sidepanel.open(item, config?)
ParameterTypeDescription
itemObjectRecord data with at minimum id and entity type info
config.openstringTab to auto-open: 'followup'
config.groupObjectPre-selected follow-up group (for Kanban drag-and-drop)

URL Synchronization

When the sidepanel opens, query parameters are updated:

  • ?type=prospect&id=42 or ?type=client&id=100
  • On page load, if these params exist, the sidepanel auto-opens

Forms

NoteForm (Note.form.vue)

Creates follow-up notes with optional status change and scheduling.

FieldTypeValidationDescription
Follow-up statusGrouped selectOptionalNew status (grouped by category)
ActionSelectOptionalcall, whatsapp, email, sms, meeting, other
NoteTextareaRequiredNote content
Scheduled dateDate inputOptionalFollow-up date (shown when status + action selected)
Scheduled timeTime inputOptionalFollow-up time (default: 09:00)

ContactForm (Contact.form.vue)

Creates or updates contacts.

FieldTypeValidationDescription
NameInputRequiredContact person name
EmailInputRequiredEmail address
Phone codeSelectRequiredCountry phone code (from catalog)
PhoneInputRequiredPhone number
PositionInputOptionalJob title (disabled for default contact)

LocationForm (Location.form.vue)

Creates or updates locations.

FieldTypeValidationDescription
NameInputRequired, max 50Location name
TypeSelectRequiredoffice, warehouse, branch, headquarters, distribution_center
LinkInputRequired, URL patternMap/address URL

DetailsForm (Details.form.vue)

Edits follow-up metadata.

FieldTypeValidationDescription
Ecommerce typeSelectOptionalE-commerce platform
ChannelInputOptionalAcquisition channel
LocaleSelectOptionalCountry (disabled for clients)
Monthly shipmentsInputNumericShipment estimate
Account valueInputNumeric, disabledAuto-calculated from shipments x locale cost
Weight rangeSelectOptionalPackage weight category
Content typeSelectOptionalPackage content
Account typeSelectOptionalindividual, startup, sme, enterprise, corporate
Estimated close dateDateOptionalDeal close target
AnnotationsTextareaOptionalFree-text notes (5 rows)

ProspectForm (Prospect.form.vue)

Reassigns the executive/salesman for a prospect.

FieldTypeValidationDescription
Assigned toSelectRequiredAdmin users with SDR/MDR roles (roles 9, 21, 23, 10, 22, 24)

Add Prospect Modal (AddProspect.modal.vue)

Modal form to create a single new prospect.

Form Fields:

FieldTypeValidationDescription
CompanyInputRequiredCompany/organization name
NameInputRequiredContact person name
EmailInputRequired, emailEmail address
LocaleSelectRequiredCountry (from catalog, filtered to active locales)
Phone codeSelectRequiredCountry phone code
PhoneInputRequired, numericPhone number
Monthly shipmentsNumber inputOptionalEstimated monthly volume
Follow-up statusGrouped selectOptionalInitial follow-up category

Flow:


CSV Import (CsvUpload.modal.vue + ProcessCsv.offcanvas.vue)

Two-step import flow for bulk prospect creation from CSV files.

Step 1: Upload (CsvUpload.modal.vue)

  1. User selects a CSV file
  2. Client-side parsing validates the CSV structure
  3. Calls POST /prospects/process to validate against the database (duplicate check)
  4. Opens the ProcessCsv offcanvas with results

Step 2: Review & Confirm (ProcessCsv.offcanvas.vue)

Displays three categories:

CategoryDescription
InsertsNew records ready to create
UpdatesExisting inactive records that can be reactivated
ErrorsDuplicates or invalid records with error codes

User reviews the results and confirms the valid records, which are sent to POST /prospects for batch creation.


Table Preferences

All three views use the useTablePreferences composable with table ID crm-prospects.

Composable: useTablePreferences(tableId, options)

FeatureDescription
Column managementToggle visibility, reorder columns via drag-and-drop
Filter managementToggle filter visibility, reorder filters
PersistenceSaves to both localStorage (table-prefs-crm-prospects) and backend API
ResetRestores defaults defined in CRM_TABLE_DEFAULTS

Settings Panel

Accessed via the gear icon in the header. Opens a panel where users can:

  • Toggle columns on/off (List view)
  • Toggle filters on/off (all views)
  • Drag to reorder columns and filters
  • Reset to default configuration

Permissions Summary

PermissionUI Effect
all-prospectsShows date range filter, salesman filter, role filters. Removes 2-month restriction
crm-edit-salesman-prospectShows "Edit salesman" button in sidepanel (prospects only)
crm-partnersEnables partner data editing in sidepanel
crm-menuEnables location CRUD in sidepanel

Services (Prospect Card Badges)

Prospect cards in the Kanban view display service badges from the following catalog:

ServiceIcon
National shipmentsShipping truck
International shipmentsAirplane
FTL - Full TruckloadMoving truck
LTL - Less than truckloadBoxes
FulfillmentWarehouse
Para paquetesOpen box
EcartpayCredit card
Import servicesGlobe
Shipments from E-commerceShopping cart

Envia Admin