Add services feature with mock data and components
Introduces comprehensive services management module with: - Dynamic service and category tables - Localization support for services section - Mock data for services and categories - Responsive UI components for services listing - Menu navigation and styling updates Enhances application's service management capabilities
This commit is contained in:
parent
408e590922
commit
4cf30e1f27
20 changed files with 951 additions and 0 deletions
|
|
@ -12,6 +12,7 @@ import { LockScreenController } from './modules/lockscreen';
|
|||
import { CashController } from './modules/cash';
|
||||
import { EmployeesController } from './modules/employees';
|
||||
import { ControlsController } from './modules/controls';
|
||||
import { ServicesController } from './modules/services';
|
||||
|
||||
/**
|
||||
* Main application class
|
||||
|
|
@ -25,6 +26,7 @@ export class App {
|
|||
readonly cash: CashController;
|
||||
readonly employees: EmployeesController;
|
||||
readonly controls: ControlsController;
|
||||
readonly services: ServicesController;
|
||||
|
||||
constructor() {
|
||||
// Initialize controllers
|
||||
|
|
@ -36,6 +38,7 @@ export class App {
|
|||
this.cash = new CashController();
|
||||
this.employees = new EmployeesController();
|
||||
this.controls = new ControlsController();
|
||||
this.services = new ServicesController();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
111
PlanTempus.Application/wwwroot/ts/modules/services.ts
Normal file
111
PlanTempus.Application/wwwroot/ts/modules/services.ts
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* Services Controller
|
||||
* Handles category collapse/expand animations
|
||||
*/
|
||||
|
||||
export class ServicesController {
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
this.setupCategoryToggle();
|
||||
}
|
||||
|
||||
private setupCategoryToggle(): void {
|
||||
document.addEventListener('click', (e) => {
|
||||
const categoryRow = (e.target as HTMLElement).closest('swp-category-row');
|
||||
if (!categoryRow) return;
|
||||
|
||||
const isExpanded = categoryRow.getAttribute('data-expanded') !== 'false';
|
||||
const categoryId = categoryRow.getAttribute('data-category');
|
||||
|
||||
// Find all service rows belonging to this category
|
||||
const serviceRows = this.getServiceRowsForCategory(categoryRow);
|
||||
|
||||
if (isExpanded) {
|
||||
// Collapse - set attribute immediately so chevron animates with rows
|
||||
categoryRow.setAttribute('data-expanded', 'false');
|
||||
this.collapseRows(serviceRows);
|
||||
} else {
|
||||
// Expand - set attribute immediately so chevron animates with rows
|
||||
categoryRow.setAttribute('data-expanded', 'true');
|
||||
this.expandRows(serviceRows);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getServiceRowsForCategory(categoryRow: Element): HTMLElement[] {
|
||||
const rows: HTMLElement[] = [];
|
||||
let sibling = categoryRow.nextElementSibling;
|
||||
|
||||
while (sibling && sibling.tagName.toLowerCase() === 'swp-data-table-row') {
|
||||
rows.push(sibling as HTMLElement);
|
||||
sibling = sibling.nextElementSibling;
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private collapseRows(rows: HTMLElement[]): void {
|
||||
if (rows.length === 0) return;
|
||||
|
||||
// Animate each row
|
||||
rows.forEach((row) => {
|
||||
const height = row.offsetHeight;
|
||||
row.style.height = `${height}px`;
|
||||
row.style.overflow = 'hidden';
|
||||
|
||||
// Force reflow
|
||||
row.offsetHeight;
|
||||
|
||||
row.style.transition = 'height 0.2s ease, opacity 0.2s ease';
|
||||
row.style.height = '0';
|
||||
row.style.opacity = '0';
|
||||
});
|
||||
|
||||
// After animation completes
|
||||
setTimeout(() => {
|
||||
rows.forEach(row => {
|
||||
row.style.display = 'none';
|
||||
row.style.height = '';
|
||||
row.style.opacity = '';
|
||||
row.style.overflow = '';
|
||||
row.style.transition = '';
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
|
||||
private expandRows(rows: HTMLElement[]): void {
|
||||
rows.forEach((row, index) => {
|
||||
// First make visible but with 0 height
|
||||
row.style.display = 'grid';
|
||||
row.style.height = '0';
|
||||
row.style.opacity = '0';
|
||||
row.style.overflow = 'hidden';
|
||||
|
||||
// Measure natural height
|
||||
row.style.height = 'auto';
|
||||
const naturalHeight = row.offsetHeight;
|
||||
row.style.height = '0';
|
||||
|
||||
// Force reflow
|
||||
row.offsetHeight;
|
||||
|
||||
// Animate to natural height
|
||||
row.style.transition = 'height 0.2s ease, opacity 0.2s ease';
|
||||
row.style.height = `${naturalHeight}px`;
|
||||
row.style.opacity = '1';
|
||||
});
|
||||
|
||||
// Cleanup after animation
|
||||
setTimeout(() => {
|
||||
rows.forEach(row => {
|
||||
row.style.height = '';
|
||||
row.style.opacity = '';
|
||||
row.style.overflow = '';
|
||||
row.style.transition = '';
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue