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
|
|
@ -890,6 +890,7 @@
|
|||
// wwwroot/ts/modules/employees.ts
|
||||
var EmployeesController = class {
|
||||
constructor() {
|
||||
this.ratesSync = null;
|
||||
this.listView = null;
|
||||
this.detailView = null;
|
||||
this.listView = document.getElementById("employees-list-view");
|
||||
|
|
@ -901,6 +902,7 @@
|
|||
this.setupBackNavigation();
|
||||
this.setupHistoryNavigation();
|
||||
this.restoreStateFromUrl();
|
||||
this.ratesSync = new RatesSyncController();
|
||||
}
|
||||
/**
|
||||
* Setup popstate listener for browser back/forward
|
||||
|
|
@ -1041,6 +1043,93 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
var RatesSyncController = class {
|
||||
constructor() {
|
||||
this.drawer = null;
|
||||
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")
|
||||
*/
|
||||
extractRateKey(checkboxId) {
|
||||
const match = checkboxId.match(/^rate-(.+)-enabled$/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
/**
|
||||
* Setup checkbox change listeners in drawer
|
||||
*/
|
||||
setupCheckboxListeners() {
|
||||
if (!this.drawer) return;
|
||||
this.drawer.addEventListener("change", (e) => {
|
||||
const target = e.target;
|
||||
if (target.type !== "checkbox" || !target.id) return;
|
||||
const rateKey = this.extractRateKey(target.id);
|
||||
if (!rateKey) return;
|
||||
const isChecked = target.checked;
|
||||
const row = target.closest("swp-data-row");
|
||||
if (!row) return;
|
||||
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);
|
||||
this.toggleCardRow(rateKey, isChecked);
|
||||
if (isChecked) {
|
||||
const textInput = document.getElementById(`rate-${rateKey}`);
|
||||
if (textInput) {
|
||||
this.syncValueToCard(rateKey, textInput.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Setup input change listeners in drawer
|
||||
*/
|
||||
setupInputListeners() {
|
||||
if (!this.drawer) return;
|
||||
this.drawer.addEventListener("input", (e) => {
|
||||
const target = e.target;
|
||||
if (target.type !== "text" || !target.id) return;
|
||||
const match = target.id.match(/^rate-(.+)$/);
|
||||
if (!match) return;
|
||||
const rateKey = match[1];
|
||||
if (rateKey.endsWith("-enabled")) return;
|
||||
this.syncValueToCard(rateKey, target.value);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Toggle card row visibility by ID
|
||||
*/
|
||||
toggleCardRow(rateKey, visible) {
|
||||
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)
|
||||
*/
|
||||
formatNumber(value) {
|
||||
const normalized = value.replace(",", ".");
|
||||
const num = parseFloat(normalized);
|
||||
if (isNaN(num)) return value;
|
||||
return num.toFixed(2).replace(".", ",");
|
||||
}
|
||||
/**
|
||||
* Sync value from drawer to card by ID
|
||||
*/
|
||||
syncValueToCard(rateKey, value) {
|
||||
const cardInput = document.getElementById(`value-${rateKey}`);
|
||||
if (!cardInput) return;
|
||||
const textInput = document.getElementById(`rate-${rateKey}`);
|
||||
const inputContainer = textInput?.closest("swp-data-input");
|
||||
const unit = inputContainer?.textContent?.trim().replace(value, "").trim() || "kr";
|
||||
const formattedValue = this.formatNumber(value);
|
||||
cardInput.value = `${formattedValue} ${unit}`;
|
||||
}
|
||||
};
|
||||
|
||||
// wwwroot/ts/app.ts
|
||||
var App = class {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue