Adds suppliers feature to application
Introduces comprehensive suppliers management with mock data, localization, and UI components Implements: - Suppliers page with data table - Localization for Danish and English - Search and filtering functionality - Responsive table design - Mock data for initial population
This commit is contained in:
parent
7aaa475a14
commit
dc2bab5702
16 changed files with 622 additions and 8 deletions
|
|
@ -14,6 +14,7 @@ import { EmployeesController } from './modules/employees';
|
|||
import { ControlsController } from './modules/controls';
|
||||
import { ServicesController } from './modules/services';
|
||||
import { CustomersController } from './modules/customers';
|
||||
import { SuppliersController } from './modules/suppliers';
|
||||
import { TrackingController } from './modules/tracking';
|
||||
import { ReportsController } from './modules/reports';
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ export class App {
|
|||
readonly controls: ControlsController;
|
||||
readonly services: ServicesController;
|
||||
readonly customers: CustomersController;
|
||||
readonly suppliers: SuppliersController;
|
||||
readonly tracking: TrackingController;
|
||||
readonly reports: ReportsController;
|
||||
|
||||
|
|
@ -46,6 +48,7 @@ export class App {
|
|||
this.controls = new ControlsController();
|
||||
this.services = new ServicesController();
|
||||
this.customers = new CustomersController();
|
||||
this.suppliers = new SuppliersController();
|
||||
this.tracking = new TrackingController();
|
||||
this.reports = new ReportsController();
|
||||
}
|
||||
|
|
|
|||
146
PlanTempus.Application/wwwroot/ts/modules/suppliers.ts
Normal file
146
PlanTempus.Application/wwwroot/ts/modules/suppliers.ts
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* Suppliers Controller
|
||||
*
|
||||
* Handles:
|
||||
* - Fuzzy search with Fuse.js
|
||||
* - Row click navigation
|
||||
*/
|
||||
|
||||
import Fuse from 'fuse.js';
|
||||
|
||||
interface SupplierItem {
|
||||
name: string;
|
||||
contact: string;
|
||||
city: string;
|
||||
element: HTMLElement;
|
||||
}
|
||||
|
||||
export class SuppliersController {
|
||||
private fuse: Fuse<SupplierItem> | null = null;
|
||||
private suppliers: SupplierItem[] = [];
|
||||
private searchInput: HTMLInputElement | null = null;
|
||||
private emptyState: HTMLElement | null = null;
|
||||
private dataTable: HTMLElement | null = null;
|
||||
|
||||
constructor() {
|
||||
// Only initialize if we're on the suppliers page
|
||||
const suppliersTable = document.querySelector('swp-card.suppliers-list');
|
||||
if (!suppliersTable) return;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
this.searchInput = document.getElementById('supplierSearchInput') as HTMLInputElement;
|
||||
this.emptyState = document.getElementById('supplierEmptyState');
|
||||
this.dataTable = document.querySelector('swp-card.suppliers-list swp-data-table');
|
||||
|
||||
this.buildSupplierIndex();
|
||||
this.setupSearch();
|
||||
this.setupRowNavigation();
|
||||
}
|
||||
|
||||
private buildSupplierIndex(): void {
|
||||
const supplierRows = document.querySelectorAll('swp-card.suppliers-list swp-data-table-row');
|
||||
|
||||
supplierRows.forEach((row) => {
|
||||
const element = row as HTMLElement;
|
||||
|
||||
const name = element.dataset.name || '';
|
||||
const contact = element.dataset.contact || '';
|
||||
const city = element.dataset.city || '';
|
||||
|
||||
this.suppliers.push({
|
||||
name,
|
||||
contact,
|
||||
city,
|
||||
element
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private setupSearch(): void {
|
||||
if (!this.searchInput) return;
|
||||
|
||||
// Initialize Fuse.js with multiple search keys
|
||||
this.fuse = new Fuse(this.suppliers, {
|
||||
keys: ['name', 'contact', 'city'],
|
||||
threshold: 0.3,
|
||||
minMatchCharLength: 2
|
||||
});
|
||||
|
||||
// Listen for input with debounce
|
||||
let debounceTimer: number;
|
||||
this.searchInput.addEventListener('input', (e) => {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = window.setTimeout(() => {
|
||||
const query = (e.target as HTMLInputElement).value.trim();
|
||||
this.filterSuppliers(query);
|
||||
}, 150);
|
||||
});
|
||||
}
|
||||
|
||||
private filterSuppliers(query: string): void {
|
||||
if (!query || query.length < 2) {
|
||||
this.showAll();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.fuse) return;
|
||||
|
||||
// Get matching suppliers
|
||||
const results = this.fuse.search(query);
|
||||
const matchingSuppliers = new Set(results.map(r => r.item.element));
|
||||
|
||||
let visibleCount = 0;
|
||||
|
||||
// Show/hide suppliers
|
||||
this.suppliers.forEach(supplier => {
|
||||
if (matchingSuppliers.has(supplier.element)) {
|
||||
supplier.element.style.display = 'grid';
|
||||
visibleCount++;
|
||||
} else {
|
||||
supplier.element.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Show/hide empty state
|
||||
this.updateEmptyState(visibleCount);
|
||||
}
|
||||
|
||||
private showAll(): void {
|
||||
this.suppliers.forEach(supplier => {
|
||||
supplier.element.style.display = 'grid';
|
||||
});
|
||||
this.updateEmptyState(this.suppliers.length);
|
||||
}
|
||||
|
||||
private updateEmptyState(visibleCount: number): void {
|
||||
if (!this.emptyState || !this.dataTable) return;
|
||||
|
||||
if (visibleCount === 0) {
|
||||
this.emptyState.style.display = 'flex';
|
||||
// Hide header when no results
|
||||
const header = this.dataTable.querySelector('swp-data-table-header') as HTMLElement;
|
||||
if (header) header.style.display = 'none';
|
||||
} else {
|
||||
this.emptyState.style.display = 'none';
|
||||
// Show header when results exist
|
||||
const header = this.dataTable.querySelector('swp-data-table-header') as HTMLElement;
|
||||
if (header) header.style.display = 'grid';
|
||||
}
|
||||
}
|
||||
|
||||
private setupRowNavigation(): void {
|
||||
// Click on rows navigates to supplier detail page
|
||||
document.addEventListener('click', (e) => {
|
||||
const row = (e.target as HTMLElement).closest<HTMLElement>('swp-card.suppliers-list swp-data-table-row[data-href]');
|
||||
if (!row) return;
|
||||
|
||||
const href = row.dataset.href;
|
||||
if (href) {
|
||||
window.location.href = href;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue