Stable main pages

This commit is contained in:
2026-07-09 21:41:26 +02:00
parent 4daa2bfca4
commit 192772f0c2
15 changed files with 9171 additions and 190 deletions
@@ -1,7 +1,571 @@
@page "/compliance"
<h3>Compliance</h3>
@using System.Collections.Generic
@using System.Linq
@using System.Threading.Tasks
@rendermode InteractiveServer
<div class="bg-[#f4f7fb] min-h-screen text-[#1f2a3a] font-sans antialiased p-6 md:p-8">
@* Page Header Area *@
<div class="flex flex-col sm:flex-row sm:items-end justify-between mb-6 gap-4">
<div class="text-left">
<h2 class="text-2xl font-bold tracking-tight text-[#003b71]">Compliance &amp; Policy Control</h2>
<p class="text-xs text-[#5a6577] mt-1">Audit travel approvals, verify National Treasury compliance, and review POPIA privacy safeguards</p>
</div>
<div class="flex gap-2">
<button @onclick="OpenPolicyLibraryModal"
class="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-semibold bg-white border border-[#e4eaf2] text-[#1f2a3a] hover:bg-[#fafcfe] transition-colors shadow-sm cursor-pointer">
<!-- BookOpen SVG -->
<svg class="w-4 h-4 text-[#003b71]" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.967 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
</svg>
Policy Library
</button>
<button @onclick="TriggerFullComplianceAudit"
class="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-semibold bg-[#0066b3] text-white hover:bg-[#003b71] transition-colors shadow-sm cursor-pointer">
<!-- Sparkles Shield SVG -->
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.742 0 01-1.043 5.636 3.741 3.741 0 01-5.076.63 3.75 3.75 0 01-3.758-3.848c-.021-.013-.042-.027-.063-.04A4.5 4.5 0 113.6 8.775 5.25 5.25 0 0113.8 6.44a3 3 0 013.758 3.848A3.713 3.752 0 0118 12.75" />
</svg>
Run Compliance Audit
</button>
</div>
</div>
@* Alert / Feedback Notification Banner *@
@if (!string.IsNullOrEmpty(AlertFeedback))
{
<div class="mb-6 p-4 bg-[#effaf3] border-l-4 border-[#22a06b] rounded-r-lg text-sm text-[#1b8054] flex items-center gap-2.5 shadow-sm transition-all text-left">
<!-- Sparkles SVG -->
<svg class="w-5 h-5 shrink-0 text-[#22a06b]" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.742 0 01-1.043 5.636 3.741 3.741 0 01-5.076.63 3.75 3.75 0 01-3.758-3.848c-.021-.013-.042-.027-.063-.04A4.5 4.5 0 113.6 8.775 5.25 5.25 0 0113.8 6.44a3 3 0 013.758 3.848A3.713 3.752 0 0118 12.75" />
</svg>
<span class="font-medium">@AlertFeedback</span>
</div>
}
@* Scorecard Grid *@
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div class="bg-white border border-[#e4eaf2] rounded-[10px] p-[18px] shadow-sm relative overflow-hidden before:content-[''] before:absolute before:top-0 before:left-0 before:w-1 before:h-full before:bg-[#22a06b] text-left">
<div class="text-[11px] text-[#5a6577] uppercase tracking-[0.08em] font-semibold mb-2">Overall Compliance Rating</div>
<div class="text-2xl font-bold tracking-tight text-[#22a06b]">@OverallCompliance%</div>
<div class="flex items-center gap-1 mt-2 text-xs text-[#8a96a8]">
<span>National Treasury compliant</span>
</div>
</div>
<div class="bg-white border border-[#e4eaf2] rounded-[10px] p-[18px] shadow-sm relative overflow-hidden before:content-[''] before:absolute before:top-0 before:left-0 before:w-1 before:h-full before:bg-[#00a3a1] text-left">
<div class="text-[11px] text-[#5a6577] uppercase tracking-[0.08em] font-semibold mb-2">POPIA Security Safeguards</div>
<div class="text-2xl font-bold tracking-tight text-[#00a3a1]">Fully Certified</div>
<div class="flex items-center gap-1 mt-2 text-xs text-[#8a96a8]">
<span>ISO 27001 active framework</span>
</div>
</div>
<div class="bg-white border border-[#e4eaf2] rounded-[10px] p-[18px] shadow-sm relative overflow-hidden before:content-[''] before:absolute before:top-0 before:left-0 before:w-1 before:h-full before:bg-[#f4a300] text-left">
<div class="text-[11px] text-[#5a6577] uppercase tracking-[0.08em] font-semibold mb-2">Bookings Outside Policy</div>
<div class="text-2xl font-bold tracking-tight text-[#f4a300]">@OutsidePolicyCount</div>
<div class="flex items-center gap-1 mt-2 text-xs text-[#8a96a8]">
<span>Requires CFO/Exec approval</span>
</div>
</div>
<div class="bg-white border border-[#e4eaf2] rounded-[10px] p-[18px] shadow-sm relative overflow-hidden before:content-[''] before:absolute before:top-0 before:left-0 before:w-1 before:h-full before:bg-[#dc2626] text-left">
<div class="text-[11px] text-[#5a6577] uppercase tracking-[0.08em] font-semibold mb-2">Open Policy Anomalies</div>
<div class="text-2xl font-bold tracking-tight text-[#dc2626]">@AnomaliesCount</div>
<div class="flex items-center gap-1 mt-2 text-xs text-[#dc2626] font-semibold">
<span>Requires instant review</span>
</div>
</div>
</div>
@* Compliance split layout *@
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
@* Card 1: Scorecard & metrics *@
<div class="bg-white border border-[#e4eaf2] rounded-[10px] p-5 shadow-sm text-left flex flex-col justify-between">
<div>
<div class="mb-4">
<h3 class="text-[15px] font-bold">SAQA Travel Compliance Scorecard</h3>
<p class="text-xs text-[#8a96a8] mt-0.5">Live index measurements against SAQA policy rules and National Treasury Instruction No. 03</p>
</div>
<div class="space-y-4">
@* Metric 1 *@
<div class="space-y-1.5">
<div class="flex justify-between items-center text-xs">
<span class="font-semibold text-[#1f2a3a]">POPIA Privacy Compliance</span>
<span class="font-bold text-[#22a06b]">100% compliant</span>
</div>
<p class="text-[10px] text-[#8a96a8]">AES 256 bit encryption on travel documents & traveller records at rest & in transit.</p>
<div class="w-full bg-[#f4f7fb] h-1.5 rounded-full overflow-hidden">
<div class="bg-[#22a06b] h-1.5 rounded-full" style="width: 100%;"></div>
</div>
</div>
@* Metric 2 *@
<div class="space-y-1.5">
<div class="flex justify-between items-center text-xs">
<span class="font-semibold text-[#1f2a3a]">Cabin Class Limits Adherence</span>
<span class="font-bold text-[#22a06b]">96% adherence</span>
</div>
<p class="text-[10px] text-[#8a96a8]">Economy limit enforced strictly. Exceptional premium cabins tracked & logged.</p>
<div class="w-full bg-[#f4f7fb] h-1.5 rounded-full overflow-hidden">
<div class="bg-[#22a06b] h-1.5 rounded-full" style="width: 96%;"></div>
</div>
</div>
@* Metric 3 *@
<div class="space-y-1.5">
<div class="flex justify-between items-center text-xs">
<span class="font-semibold text-[#1f2a3a]">Hotel Rate Matrix Compliance</span>
<span class="font-bold text-[#f4a300]">78% compliance</span>
</div>
<p class="text-[10px] text-[#8a96a8]">Ensures 3 quotes per booking. Handful of peak-season bookings bypassed limit matrix.</p>
<div class="w-full bg-[#f4f7fb] h-1.5 rounded-full overflow-hidden">
<div class="bg-[#f4a300] h-1.5 rounded-full" style="width: 78%;"></div>
</div>
</div>
@* Metric 4 *@
<div class="space-y-1.5">
<div class="flex justify-between items-center text-xs">
<span class="font-semibold text-[#1f2a3a]">After-Hours Support SLA Response</span>
<span class="font-bold text-[#00a3a1]">98% performance</span>
</div>
<p class="text-[10px] text-[#8a96a8]">Emergency consultant call backs resolved in less than 15 minutes.</p>
<div class="w-full bg-[#f4f7fb] h-1.5 rounded-full overflow-hidden">
<div class="bg-[#00a3a1] h-1.5 rounded-full" style="width: 98%;"></div>
</div>
</div>
</div>
</div>
<div class="mt-6 pt-4 border-t border-[#e4eaf2] flex justify-between items-center text-xs text-[#8a96a8]">
<span>Sourced from GDS telemetry sync logs</span>
<span class="font-semibold text-[#1f2a3a]">Updated May 2026</span>
</div>
</div>
@* Card 2: Exceptions and Flags *@
<div class="bg-white border border-[#e4eaf2] rounded-[10px] p-5 shadow-sm text-left flex flex-col justify-between">
<div>
<div class="flex items-center justify-between mb-4 pb-1 border-b border-[#e4eaf2]">
<div>
<h3 class="text-[15px] font-bold">Exceptions &amp; Flagged Bookings</h3>
<p class="text-xs text-[#8a96a8] mt-0.5">Real-time alerts requiring human justification &amp; executive approval</p>
</div>
<span class="bg-[#fff4dd] text-[#b06b00] px-2.5 py-0.5 rounded-full text-[11px] font-semibold">Active Review</span>
</div>
<div class="space-y-3">
@foreach (var exc in ExceptionsList)
{
<div class="p-3 border rounded-lg flex items-start gap-3 transition-all hover:bg-[#fafcfe] @(exc.IsCritical ? "bg-[#fdeef0] border-[#f8b4b4]" : "bg-[#fff9ec] border-[#fbd897]")">
<div class="shrink-0 mt-0.5">
@if (exc.IsCritical)
{
<!-- Alert Triangle SVG -->
<svg class="w-5 h-5 text-[#dc2626]" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>
}
else
{
<!-- Info Shield SVG -->
<svg class="w-5 h-5 text-[#f4a300]" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
}
</div>
<div class="flex-1">
<div class="flex justify-between items-baseline mb-1">
<h4 class="text-xs font-bold text-[#1f2a3a]">@exc.Title</h4>
<span class="text-[9px] font-mono text-[#8a96a8]">@exc.Date</span>
</div>
<p class="text-xs text-[#5a6577]">@exc.Description</p>
<p class="text-[10px] text-[#8a96a8] mt-1 font-medium italic">Justification: @exc.Justification</p>
<div class="mt-2.5 flex items-center justify-between border-t border-[#e4eaf2]/40 pt-2">
<span class="text-[10px] font-bold text-[#003b71]">Owner: @exc.Traveller</span>
<div class="flex gap-2">
@if (exc.Status == "Pending")
{
<button @onclick="() => AuthorizeException(exc.Id)"
class="px-2 py-0.5 bg-[#22a06b] hover:bg-[#1b8054] text-white text-[10px] font-bold rounded shadow-xs cursor-pointer">
Approve
</button>
<button @onclick="() => RejectException(exc.Id)"
class="px-2 py-0.5 bg-white hover:bg-gray-100 border border-gray-300 text-gray-700 text-[10px] font-bold rounded shadow-xs cursor-pointer">
Reject
</button>
}
else
{
<span class="px-2 py-0.5 rounded text-[10px] font-bold @(exc.Status == "Approved" ? "bg-green-100 text-green-700" : "bg-red-100 text-red-700")">
@exc.Status
</span>
}
</div>
</div>
</div>
</div>
}
</div>
</div>
<div class="mt-4 pt-3 border-t border-[#e4eaf2] text-center">
<p class="text-[11px] text-[#8a96a8]">Flagged records automatically route alerts to executive dashboards upon booking completion.</p>
</div>
</div>
</div>
@* Compliance and Policy Library Grid Sections *@
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6 mb-6">
@* Security compliance cards - left side (lg:col-span-8) *@
<div class="lg:col-span-8 bg-white border border-[#e4eaf2] rounded-[10px] p-5 shadow-sm text-left">
<div class="flex items-center justify-between mb-4 border-b border-[#e4eaf2] pb-3">
<div>
<h3 class="text-base font-bold text-[#003b71]">Travel Data Privacy &amp; Cyber Safeguards</h3>
<p class="text-xs text-[#8a96a8] mt-0.5">POPIA compliance rules and robust data protection standards configured across platforms</p>
</div>
<span class="bg-[#e2eefb] text-[#0066b3] px-2.5 py-0.5 rounded-full text-[11px] font-semibold">ISO 27001</span>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
@* Safeguard 1 *@
<div class="p-3.5 border border-[#e4eaf2] rounded-xl bg-[#f4f7fb]/40">
<div class="h-9 w-9 rounded-lg bg-[#e2eefb] text-[#0066b3] flex items-center justify-center mb-3">
<!-- Key SVG -->
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z" />
</svg>
</div>
<h4 class="text-xs font-bold text-[#1f2a3a] uppercase tracking-wide">AES 256 Data Encryption</h4>
<p class="text-[11px] text-[#5a6577] mt-1.5">All passports, reward accounts, and credit card frameworks are shielded securely using military-grade AES-256 standard protocols.</p>
</div>
@* Safeguard 2 *@
<div class="p-3.5 border border-[#e4eaf2] rounded-xl bg-[#f4f7fb]/40">
<div class="h-9 w-9 rounded-lg bg-[#e3f6ed] text-[#22a06b] flex items-center justify-center mb-3">
<!-- ShieldCheck SVG -->
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.742 0 01-1.043 5.636 3.741 3.741 0 01-5.076.63 3.75 3.75 0 01-3.758-3.848c-.021-.013-.042-.027-.063-.04A4.5 4.5 0 113.6 8.775 5.25 5.25 0 0113.8 6.44a3 3 0 013.758 3.848A3.713 3.752 0 0118 12.75" />
</svg>
</div>
<h4 class="text-xs font-bold text-[#1f2a3a] uppercase tracking-wide">POPIA Information Safeguards</h4>
<p class="text-[11px] text-[#5a6577] mt-1.5">Strict privacy protocols verified. Consent tracking is logged on every user profile activation, complying with South African privacy laws.</p>
</div>
@* Safeguard 3 *@
<div class="p-3.5 border border-[#e4eaf2] rounded-xl bg-[#f4f7fb]/40">
<div class="h-9 w-9 rounded-lg bg-[#e0f7f6] text-[#00a3a1] flex items-center justify-center mb-3">
<!-- History SVG -->
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h4 class="text-xs font-bold text-[#1f2a3a] uppercase tracking-wide">7-Year Audit Trail Retained</h4>
<p class="text-[11px] text-[#5a6577] mt-1.5">Every booking, approval, modifier, or exception justification is timestamped and archived safely for external AGSA auditing purposes.</p>
</div>
@* Safeguard 4 *@
<div class="p-3.5 border border-[#e4eaf2] rounded-xl bg-[#f4f7fb]/40">
<div class="h-9 w-9 rounded-lg bg-[#fff4dd] text-[#f4a300] flex items-center justify-center mb-3">
<!-- Locked SVG -->
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" />
</svg>
</div>
<h4 class="text-xs font-bold text-[#1f2a3a] uppercase tracking-wide">ISO 27001 Certified Host</h4>
<p class="text-[11px] text-[#5a6577] mt-1.5">Data is hosted inside secure data centers. Monitored 24/7 with strict failover controls.</p>
</div>
</div>
</div>
@* Compliance reports generator - right side (lg:col-span-4) *@
<div class="lg:col-span-4 bg-white border border-[#e4eaf2] rounded-[10px] p-5 shadow-sm text-left flex flex-col justify-between">
<div>
<div class="mb-4">
<h3 class="text-[15px] font-bold">Standard SLA Report Outputs</h3>
<p class="text-xs text-[#8a96a8] mt-0.5">Export standard compliance matrices & cost containment breakdowns</p>
</div>
<div class="space-y-3">
@* Report row 1 *@
<div class="flex items-center justify-between p-2 rounded bg-[#f4f7fb] border border-[#e4eaf2]">
<div class="text-left">
<div class="text-xs font-bold text-[#1f2a3a]">National Treasury Matrix</div>
<span class="text-[10px] text-[#8a96a8]">Instruction 03 compliance logs</span>
</div>
<button @onclick='() => TriggerReportDownload("National Treasury Matrix")'
class="px-3 py-1 bg-white hover:bg-gray-50 text-xs font-bold border border-gray-300 text-[#1f2a3a] rounded transition">
Excel
</button>
</div>
@* Report row 2 *@
<div class="flex items-center justify-between p-2 rounded bg-[#f4f7fb] border border-[#e4eaf2]">
<div class="text-left">
<div class="text-xs font-bold text-[#1f2a3a]">POPIA Privacy Compliance Audit</div>
<span class="text-[10px] text-[#8a96a8]">Access log validation reports</span>
</div>
<button @onclick='() => TriggerReportDownload("POPIA Privacy Compliance Audit")'
class="px-3 py-1 bg-white hover:bg-gray-50 text-xs font-bold border border-gray-300 text-[#1f2a3a] rounded transition">
PDF
</button>
</div>
@* Report row 3 *@
<div class="flex items-center justify-between p-2 rounded bg-[#f4f7fb] border border-[#e4eaf2]">
<div class="text-left">
<div class="text-xs font-bold text-[#1f2a3a]">Executive Class Upgrade Log</div>
<span class="text-[10px] text-[#8a96a8]">Exemptions log with justifications</span>
</div>
<button @onclick='() => TriggerReportDownload("Executive Class Upgrade Log")'
class="px-3 py-1 bg-white hover:bg-gray-50 text-xs font-bold border border-gray-300 text-[#1f2a3a] rounded transition">
PDF
</button>
</div>
@* Report row 4 *@
<div class="flex items-center justify-between p-2 rounded bg-[#f4f7fb] border border-[#e4eaf2]">
<div class="text-left">
<div class="text-xs font-bold text-[#1f2a3a]">Standard Monthly SLA Review</div>
<span class="text-[10px] text-[#8a96a8]">Performance vs target response matrix</span>
</div>
<button @onclick='() => TriggerReportDownload("Standard Monthly SLA Review")'
class="px-3 py-1 bg-white hover:bg-gray-50 text-xs font-bold border border-gray-300 text-[#1f2a3a] rounded transition">
Excel
</button>
</div>
</div>
</div>
<div class="mt-4 pt-3.5 border-t border-[#e4eaf2]">
<p class="text-[10px] text-[#8a96a8]">Report metrics cover SAQA travel spend data for the May 2026 reporting cycle.</p>
</div>
</div>
</div>
</div>
@* ===================== MODAL DIALOGS ===================== *@
@* 1. Policy Library Reference Modal *@
@if (ShowPolicyLibraryModal)
{
<div class="fixed inset-0 bg-[#1f2a3a]/40 backdrop-blur-xs z-50 flex items-center justify-center p-4">
<div class="bg-white rounded-xl border border-[#e4eaf2] shadow-xl max-w-2xl w-full p-6 flex flex-col max-h-[90vh]">
<div class="flex items-center justify-between mb-4 pb-2 border-b border-[#e4eaf2]">
<div class="text-left">
<h3 class="text-base font-bold text-[#003b71]">SAQA Travel Policy Library</h3>
<p class="text-xs text-[#8a96a8]">Authorized compliance guidelines and cap matrices</p>
</div>
<button @onclick="ClosePolicyLibraryModal" class="text-[#8a96a8] hover:text-[#1f2a3a] cursor-pointer">
<!-- Close X SVG -->
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="flex-1 overflow-y-auto space-y-4 pr-1 text-sm text-[#1f2a3a]">
<div class="p-3 bg-[#e2eefb] text-[#0066b3] text-xs font-medium rounded-lg flex gap-2">
<!-- Info SVG -->
<svg class="w-4 h-4 shrink-0 mt-0.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="16" x2="12" y2="12" />
<line x1="12" y1="8" x2="12.01" y2="8" />
</svg>
<span>All bookings generated must comply with SAQA corporate governance policy and National Treasury frameworks. Exceptions require written CFO sign-off prior to ticketing.</span>
</div>
<div class="space-y-3">
<div class="p-3.5 border border-[#e4eaf2] rounded-lg">
<div class="font-bold text-[#003b71] text-xs uppercase mb-1">1. Airline Cabin Restrictions</div>
<p class="text-xs text-[#5a6577]">All local, regional, and international travel bookings are restricted to Economy Cabin flights. Premium Economy or Business classes require verified medical justifications from Human Resources, approved by the Chief Financial Officer.</p>
</div>
<div class="p-3.5 border border-[#e4eaf2] rounded-lg">
<div class="font-bold text-[#00a3a1] text-xs uppercase mb-1">2. Accommodation Rate Matrices</div>
<p class="text-xs text-[#5a6577]">Maximum nightly accommodation rates are aligned strictly with National Treasury Instruction No. 03 of 2016/17 (Cape Town maximum cap: R 1,900/night; Johannesburg: R 1,650/night). Minimum of 3 competitive quotes must be processed by the corporate booking tool prior to checkout.</p>
</div>
<div class="p-3.5 border border-[#e4eaf2] rounded-lg">
<div class="font-bold text-[#22a06b] text-xs uppercase mb-1">3. Ground Transportation Caps</div>
<p class="text-xs text-[#5a6577]">Car rental class is capped at Group B vehicles (e.g. automatic compact). Shuttles should be booked through verified pre-authorized local transfer vendors. Personal extension days differential billing is strictly monitored.</p>
</div>
<div class="p-3.5 border border-[#e4eaf2] rounded-lg">
<div class="font-bold text-[#f4a300] text-xs uppercase mb-1">4. Foreign Exchange &amp; Per Diems</div>
<p class="text-xs text-[#5a6577]">Daily travel allowances (per diems) are aligned with SARS guidelines for overseas destinations. Expenses must be OCR-submitted and synced within 7 working days post-trip completion.</p>
</div>
</div>
</div>
<div class="flex justify-end pt-4 border-t border-[#e4eaf2] mt-4">
<button @onclick="ClosePolicyLibraryModal"
class="px-4 py-2 rounded-lg text-sm font-semibold text-white bg-[#003b71] hover:bg-[#00264d] transition shadow-sm cursor-pointer">
Close Database
</button>
</div>
</div>
</div>
}
@code {
// Exception class model representation
public class TravelException
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Date { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Justification { get; set; } = string.Empty;
public string Traveller { get; set; } = string.Empty;
public string Status { get; set; } = "Pending";
public bool IsCritical { get; set; }
}
// Core States
private int OverallCompliance { get; set; } = 94;
private int OutsidePolicyCount { get; set; } = 7;
private int AnomaliesCount { get; set; } = 2;
private string AlertFeedback { get; set; } = string.Empty;
// Modal control
private bool ShowPolicyLibraryModal { get; set; } = false;
// Database arrays
private List<TravelException> ExceptionsList { get; set; } = new();
protected override void OnInitialized()
{
// Populate pre-loaded Exceptions & Policy alerts matching GDS parameters
ExceptionsList = new List<TravelException>
{
new TravelException
{
Id = 1,
Title = "Cabin Class Upgrade (BA JNB to LHR)",
Date = "14 May 2026",
Description = "T. Maluleke booked Premium Economy cabin on British Airways JNB-LHR route.",
Justification = "Medical reasons verified by SAQA HR and confirmed by executive CFO. Approved cabin class exception.",
Traveller = "T. Maluleke",
Status = "Approved",
IsCritical = false
},
new TravelException
{
Id = 2,
Title = "Hotel Rate Bypassed Limits (Cape Town Engagement)",
Date = "22 May 2026",
Description = "J. Mokoena nightly rate processed at R 2,400 per night vs maximum NT Cap matrix of R 1,900.",
Justification = "Peak season business engagement at Waterfront. No alternative hotels available within standard NT limits on short notice.",
Traveller = "J. Mokoena",
Status = "Pending",
IsCritical = true
},
new TravelException
{
Id = 3,
Title = "Leisure Extension differential check",
Date = "20 May 2026",
Description = "S. Pillay added 2 non-working personal days to domestic Durban travel itinerary.",
Justification = "Weekend stay extension. Airfare price differential & weekend hotel lodging billed personally to coordinator account.",
Traveller = "S. Pillay",
Status = "Approved",
IsCritical = false
}
};
}
// Interactive Exception Approvals
private void AuthorizeException(int id)
{
var exc = ExceptionsList.FirstOrDefault(e => e.Id == id);
if (exc != null)
{
exc.Status = "Approved";
if (exc.IsCritical)
{
AnomaliesCount = System.Math.Max(0, AnomaliesCount - 1);
OverallCompliance = System.Math.Min(100, OverallCompliance + 2);
}
TriggerFeedback($"Exception justification authorized successfully for {exc.Traveller}. Compliance indices recalculated.");
}
}
private void RejectException(int id)
{
var exc = ExceptionsList.FirstOrDefault(e => e.Id == id);
if (exc != null)
{
exc.Status = "Rejected";
if (exc.IsCritical)
{
AnomaliesCount = System.Math.Max(0, AnomaliesCount - 1);
}
TriggerFeedback($"Exception justification rejected for {exc.Traveller}. Compliance violation flagged for PO settlement collection.");
}
}
// Compliance Audit trigger
private async Task TriggerFullComplianceAudit()
{
TriggerFeedback("Analyzing Sync Logs... Querying 142 Active GDS PNR entries for policy compliance.");
await Task.Delay(1800);
OverallCompliance = 96;
AnomaliesCount = ExceptionsList.Count(e => e.Status == "Pending");
OutsidePolicyCount = ExceptionsList.Count;
TriggerFeedback("Compliance Telemetry Scan Finished! 94% overall compliance rate recalculated across all May 2026 travel bookings. All flags archived.");
}
// Report Downloads simulation
private void TriggerReportDownload(string reportName)
{
TriggerFeedback($"Compiling dataset... {reportName} download queued. System auto-generating document structure.");
}
// Modal Helpers
private void OpenPolicyLibraryModal()
{
ShowPolicyLibraryModal = true;
}
private void ClosePolicyLibraryModal()
{
ShowPolicyLibraryModal = false;
}
// Feedback alert triggered toaster
private void TriggerFeedback(string message)
{
AlertFeedback = message;
_ = DismissAlertAfterDelay();
}
private async Task DismissAlertAfterDelay()
{
await Task.Delay(5000);
AlertFeedback = string.Empty;
StateHasChanged();
}
}