Enhances employee details with comprehensive salary and HR data
Adds detailed salary rates, commission structures, and HR-related records Introduces new data models and view components for: - Salary rates and supplements - Commissions and rate configurations - Employee HR tracking (certifications, courses, absence) Implements dynamic rate synchronization between drawer and card views
This commit is contained in:
parent
2e6207bb0b
commit
f71f00099a
15 changed files with 1589 additions and 137 deletions
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
export class EmployeesController {
|
||||
private ratesSync: RatesSyncController | null = null;
|
||||
private listView: HTMLElement | null = null;
|
||||
private detailView: HTMLElement | null = null;
|
||||
|
||||
|
|
@ -23,6 +24,7 @@ export class EmployeesController {
|
|||
this.setupBackNavigation();
|
||||
this.setupHistoryNavigation();
|
||||
this.restoreStateFromUrl();
|
||||
this.ratesSync = new RatesSyncController();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -189,3 +191,132 @@ export class EmployeesController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rates Sync Controller
|
||||
*
|
||||
* Syncs changes between the rates drawer and the salary tab cards.
|
||||
* Uses ID-based lookups:
|
||||
* - Checkbox: id="rate-{key}-enabled"
|
||||
* - Text input: id="rate-{key}"
|
||||
* - Card row: id="card-{key}"
|
||||
*/
|
||||
class RatesSyncController {
|
||||
private drawer: HTMLElement | null = null;
|
||||
|
||||
constructor() {
|
||||
this.drawer = document.getElementById('rates-drawer');
|
||||
|
||||
if (!this.drawer) return;
|
||||
|
||||
this.setupCheckboxListeners();
|
||||
this.setupInputListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract rate key from checkbox ID (e.g., "rate-normal-enabled" → "normal")
|
||||
*/
|
||||
private extractRateKey(checkboxId: string): string | null {
|
||||
const match = checkboxId.match(/^rate-(.+)-enabled$/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup checkbox change listeners in drawer
|
||||
*/
|
||||
private setupCheckboxListeners(): void {
|
||||
if (!this.drawer) return;
|
||||
|
||||
this.drawer.addEventListener('change', (e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
if (target.type !== 'checkbox' || !target.id) return;
|
||||
|
||||
const rateKey = this.extractRateKey(target.id);
|
||||
if (!rateKey) return;
|
||||
|
||||
const isChecked = target.checked;
|
||||
const row = target.closest<HTMLElement>('swp-data-row');
|
||||
if (!row) return;
|
||||
|
||||
// Toggle disabled class in drawer row
|
||||
const label = row.querySelector('swp-data-label');
|
||||
const input = row.querySelector('swp-data-input');
|
||||
if (label) label.classList.toggle('disabled', !isChecked);
|
||||
if (input) input.classList.toggle('disabled', !isChecked);
|
||||
|
||||
// Toggle visibility in card
|
||||
this.toggleCardRow(rateKey, isChecked);
|
||||
|
||||
// If enabling, also sync the current value
|
||||
if (isChecked) {
|
||||
const textInput = document.getElementById(`rate-${rateKey}`) as HTMLInputElement | null;
|
||||
if (textInput) {
|
||||
this.syncValueToCard(rateKey, textInput.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup input change listeners in drawer
|
||||
*/
|
||||
private setupInputListeners(): void {
|
||||
if (!this.drawer) return;
|
||||
|
||||
this.drawer.addEventListener('input', (e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
if (target.type !== 'text' || !target.id) return;
|
||||
|
||||
// Extract rate key from input ID (e.g., "rate-normal" → "normal")
|
||||
const match = target.id.match(/^rate-(.+)$/);
|
||||
if (!match) return;
|
||||
|
||||
const rateKey = match[1];
|
||||
// Skip if this matches the checkbox pattern
|
||||
if (rateKey.endsWith('-enabled')) return;
|
||||
|
||||
this.syncValueToCard(rateKey, target.value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle card row visibility by ID
|
||||
*/
|
||||
private toggleCardRow(rateKey: string, visible: boolean): void {
|
||||
const cardRow = document.getElementById(`card-${rateKey}`);
|
||||
if (cardRow) {
|
||||
cardRow.style.display = visible ? '' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format number with 2 decimals using Danish locale (comma as decimal separator)
|
||||
*/
|
||||
private formatNumber(value: string): string {
|
||||
// Parse the input (handle both dot and comma as decimal separator)
|
||||
const normalized = value.replace(',', '.');
|
||||
const num = parseFloat(normalized);
|
||||
|
||||
if (isNaN(num)) return value;
|
||||
|
||||
// Format with 2 decimals and comma as decimal separator
|
||||
return num.toFixed(2).replace('.', ',');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync value from drawer to card by ID
|
||||
*/
|
||||
private syncValueToCard(rateKey: string, value: string): void {
|
||||
const cardInput = document.getElementById(`value-${rateKey}`) as HTMLInputElement | null;
|
||||
if (!cardInput) return;
|
||||
|
||||
// Get the unit from drawer input container
|
||||
const textInput = document.getElementById(`rate-${rateKey}`);
|
||||
const inputContainer = textInput?.closest('swp-data-input');
|
||||
const unit = inputContainer?.textContent?.trim().replace(value, '').trim() || 'kr';
|
||||
|
||||
// Format with 2 decimals
|
||||
const formattedValue = this.formatNumber(value);
|
||||
cardInput.value = `${formattedValue} ${unit}`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue