Adds comprehensive customer detail view components

Implements full customer detail page with multiple feature-rich components including overview, economy, statistics, journal, appointments, giftcards, and activity sections

Creates reusable ViewComponents for different customer detail aspects with robust data modeling and presentation logic
This commit is contained in:
Janus C. H. Knudsen 2026-01-25 01:55:41 +01:00
parent 38e9243bcd
commit 1b25978d9b
26 changed files with 3792 additions and 956 deletions

View file

@ -4,9 +4,11 @@
* Handles:
* - Fuzzy search with Fuse.js
* - Customer drawer population
* - Customer detail economy chart
*/
import Fuse from 'fuse.js';
import { createChart } from '@sevenweirdpeople/swp-charting';
interface CustomerItem {
name: string;
@ -233,3 +235,94 @@ export class CustomersController {
return tag.charAt(0).toUpperCase() + tag.slice(1);
}
}
/**
* Customer Economy Controller
*
* Handles the economy chart on customer detail page.
* Initializes chart lazily when economy tab is shown.
*/
interface ChartDataPoint {
x: string;
y: number;
}
interface ChartSeries {
name: string;
color: string;
data: ChartDataPoint[];
}
interface CustomerChartData {
categories: string[];
series: ChartSeries[];
}
class CustomerEconomyController {
private chartInitialized = false;
private chart: ReturnType<typeof createChart> | null = null;
constructor() {
this.setupTabListener();
// Check if economy tab is already active on page load
this.checkInitialTab();
}
private setupTabListener(): void {
document.addEventListener('click', (e: Event) => {
const target = e.target as HTMLElement;
const tab = target.closest<HTMLElement>('swp-tab[data-tab="economy"]');
if (tab) {
// Small delay to let tab content become visible
setTimeout(() => this.initializeChart(), 50);
}
});
}
private checkInitialTab(): void {
const activeTab = document.querySelector('swp-tab[data-tab="economy"].active');
if (activeTab) {
this.initializeChart();
}
}
private initializeChart(): void {
if (this.chartInitialized) return;
const container = document.getElementById('customerRevenueChart');
if (!container) return;
const dataScript = document.getElementById('customerRevenueChartData');
if (!dataScript) return;
try {
const data = JSON.parse(dataScript.textContent || '') as CustomerChartData;
this.createRevenueChart(container, data);
this.chartInitialized = true;
} catch (err) {
console.error('Failed to parse chart data:', err);
}
}
private createRevenueChart(container: HTMLElement, data: CustomerChartData): void {
this.chart = createChart(container, {
deferRender: true,
height: 200,
xAxis: {
categories: data.categories
},
series: data.series.map(s => ({
name: s.name,
color: s.color,
data: s.data
})),
legend: false
});
}
}
// Initialize economy controller if on customer detail page
if (document.getElementById('customerRevenueChart') || document.querySelector('swp-tab[data-tab="economy"]')) {
new CustomerEconomyController();
}