Files
app/src/services/leave-notification.ts
BOHA 6b31b2f74b feat: system settings, dynamic logos, template numbering, permission consolidation
- System settings page with tabs: Security, System, Firma
- Configurable attendance rules (break thresholds, rounding) from DB
- Configurable document numbering with template patterns ({YYYY}/{PREFIX}/{NNN})
- Dynamic logo upload (light/dark variants) served from DB instead of static files
- Email settings (SMTP from/name, alert/leave emails) configurable in UI
- Currency and VAT rate lists configurable, used across all modules
- Permissions simplified: offers.settings + settings.roles + settings.security → settings.manage
- Leaflet bundled locally, removed unpkg.com from CSP
- Silent catch blocks fixed with proper logging
- console.log replaced with app.log.info in server.ts
- Schema renamed: company-settings.schema → settings.schema
- App info section: version, Node.js, uptime, memory, DB status, NAS status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:15:47 +01:00

113 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { sendMail } from "./mailer";
import { config } from "../config/env";
import { localDateCzStr, localDateTimeCzStr } from "../utils/date";
import { getSystemSettings } from "./system-settings";
const LEAVE_TYPE_LABELS: Record<string, string> = {
vacation: "Dovolená",
sick: "Nemocenská",
unpaid: "Neplacené volno",
};
function formatDate(dateStr: string): string {
try {
const d = new Date(dateStr);
return localDateCzStr(d);
} catch {
return dateStr;
}
}
function escapeHtml(str: string): string {
return str
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
interface LeaveRequestData {
leave_type: string;
date_from: string;
date_to: string;
total_days: number;
total_hours: number;
notes?: string | null;
}
export async function notifyNewLeaveRequest(
request: LeaveRequestData,
employeeName: string,
): Promise<void> {
const settings = await getSystemSettings();
const notifyEmail = settings.leave_notify_email || config.email.leaveNotify;
if (!notifyEmail) return;
const leaveType = LEAVE_TYPE_LABELS[request.leave_type] || request.leave_type;
const dateFrom = formatDate(request.date_from);
const dateTo = formatDate(request.date_to);
const notes = request.notes || "";
const subject = `Nová žádost o nepřítomnost - ${employeeName} (${leaveType})`;
const appUrl = config.appUrl || "";
const approvalLink = appUrl
? `<p style="margin-top: 20px;">
<a href="${escapeHtml(appUrl)}/leave-approval"
style="background: #de3a3a; color: #fff; padding: 10px 20px;
text-decoration: none; border-radius: 5px;">
Přejít ke schvalování
</a>
</p>`
: "";
const now = new Date();
const timestamp = localDateTimeCzStr(now);
const html = `
<html>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<h2 style="color: #de3a3a;">Nová žádost o nepřítomnost</h2>
<table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
<tr>
<td style="padding: 10px; background: #f5f5f5; font-weight: bold; width: 180px;">Zaměstnanec:</td>
<td style="padding: 10px; border-bottom: 1px solid #ddd;">${escapeHtml(employeeName)}</td>
</tr>
<tr>
<td style="padding: 10px; background: #f5f5f5; font-weight: bold;">Typ:</td>
<td style="padding: 10px; border-bottom: 1px solid #ddd;">${escapeHtml(leaveType)}</td>
</tr>
<tr>
<td style="padding: 10px; background: #f5f5f5; font-weight: bold;">Období:</td>
<td style="padding: 10px; border-bottom: 1px solid #ddd;">${dateFrom} ${dateTo}</td>
</tr>
<tr>
<td style="padding: 10px; background: #f5f5f5; font-weight: bold;">Pracovní dny:</td>
<td style="padding: 10px; border-bottom: 1px solid #ddd;">${request.total_days} dní (${request.total_hours} hodin)</td>
</tr>
${
notes
? `<tr>
<td style="padding: 10px; background: #f5f5f5; font-weight: bold;">Poznámka:</td>
<td style="padding: 10px; border-bottom: 1px solid #ddd;">${escapeHtml(notes)}</td>
</tr>`
: ""
}
</table>
${approvalLink}
<hr style="margin: 30px 0; border: none; border-top: 1px solid #ddd;">
<p style="font-size: 12px; color: #999;">
Tato zpráva byla automaticky vygenerována systémem.<br>
Datum: ${timestamp}
</p>
</body>
</html>`;
const sent = await sendMail(notifyEmail, subject, html);
if (!sent) {
console.error(
`LeaveNotification: Failed to send notification to ${notifyEmail}`,
);
}
}