17 Components

UI Components

Clientra CRM includes 17 reusable UI components in assets/js/components/. These are global classes instantiated and shared across page modules, providing consistent UI patterns throughout the application.

DataTable

File: components/DataTable.js

The primary data display component used on every list page. Provides sorting, pagination, column management, row selection, bulk actions, and inline search.

Features

  • Sortable columns with directional indicators
  • Client-side and server-side pagination
  • Column visibility toggle (via ColumnManager)
  • Multi-row selection with checkbox column
  • Bulk action buttons (delete, export, assign, bulk update)
  • Select All across pages: On Properties, "Select All" selects all matching records across all pages (not just the visible page)
  • Empty state messaging
  • Loading skeleton states
  • Row click handlers for quick view

Usage

javascript
const table = new DataTable('#table-container', {
  columns: [
    { key: 'ref',    label: 'Ref',    sortable: true  },
    { key: 'name',   label: 'Name',   sortable: true  },
    { key: 'status', label: 'Status', sortable: false },
    { key: 'actions', label: '',      sortable: false }
  ],
  data: leads,
  pageSize: 25,
  onSort: (col, dir) => loadData({ sort: col, dir }),
  onRowClick: (row) => showQuickView(row.id)
});

QuickViewModal

File: components/QuickViewModal.js

A side-sliding panel modal for displaying entity details without navigating away from the list page. Used on Properties, Leads, Contacts, and other list views.

❌ XSS Prevention — Required
Always use QuickViewModal.esc(value) when injecting user-generated content into innerHTML inside the modal. Never inject raw strings directly — this prevents XSS attacks.
javascript
// CORRECT — Always escape user data
modal.setContent(`
  <div class="qv-field">
    <label>Name</label>
    <span>${QuickViewModal.esc(contact.name)}</span>
  </div>
`);

// WRONG — Never inject raw user data
modal.setContent(`<span>${contact.name}</span>`); // XSS risk!

// Open / close the modal
const modal = new QuickViewModal();
modal.open({ title: 'Lead Details', width: '480px' });
modal.close();

SalesOfferModal

File: components/SalesOfferModal.js

Specialized modal for generating property sales offer PDFs. Allows selecting a property, choosing a PDF template and letterhead, and generating a professional sales offer document complete with property images.

FilterPanel

File: components/FilterPanel.js

A collapsible advanced filter panel that renders dynamic filter controls based on a configuration schema. Used on all list pages for filtering data. On the Properties page, filters support cascading/linked behavior — selecting a Location automatically narrows the Community and Project dropdowns to only show related items from the lookups system.

Supported Filter Types

TypeDescription
textFree-text search input
selectDropdown with predefined options
multiselectMultiple-choice dropdown
dateDate picker (single date)
daterangeStart/end date range picker
numberNumeric input with min/max
booleanYes/No toggle
lookupDynamic lookup dropdown (LookupSelect)
javascript
const filters = new FilterPanel('#filter-container', {
  filters: [
    { key: 'status', label: 'Status', type: 'select',
      options: ['New', 'Contacted', 'Won', 'Lost'] },
    { key: 'location', label: 'Location', type: 'lookup',
      source: 'locations' },
    { key: 'price_min', label: 'Min Price', type: 'number' },
  ],
  onChange: (values) => loadLeads(values)
});

FilterPresets

File: components/FilterPresets.js

Works alongside FilterPanel to allow users to save, name, and quickly load their commonly-used filter configurations. Presets are stored in localStorage per page per user.

Chart

File: components/Chart.js

A wrapper component around Chart.js 4.4.0 (loaded from CDN). Provides consistent styling, theming integration, and a simplified configuration API for all dashboard charts.

Supported Chart Types

bar line pie doughnut area
javascript
const chart = new Chart('#chart-canvas', {
  type: 'bar',
  labels: ['Jan', 'Feb', 'Mar'],
  datasets: [{
    label: 'Deals Closed',
    data: [12, 18, 9],
    backgroundColor: 'var(--color-primary)'
  }],
  options: { responsive: true }
});

KPICard

File: components/KPICard.js

Key Performance Indicator card component used on the Dashboard and Report pages. Displays a metric value with label, trend indicator, and optional icon.

javascript
KPICard.render({
  title: 'Active Leads',
  value: 142,
  change: '+12%',
  trend: 'up',       // 'up' | 'down' | 'neutral'
  icon: '🎯',
  color: 'primary'   // 'primary' | 'success' | 'warning' | 'danger'
});

PropertyCard

File: components/PropertyCard.js

Grid view card for property listings. Displays the cover image, property type badge, key details (beds, baths, size), price, and status badge. Used in the Properties grid view and the Portal page.

Timeline

File: components/Timeline.js

Entity activity timeline component. Renders the full communication and activity history for any CRM entity (lead, deal, contact, property, owner) in a chronological feed.

Rendered Event Types

  • Email sent/received with subject and preview
  • Call logged with duration and outcome
  • WhatsApp message sent
  • Meeting scheduled or completed
  • Note added with full text
  • Status/stage change events
  • Field update audit entries

ContactPicker

File: components/ContactPicker.js

An autocomplete search input for selecting contacts. Used on lead, deal, and communication forms to link entities to existing contacts without leaving the current form.

javascript
const picker = new ContactPicker('#contact-field', {
  placeholder: 'Search contacts...',
  minChars: 2,
  onSelect: (contact) => {
    form.contactId = contact.id;
    form.contactName = contact.name;
  }
});

ProjectPicker

File: components/ProjectPicker.js

Autocomplete project search input for linking entities to real estate development projects. Returns the project ID and name on selection.

UnitPicker

File: components/UnitPicker.js

Unit search and selection input. Optionally scoped to a specific project. Used when creating listings or deals to link to a specific unit within a development.

LookupSelect

File: components/LookupSelect.js

Dynamic dropdown that fetches options from the Lookups API. Used for developers, locations, communities, projects, and phases throughout all forms.

Available Lookup Sources

developers locations communities projects phases
javascript
const locationSelect = new LookupSelect('#location-field', {
  source: 'locations',
  placeholder: 'Select location...',
  allowCreate: true,  // Allow adding new items inline
  value: existingLocationId,
  onChange: (val) => { form.locationId = val; }
});

ColumnManager

File: components/ColumnManager.js

Column visibility management panel for DataTable. Renders a dropdown with checkboxes to show/hide individual table columns. Column preferences are persisted in localStorage per table per user.

FileUploader

File: components/FileUploader.js

Multi-file upload component with drag-and-drop support, progress tracking, file type validation, and size limits.

Features

  • Drag-and-drop upload zone
  • Click to browse file picker
  • Per-file upload progress bars
  • File type and size validation
  • Preview for image files
  • Upload cancellation
  • Supports local storage and Wasabi S3
javascript
const uploader = new FileUploader('#upload-zone', {
  entityType: 'property',
  entityId: propertyId,
  accept: ['.pdf', '.jpg', '.png', '.docx'],
  maxSizeMB: 25,
  multiple: true,
  onComplete: (files) => refreshDocumentList()
});

NotificationPanel

File: components/NotificationPanel.js

Real-time notification dropdown panel rendered in the top navigation bar. Displays unread notification count, notification list, and mark-as-read functionality.

Features

  • Unread count badge on nav icon
  • Dropdown panel with notification feed
  • Mark individual notifications as read
  • Mark all notifications as read (POST request)
  • Delete individual notifications
  • Polling interval for new notifications
  • Click notification to navigate to related entity
ℹ markNotificationsRead is POST
The mark_all_notifications_read endpoint uses POST method. Both api.php and api.js enforce this. Do not use GET for this operation.

ResponsibilityPicker

File: components/ResponsibilityPicker.js

Entity responsibility assignment component. Allows selecting primary and secondary agents responsible for a lead, deal, or property. Used to define agent accountability beyond simple "assigned to" fields.