Partners CRM — UI
Routes
| Route | Component | Description |
|---|---|---|
/partners/crm | views/partners/crm/index.vue | Partners CRM entry point; lazy-loaded as a child of /partners |
Deep Links
The side panel can be opened directly via query parameters:
/partners/crm?type=partner&id={partnerId}
/partners/crm?type=prospect&id={prospectId}These parameters are read on mount; if both are present the side panel opens automatically to the correct entity. Tasks and mailing modules use these URLs to link back to a specific partner or prospect from notifications and emails.
Screen Map
/partners/crm
├── Header bar
│ ├── View switcher (Kanban / List / Calendar)
│ ├── "+ Create" button → AddProspect.modal.vue
│ └── Settings gear → opens active view's column/filter settings
├── Kamban.vue (activeView === 'kanban')
├── ListView.vue (activeView === 'list')
├── CalendarView.vue (activeView === 'calendar')
├── Sidepanel.vue (always mounted, toggled via provide)
├── AddProspect.modal.vue
└── ModalAdministrator.vueScreens
Index (index.vue)
Entry point. Switches between three view components using v-if / v-else-if, keeping only one mounted at a time. Provides shared state to all child components via Vue's provide / inject.
Provide injections
| Key | Value | Consumers |
|---|---|---|
$kamban | activeViewRef (computed ref to Kamban, List, or Calendar) | Sidepanel — calls reload() after a note is saved |
$sidepanel | Sidepanel ref | Kamban — opens panel on drag; List/Calendar — opens panel on row/event click |
$amodal | AddProspect ref | Any child that needs to trigger prospect creation |
$modalAdm | ModalAdministrator ref | List view partner row action |
administrators | Array of admin users (role 20) | Sidepanel, ModalAdministrator |
mdrAgents | Array of MDR agents (role 10) | Sidepanel |
Lifecycle: On mount, fetches administrator and MDR agent lists via api.catalog.getAdministrators([20, 10]) and stores them in reactive refs shared through provide.
Kanban (Kamban.vue)
Displays partners and partner prospects as cards organized into columns defined by catalog_follow_up_statuses (source = partner). Each column supports infinite scroll.
Data loading
- Columns:
api.catalog.getFollowUpPartnerCategories()→GET /catalog/tours?type=partner - Cards:
api.partner.getOnGroup(params)→GET /partner/grouped - KPIs:
api.partner.getKpis(params)→GET /partner/kpis— displayed as a total referral revenue badge above each column
Drag-and-drop behavior
Powered by vuedraggable. Dropping a card does not PATCH the status immediately. Instead, it invokes onEndCallback, which calls $sidepanel.open(item, { open: 'notes', group: targetColumnId }). This opens the side panel with the target column pre-selected in the note form, requiring the user to confirm the stage change by writing a note.
Settings offcanvas: Column visibility and filter presets saved via useTablePreferences with id crm-partners.
Loading state: Skeleton.vue renders placeholder cards while initial data is fetching.
List (ListView.vue)
Paginated DataTable of all partners and partner prospects. Powered by api.partner.getList() → GET /partner/list.
Column configuration: Managed by useTablePreferences with id crm-partners. Default columns and filters defined in table.defaults.js.
Row click: Opens $sidepanel with the clicked entity.
Partner-specific row actions (registered partners only):
| Action | Permission | Description |
|---|---|---|
| Activate / Deactivate | crm-partners | Toggles partner status via api.partner.updatePartnerStatus() |
| View clients | — | Navigates to /partners/{id}/clients |
| View commissions | — | Navigates to /partners/{id}/commissions |
| View retained commissions | — | Navigates to /partners/{id}/commissions_retained |
| Assign administrator | update-partner | Opens ModalAdministrator |
Calendar (CalendarView.vue)
Displays scheduled follow-up activities using FullCalendar. Fetches from api.partner.getActivities() → GET /partner/activities.
The calendar is a UNION of:
- Scheduled notes for registered partners (from
follow_up_commentswithscheduled_date) - Scheduled notes for partner prospects (from
prospect_follow_up_commentswithscheduled_date)
Event click: Opens $sidepanel for the corresponding entity.
Filters: Date range, activity filter (overdue, upcoming, today, month), entity type (partner / prospect), pipeline column, locale. Saved via useTablePreferences with id crm-partners-calendar.
Side Panel (Sidepanel.vue)
Full-screen offcanvas (Offcanvas component with extension: true) that shows detail for the selected partner or partner prospect. Dispatches to the correct API based on options.type.
Layout: Three-column layout within the offcanvas body.
| Column | Contents |
|---|---|
| Left (30%) | Contact card (name, id, pipeline badge, email, phone, balance); lead summary table (source badge, company link, created/first contact/last contact dates); Add Reminder button |
| Center (40%) | Active tab content (Notes timeline, Contacts list, Mail tab, Details form) |
| Right (30%) | Activity feed placeholder |
Tabs
| Tab | Component | API called |
|---|---|---|
| Follow-up / Notes | Follow/Note.form.vue + notes timeline | POST /partner/{id}/notes or POST /prospects/{id}/notes |
| Contacts | Follow/Contact.form.vue | POST /partner/{id}/contacts or prospect contacts API |
Shared MailingTab component | Mailing module (partners / partner_prospects) | |
| Details | Follow/Details.form.vue | PATCH /partner/{id}/data or PATCH /prospects/{id}/partner-data |
Source badge: The left column always shows either a "Platform" badge (registered partner) or a "New" badge (partner prospect).
Registered partner link: For type = partner, the ID shown in the header is a router link to /partners/{id}/clients.
Reminder: The "Add Reminder" button shows an inline TodoForm that creates a task linked to the current entity via URL /partners/crm?type={type}&id={id}.
Opening the panel
// Programmatically open from any child component
const sidepanel = inject('$sidepanel')
sidepanel.value.open(item, { open: 'notes', group: targetColumnId })Module-Specific Components
All under frontend/client/src/views/partners/components/crm/:
| File | Role |
|---|---|
Kamban.vue | Kanban board with infinite-scroll columns and drag-and-drop |
ListView.vue | DataTable with partner-specific row actions |
CalendarView.vue | FullCalendar activity view |
Sidepanel.vue | Three-column offcanvas detail panel |
AddProspect.modal.vue | Modal to create a new partner prospect |
Skeleton.vue | Kanban loading skeleton (placeholder cards per column) |
table.defaults.js | Default column visibility and filter config for List and Calendar |
Follow/Note.form.vue | Follow-up note form: status picker, action type, text, optional schedule date |
Follow/Details.form.vue | Partner metadata form: URL, type, origin, rating, locale, social network |
Follow/Contact.form.vue | Contact CRUD form: name, email, phone code + number, position |
Related (shared, not under crm/):
| File | Role |
|---|---|
views/partners/components/ModalAministrator.vue | Assign administrator modal (opened from List view) |
components/interface/FollowUp/Notes.vue | Shared notes timeline renderer |
components/interface/FollowUp/Contacts.vue | Shared contacts list renderer |
components/interface/Email/MailingTab.vue | Shared email tab |
views/tasks/components/TodoForm.vue | Reminder/task creation form |
User Flows
Creating a Partner Prospect
Logging a Follow-up Note (from side panel)
Moving a Card via Kanban Drag
Editing Partner Details
Permissions Reference
| Permission | Scope | Effect in UI |
|---|---|---|
crm-partners | All CRM features | Without this, the Partners CRM route is inaccessible and all CRM API calls return 403 |
update-partner | List view | Enables the "Assign Administrator" row action for registered partners |
view-partners | Partners admin table | Controls access to /partners (the operational table), not this CRM |
crm-view-all | Calendar / List | Enables salesman filter and broader activity visibility |
State Management
The Partners CRM uses Vue's provide / inject pattern for cross-component communication rather than a Pinia store. This is intentional — the shared state (open panel, active view ref, modal refs) is ephemeral UI state tied to the lifetime of the index.vue component tree.
Table preferences (column visibility, active filters) are persisted through the useTablePreferences composable with the following IDs:
| View | Preference ID |
|---|---|
| Kanban | crm-partners |
| List | crm-partners |
| Calendar | crm-partners-calendar |
Preferences are stored in both localStorage (for immediate reads) and the API (for cross-device persistence).
i18n Keys
Partners CRM-specific i18n keys are defined in:
frontend/client/src/constants/partnersCrm.i18n.jsThis file provides translation keys for CRM column labels specific to the partner channel (e.g., referral counts, verified status). General CRM keys (crm.*) and action/label keys are shared with the main CRM.
