Various CSS work
This commit is contained in:
parent
ef174af0e1
commit
15579acba8
52 changed files with 8001 additions and 944 deletions
|
|
@ -10,6 +10,7 @@ import { ThemeController } from './modules/theme';
|
|||
import { SearchController } from './modules/search';
|
||||
import { LockScreenController } from './modules/lockscreen';
|
||||
import { CashController } from './modules/cash';
|
||||
import { EmployeesController } from './modules/employees';
|
||||
|
||||
/**
|
||||
* Main application class
|
||||
|
|
@ -21,6 +22,7 @@ export class App {
|
|||
readonly search: SearchController;
|
||||
readonly lockScreen: LockScreenController;
|
||||
readonly cash: CashController;
|
||||
readonly employees: EmployeesController;
|
||||
|
||||
constructor() {
|
||||
// Initialize controllers
|
||||
|
|
@ -30,6 +32,7 @@ export class App {
|
|||
this.search = new SearchController();
|
||||
this.lockScreen = new LockScreenController(this.drawers);
|
||||
this.cash = new CashController();
|
||||
this.employees = new EmployeesController();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
191
PlanTempus.Application/wwwroot/ts/modules/employees.ts
Normal file
191
PlanTempus.Application/wwwroot/ts/modules/employees.ts
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/**
|
||||
* Employees Controller
|
||||
*
|
||||
* Handles content swap between list view and detail view,
|
||||
* plus tab switching within each view.
|
||||
* Uses History API for browser back/forward navigation.
|
||||
*/
|
||||
|
||||
export class EmployeesController {
|
||||
private listView: HTMLElement | null = null;
|
||||
private detailView: HTMLElement | null = null;
|
||||
|
||||
constructor() {
|
||||
this.listView = document.getElementById('employees-list-view');
|
||||
this.detailView = document.getElementById('employee-detail-view');
|
||||
|
||||
// Only initialize if we're on the employees page
|
||||
if (!this.listView) return;
|
||||
|
||||
this.setupListTabs();
|
||||
this.setupDetailTabs();
|
||||
this.setupChevronNavigation();
|
||||
this.setupBackNavigation();
|
||||
this.setupHistoryNavigation();
|
||||
this.restoreStateFromUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup popstate listener for browser back/forward
|
||||
*/
|
||||
private setupHistoryNavigation(): void {
|
||||
window.addEventListener('popstate', (e: PopStateEvent) => {
|
||||
if (e.state?.employeeKey) {
|
||||
this.showDetailViewInternal(e.state.employeeKey);
|
||||
} else {
|
||||
this.showListViewInternal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore view state from URL on page load
|
||||
*/
|
||||
private restoreStateFromUrl(): void {
|
||||
const hash = window.location.hash;
|
||||
if (hash.startsWith('#employee-')) {
|
||||
const employeeKey = hash.substring(1); // Remove #
|
||||
this.showDetailViewInternal(employeeKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup tab switching for the list view
|
||||
*/
|
||||
private setupListTabs(): void {
|
||||
if (!this.listView) return;
|
||||
|
||||
const tabs = this.listView.querySelectorAll<HTMLElement>('swp-tab-bar > swp-tab[data-tab]');
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const targetTab = tab.dataset.tab;
|
||||
if (targetTab) {
|
||||
this.switchTab(this.listView!, targetTab);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup tab switching for the detail view
|
||||
*/
|
||||
private setupDetailTabs(): void {
|
||||
if (!this.detailView) return;
|
||||
|
||||
const tabs = this.detailView.querySelectorAll<HTMLElement>('swp-tab-bar > swp-tab[data-tab]');
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const targetTab = tab.dataset.tab;
|
||||
if (targetTab) {
|
||||
this.switchTab(this.detailView!, targetTab);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to a specific tab within a container
|
||||
*/
|
||||
private switchTab(container: HTMLElement, targetTab: string): void {
|
||||
const tabs = container.querySelectorAll<HTMLElement>('swp-tab-bar > swp-tab[data-tab]');
|
||||
const contents = container.querySelectorAll<HTMLElement>('swp-tab-content[data-tab]');
|
||||
|
||||
tabs.forEach(t => {
|
||||
t.classList.toggle('active', t.dataset.tab === targetTab);
|
||||
});
|
||||
|
||||
contents.forEach(content => {
|
||||
content.classList.toggle('active', content.dataset.tab === targetTab);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup row click to show detail view
|
||||
* Ignores clicks on action buttons
|
||||
*/
|
||||
private setupChevronNavigation(): void {
|
||||
document.addEventListener('click', (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
|
||||
// Ignore clicks on action buttons
|
||||
if (target.closest('swp-icon-btn') || target.closest('swp-table-actions')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const row = target.closest<HTMLElement>('swp-employee-row[data-employee-detail]');
|
||||
|
||||
if (row) {
|
||||
const employeeKey = row.dataset.employeeDetail;
|
||||
if (employeeKey) {
|
||||
this.showDetailView(employeeKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup back button to return to list view
|
||||
*/
|
||||
private setupBackNavigation(): void {
|
||||
document.addEventListener('click', (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
const backLink = target.closest<HTMLElement>('[data-employee-back]');
|
||||
|
||||
if (backLink) {
|
||||
this.showListView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the detail view and hide list view (with history push)
|
||||
*/
|
||||
private showDetailView(employeeKey: string): void {
|
||||
// Push state to history
|
||||
history.pushState(
|
||||
{ employeeKey },
|
||||
'',
|
||||
`#${employeeKey}`
|
||||
);
|
||||
this.showDetailViewInternal(employeeKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show detail view without modifying history (for popstate)
|
||||
*/
|
||||
private showDetailViewInternal(employeeKey: string): void {
|
||||
if (this.listView && this.detailView) {
|
||||
this.listView.style.display = 'none';
|
||||
this.detailView.style.display = 'block';
|
||||
this.detailView.dataset.employee = employeeKey;
|
||||
|
||||
// Reset to first tab
|
||||
this.switchTab(this.detailView, 'general');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the list view and hide detail view (with history push)
|
||||
*/
|
||||
private showListView(): void {
|
||||
// Push state to history (clear hash)
|
||||
history.pushState(
|
||||
{},
|
||||
'',
|
||||
window.location.pathname
|
||||
);
|
||||
this.showListViewInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show list view without modifying history (for popstate)
|
||||
*/
|
||||
private showListViewInternal(): void {
|
||||
if (this.listView && this.detailView) {
|
||||
this.detailView.style.display = 'none';
|
||||
this.listView.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue