{"id":3208,"date":"2025-07-21T08:56:15","date_gmt":"2025-07-21T06:56:15","guid":{"rendered":"https:\/\/j-cred.co.za\/?page_id=3208"},"modified":"2025-07-21T09:05:01","modified_gmt":"2025-07-21T07:05:01","slug":"ivms","status":"publish","type":"page","link":"https:\/\/j-cred.co.za\/zh\/ivms\/","title":{"rendered":"IVMS"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"3208\" class=\"elementor elementor-3208\" data-elementor-settings=\"{&quot;ha_cmc_init_switcher&quot;:&quot;no&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-148dced e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no e-con e-parent\" data-id=\"148dced\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;,&quot;_ha_eqh_enable&quot;:false}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-e8fd347 e-grid e-con-full wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no e-con e-child\" data-id=\"e8fd347\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;_ha_eqh_enable&quot;:false}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ef9bfd7 elementor-widget elementor-widget-image\" data-id=\"ef9bfd7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img fetchpriority=\"high\" decoding=\"async\" width=\"1032\" height=\"323\" src=\"https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/J-Cred-G2C-Logo.webp\" class=\"attachment-full size-full wp-image-3223\" alt=\"\" srcset=\"https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/J-Cred-G2C-Logo.webp 1032w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/J-Cred-G2C-Logo-300x94.webp 300w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/J-Cred-G2C-Logo-1024x320.webp 1024w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/J-Cred-G2C-Logo-768x240.webp 768w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/J-Cred-G2C-Logo-18x6.webp 18w\" sizes=\"(max-width: 1032px) 100vw, 1032px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-728ecb6 elementor-widget elementor-widget-image\" data-id=\"728ecb6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1875\" height=\"203\" src=\"https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/KTO-Digital-Innovate.-Disrupt.-Transform.webp\" class=\"attachment-full size-full wp-image-3326\" alt=\"\" srcset=\"https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/KTO-Digital-Innovate.-Disrupt.-Transform.webp 1875w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/KTO-Digital-Innovate.-Disrupt.-Transform-300x32.webp 300w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/KTO-Digital-Innovate.-Disrupt.-Transform-1024x111.webp 1024w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/KTO-Digital-Innovate.-Disrupt.-Transform-768x83.webp 768w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/KTO-Digital-Innovate.-Disrupt.-Transform-1536x166.webp 1536w, https:\/\/j-cred.co.za\/wp-content\/uploads\/2025\/07\/KTO-Digital-Innovate.-Disrupt.-Transform-18x2.webp 18w\" sizes=\"(max-width: 1875px) 100vw, 1875px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-e758937 e-con-full e-flex wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no e-con e-parent\" data-id=\"e758937\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;_ha_eqh_enable&quot;:false}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-8fc2d34 elementor-widget elementor-widget-html\" data-id=\"8fc2d34\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>IVMS<\/title>\r\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\r\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">\r\n    <style>\r\n        body {\r\n            font-family: 'Inter', sans-serif;\r\n            background-color: #f4f7fa;\r\n        }\r\n        .modal-backdrop {\r\n            background-color: rgba(0,0,0,0.6);\r\n            transition: opacity 0.3s ease;\r\n        }\r\n        .modal-content {\r\n            transition: transform 0.3s ease, opacity 0.3s ease;\r\n        }\r\n        .timeline-item::before {\r\n            content: '';\r\n            position: absolute;\r\n            left: 19px;\r\n            top: 48px;\r\n            bottom: 0;\r\n            width: 2px;\r\n            background-color: #e2e8f0;\r\n        }\r\n        .timeline-item:last-child::before { display: none; }\r\n        .status-badge {\r\n            display: inline-block;\r\n            padding: 0.25rem 0.75rem;\r\n            border-radius: 9999px;\r\n            font-weight: 600;\r\n            font-size: 0.75rem;\r\n        }\r\n        .status-pending-verification { background-color: #fef3c7; color: #92400e; }\r\n        .status-pending-assessment { background-color: #dbeafe; color: #1e40af; }\r\n        .status-pending-approval { background-color: #fee2e2; color: #991b1b; }\r\n        .status-approved { background-color: #dcfce7; color: #166534; }\r\n        .status-rejected { background-color: #e5e7eb; color: #374151; }\r\n        .nav-link.active { background-color: #2563eb; color: white; }\r\n        .step-indicator.active {\r\n            background-color: #2563eb;\r\n            color: white;\r\n            border-color: #2563eb;\r\n        }\r\n         .step-indicator.completed {\r\n            background-color: #16a34a;\r\n            color: white;\r\n            border-color: #16a34a;\r\n        }\r\n        #signature-pad {\r\n            border: 2px dashed #cbd5e1;\r\n            cursor: crosshair;\r\n        }\r\n    <\/style>\r\n<\/head>\r\n<body class=\"text-gray-800\">\r\n\r\n    <!-- Landing Page View -->\r\n    <div id=\"landing-page\">\r\n        <div class=\"bg-gray-900 text-white\">\r\n            <nav class=\"container mx-auto px-6 py-4 flex justify-between items-center\">\r\n                <h1 class=\"text-2xl font-bold\">IVMS<\/h1>\r\n                <button id=\"show-login-modal-btn\" class=\"bg-blue-600 font-bold py-2 px-5 rounded-md hover:bg-blue-700 transition-colors\">Login to Portal<\/button>\r\n            <\/nav>\r\n            <div class=\"container mx-auto px-6 py-24 text-center\">\r\n                <h2 class=\"text-5xl font-extrabold\">The National Standard for Municipal Sustainability.<\/h2>\r\n                <p class=\"mt-6 text-xl text-gray-300 max-w-3xl mx-auto\">Engineered for compliance and designed for impact, the Indigent Verification Management System (IVMS) is restoring data integrity and financial viability to local government across South Africa.<\/p>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <div class=\"py-20 bg-white\">\r\n            <div class=\"container mx-auto px-6\">\r\n                <div class=\"text-center\">\r\n                    <h3 class=\"text-4xl font-bold text-gray-900\">Solving the Core of Systemic Failure<\/h3>\r\n                    <p class=\"mt-4 text-lg text-gray-600 max-w-3xl mx-auto\">The municipal crisis was never a lack of will, but a crisis of data integrity. IVMS was designed to solve the four key systemic disconnections that undermined past efforts.<\/p>\r\n                <\/div>\r\n                <div class=\"mt-12 grid md:grid-cols-2 lg:grid-cols-4 gap-8\">\r\n                    <div class=\"p-8 bg-gray-50 rounded-lg\">\r\n                        <h4 class=\"text-xl font-bold\">Fragmented Systems<\/h4>\r\n                        <p class=\"mt-2 text-gray-600\">IVMS replaces siloed processes with a single, authoritative platform, creating one source of truth for indigent management.<\/p>\r\n                    <\/div>\r\n                    <div class=\"p-8 bg-gray-50 rounded-lg\">\r\n                        <h4 class=\"text-xl font-bold\">Compliance as an Afterthought<\/h4>\r\n                        <p class=\"mt-2 text-gray-600\">Built on a foundation of MSA and MFMA policy, IVMS ensures every step of the workflow is inherently compliant.<\/p>\r\n                    <\/div>\r\n                    <div class=\"p-8 bg-gray-50 rounded-lg\">\r\n                        <h4 class=\"text-xl font-bold\">No Actionable Intelligence<\/h4>\r\n                        <p class=\"mt-2 text-gray-600\">The system translates verified data into clear, prioritized actions that enable effective decision-making.<\/p>\r\n                    <\/div>\r\n                    <div class=\"p-8 bg-gray-50 rounded-lg\">\r\n                        <h4 class=\"text-xl font-bold\">Citizen Disengagement<\/h4>\r\n                        <p class=\"mt-2 text-gray-600\">By ensuring a fair, transparent, and auditable process, IVMS rebuilds the trust that is essential for a healthy culture of payment.<\/p>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n        \r\n        <div class=\"py-20\">\r\n             <div class=\"container mx-auto px-6\">\r\n                <div class=\"text-center\">\r\n                    <h3 class=\"text-4xl font-bold text-gray-900\">The IVMS: A Proven Foundational Solution<\/h3>\r\n                    <p class=\"mt-4 text-lg text-gray-600 max-w-3xl mx-auto\">IVMS creates a single, auditable source of truth for indigent management\u2014the cornerstone of fair service delivery and revenue protection for municipalities nationwide.<\/p>\r\n                <\/div>\r\n                <div class=\"mt-12 max-w-4xl mx-auto bg-white p-10 rounded-xl shadow-lg border\">\r\n                    <ul class=\"space-y-6\">\r\n                        <li class=\"flex items-start\"><span class=\"text-2xl text-green-500 mr-4\">&#10003;<\/span><div><h5 class=\"font-semibold\">Engineered for Compliance<\/h5><p class=\"text-gray-600\">Built from the ground up on MSA, MFMA, and POPIA principles, ensuring every workflow is inherently compliant.<\/p><\/div><\/li>\r\n                        <li class=\"flex items-start\"><span class=\"text-2xl text-green-500 mr-4\">&#10003;<\/span><div><h5 class=\"font-semibold\">Restores Data Integrity<\/h5><p class=\"text-gray-600\">As a Registered Credit Bureau, we verify applicant data against trusted national sources, eliminating \"garbage in, garbage out.\"<\/p><\/div><\/li>\r\n                        <li class=\"flex items-start\"><span class=\"text-2xl text-green-500 mr-4\">&#10003;<\/span><div><h5 class=\"font-semibold\">Bridges the Digital Divide<\/h5><p class=\"text-gray-600\">A hybrid model using field agents with tablets ensures all citizens can apply, regardless of digital access or literacy.<\/p><\/div><\/li>\r\n                        <li class=\"flex items-start\"><span class=\"text-2xl text-green-500 mr-4\">&#10003;<\/span><div><h5 class=\"font-semibold\">Creates an Auditable Trail<\/h5><p class=\"text-gray-600\">Every action, from capture to final decision, is logged, creating a clean, defensible record for the Auditor-General.<\/p><\/div><\/li>\r\n                    <\/ul>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n        <footer class=\"bg-gray-800 text-white py-6\">\r\n            <div class=\"container mx-auto px-6 text-center\">\r\n                <p class=\"text-gray-400\">Powered by SALGA<\/p>\r\n                <a href=\"https:\/\/j-cred.co.za\" target=\"_blank\" class=\"text-blue-400 hover:underline\">j-cred.co.za<\/a>\r\n            <\/div>\r\n        <\/footer>\r\n    <\/div>\r\n\r\n    <!-- Login Modal -->\r\n    <div id=\"login-modal\" class=\"fixed inset-0 z-50 hidden items-center justify-center modal-backdrop\">\r\n        <div class=\"bg-white rounded-lg shadow-2xl w-full max-w-sm mx-4 p-8 modal-content transform scale-95 opacity-0\">\r\n            <h3 class=\"text-2xl font-bold text-center mb-6\">Login to IVMS<\/h3>\r\n            <div class=\"space-y-6\">\r\n                <div>\r\n                    <label for=\"login-role-switcher\" class=\"block text-sm font-medium text-gray-700\">Select Your Role<\/label>\r\n                    <select id=\"login-role-switcher\" class=\"mt-1 block w-full p-3 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500\">\r\n                        <option value=\"agent\">IVMS Agent<\/option>\r\n                        <option value=\"clerk\">Municipal Clerk<\/option>\r\n                        <option value=\"assessor\">Assessor<\/option>\r\n                        <option value=\"manager\">Manager<\/option>\r\n                        <option value=\"admin\">SALGA Admin<\/option>\r\n                    <\/select>\r\n                <\/div>\r\n                <div>\r\n                    <label for=\"password\" class=\"block text-sm font-medium text-gray-700\">Password<\/label>\r\n                    <input type=\"password\" id=\"password\" class=\"mt-1 block w-full p-3 border border-gray-300 rounded-md shadow-sm\" value=\"password\">\r\n                <\/div>\r\n                <button id=\"login-btn\" class=\"w-full bg-blue-600 text-white font-bold py-3 px-4 rounded-md hover:bg-blue-700 transition-colors\">Login<\/button>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <!-- Main Portal View -->\r\n    <div id=\"portal-view\" class=\"hidden\">\r\n        <div class=\"flex h-screen bg-gray-100\">\r\n            <!-- Left Navigation -->\r\n            <aside class=\"w-64 bg-gray-800 text-white flex flex-col\">\r\n                <div class=\"h-16 flex items-center justify-center text-2xl font-bold border-b border-gray-700\">IVMS<\/div>\r\n                <nav id=\"portal-nav\" class=\"flex-1 p-4 space-y-2\">\r\n                    <!-- Nav links will be populated by JS -->\r\n                <\/nav>\r\n                <div class=\"p-4 border-t border-gray-700\">\r\n                    <p id=\"user-role-display\" class=\"font-semibold\"><\/p>\r\n                    <div id=\"user-municipality-info\" class=\"text-xs text-gray-400 mt-1\"><\/div>\r\n                    <a href=\"#\" id=\"logout-btn\" class=\"text-sm text-gray-400 hover:text-white mt-2 inline-block\">Logout<\/a>\r\n                <\/div>\r\n            <\/aside>\r\n\r\n            <!-- Main Content Area -->\r\n            <div class=\"flex-1 flex flex-col overflow-hidden\">\r\n                <header class=\"h-16 bg-white border-b border-gray-200 flex items-center px-8\">\r\n                    <h2 id=\"portal-header\" class=\"text-xl font-semibold text-gray-800\">Dashboard<\/h2>\r\n                <\/header>\r\n                <main id=\"portal-content\" class=\"flex-1 overflow-x-hidden overflow-y-auto bg-gray-100 p-8\">\r\n                    <!-- Content for the selected view will be rendered here -->\r\n                <\/main>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n    \r\n    <!-- Modal for IVMS Actions -->\r\n    <div id=\"action-modal\" class=\"fixed inset-0 z-50 hidden items-center justify-center modal-backdrop\">\r\n        <div class=\"bg-white rounded-lg shadow-2xl w-full max-w-lg mx-4 modal-content transform scale-95 opacity-0\">\r\n            <div class=\"px-6 py-4 border-b border-gray-200\"><h3 id=\"modal-title\" class=\"text-lg font-semibold\">Action Title<\/h3><\/div>\r\n            <div id=\"modal-body\" class=\"p-6\"><\/div>\r\n            <div class=\"px-6 py-4 bg-gray-50 flex justify-end space-x-3\">\r\n                <button id=\"modal-cancel-btn\" class=\"bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded-md hover:bg-gray-300\">Cancel<\/button>\r\n                <button id=\"modal-confirm-btn\" class=\"bg-green-600 text-white font-bold py-2 px-4 rounded-md hover:bg-green-700\">Confirm<\/button>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <!-- Modal for Signature -->\r\n    <div id=\"signature-modal\" class=\"fixed inset-0 z-50 hidden items-center justify-center modal-backdrop\">\r\n        <div class=\"bg-white rounded-lg shadow-2xl w-full max-w-lg mx-4 modal-content transform scale-95 opacity-0\">\r\n            <div class=\"px-6 py-4 border-b border-gray-200\"><h3 id=\"signature-modal-title\" class=\"text-lg font-semibold\">Signature<\/h3><\/div>\r\n            <div class=\"p-6\">\r\n                <canvas id=\"signature-pad\" width=\"460\" height=\"200\"><\/canvas>\r\n                <p class=\"text-xs text-gray-500 mt-2\">Please sign in the box above.<\/p>\r\n            <\/div>\r\n            <div class=\"px-6 py-4 bg-gray-50 flex justify-end space-x-3\">\r\n                <button id=\"sig-clear-btn\" class=\"bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded-md hover:bg-gray-300\">Clear<\/button>\r\n                <button id=\"sig-save-btn\" class=\"bg-blue-600 text-white font-bold py-2 px-4 rounded-md hover:bg-blue-700\">Save Signature<\/button>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n    \r\n    <!-- Modal for OTP -->\r\n    <div id=\"otp-modal\" class=\"fixed inset-0 z-50 hidden items-center justify-center modal-backdrop\">\r\n        <div class=\"bg-white rounded-lg shadow-2xl w-full max-w-sm mx-4 p-8 modal-content transform scale-95 opacity-0\">\r\n            <h3 class=\"text-2xl font-bold text-center mb-4\">Final Submission<\/h3>\r\n            <p class=\"text-center text-gray-600 mb-6\">An OTP has been sent to your device. Please enter it below to submit the application.<\/p>\r\n            <div>\r\n                <label for=\"otp-input\" class=\"block text-sm font-medium text-gray-700\">Enter 4-Digit OTP<\/label>\r\n                <input type=\"text\" id=\"otp-input\" class=\"mt-1 block w-full p-3 border border-gray-300 rounded-md shadow-sm\" maxlength=\"4\">\r\n                <p id=\"otp-error\" class=\"text-red-500 text-sm mt-2 hidden\">Invalid OTP. Please try again.<\/p>\r\n            <\/div>\r\n            <div class=\"mt-6 flex justify-end space-x-3\">\r\n                <button id=\"otp-cancel-btn\" class=\"bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded-md hover:bg-gray-300\">Cancel<\/button>\r\n                <button id=\"otp-verify-btn\" class=\"bg-green-600 text-white font-bold py-2 px-4 rounded-md hover:bg-green-700\">Verify & Submit<\/button>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n\r\n    <script>\r\n    document.addEventListener('DOMContentLoaded', () => {\r\n        const App = {\r\n            state: {\r\n                isLoggedIn: false,\r\n                currentUser: null,\r\n                applications: [],\r\n                users: [],\r\n                municipalities: {},\r\n                unsyncedApplications: 3,\r\n                selectedApplicationId: null,\r\n                currentPortalView: 'dashboard',\r\n                newApplicationData: {},\r\n                newApplicationCurrentStep: 1,\r\n                signatureCallback: null,\r\n                otpCallback: null,\r\n            },\r\n\r\n            init() {\r\n                this.generateMockData();\r\n                this.attachEventListeners();\r\n                this.render();\r\n            },\r\n\r\n            attachEventListeners() {\r\n                document.getElementById('show-login-modal-btn').addEventListener('click', () => this.showLoginModal());\r\n                document.getElementById('login-btn').addEventListener('click', () => this.handleLogin());\r\n                document.getElementById('logout-btn').addEventListener('click', () => this.handleLogout());\r\n                document.getElementById('portal-nav').addEventListener('click', (e) => {\r\n                    const link = e.target.closest('a');\r\n                    if (link) {\r\n                        e.preventDefault();\r\n                        if (link.dataset.view === 'sync') {\r\n                            this.handleSync(link);\r\n                        } else {\r\n                            this.state.currentPortalView = link.dataset.view;\r\n                            this.render();\r\n                        }\r\n                    }\r\n                });\r\n                document.getElementById('modal-cancel-btn').addEventListener('click', () => this.hideActionModal());\r\n            },\r\n\r\n            render() {\r\n                if (this.state.isLoggedIn) {\r\n                    document.getElementById('landing-page').classList.add('hidden');\r\n                    document.getElementById('portal-view').classList.remove('hidden');\r\n                    this.renderPortal();\r\n                } else {\r\n                    document.getElementById('landing-page').classList.remove('hidden');\r\n                    document.getElementById('portal-view').classList.add('hidden');\r\n                }\r\n            },\r\n            \r\n            renderPortal() {\r\n                const navContainer = document.getElementById('portal-nav');\r\n                const contentContainer = document.getElementById('portal-content');\r\n                const header = document.getElementById('portal-header');\r\n                const user = this.state.currentUser;\r\n\r\n                document.getElementById('user-role-display').textContent = user.name;\r\n                const muniInfoEl = document.getElementById('user-municipality-info');\r\n                if (user.role !== 'admin') {\r\n                    muniInfoEl.innerHTML = `\r\n                        <p>${user.municipality}<\/p>\r\n                        <p>${user.province}<\/p>\r\n                        <a href=\"${user.website}\" target=\"_blank\" class=\"text-blue-400 hover:text-blue-300 underline\">Website<\/a>\r\n                    `;\r\n                } else {\r\n                    muniInfoEl.innerHTML = `<p>National Oversight<\/p>`;\r\n                }\r\n\r\n                let navLinks = '';\r\n                if (user.role === 'admin') {\r\n                    navLinks = `\r\n                        <a href=\"#\" data-view=\"compliance\" class=\"nav-link block py-2.5 px-4 rounded transition duration-200 hover:bg-blue-700\">Compliance Dashboard<\/a>\r\n                        <a href=\"#\" data-view=\"user_management\" class=\"nav-link block py-2.5 px-4 rounded transition duration-200 hover:bg-blue-700\">User Management<\/a>\r\n                    `;\r\n                } else {\r\n                    navLinks = `<a href=\"#\" data-view=\"dashboard\" class=\"nav-link block py-2.5 px-4 rounded transition duration-200 hover:bg-blue-700\">Applications<\/a>`;\r\n                    if (user.role === 'agent' || user.role === 'clerk') {\r\n                        const badge = this.state.unsyncedApplications > 0 ? `<span class=\"bg-red-500 text-white text-xs font-bold rounded-full px-2 py-0.5 ml-auto\">${this.state.unsyncedApplications}<\/span>` : '';\r\n                        navLinks += `<a href=\"#\" data-view=\"sync\" class=\"nav-link flex items-center py-2.5 px-4 rounded transition duration-200 hover:bg-blue-700\">Sync Applications ${badge}<\/a>`;\r\n                    }\r\n                }\r\n                navContainer.innerHTML = navLinks;\r\n                \r\n                navContainer.querySelectorAll('.nav-link').forEach(link => {\r\n                    link.classList.remove('active');\r\n                    if(link.dataset.view === this.state.currentPortalView) {\r\n                        link.classList.add('active');\r\n                    }\r\n                });\r\n                \r\n                contentContainer.innerHTML = '';\r\n                switch(this.state.currentPortalView) {\r\n                    case 'dashboard':\r\n                        header.textContent = 'Applications Overview';\r\n                        contentContainer.innerHTML = this.getDashboardViewHTML();\r\n                        this.renderDashboardTable();\r\n                        break;\r\n                    case 'detail':\r\n                         header.textContent = `Application Details: ${this.state.selectedApplicationId}`;\r\n                         contentContainer.innerHTML = this.getDetailViewHTML(this.state.selectedApplicationId);\r\n                         this.attachDetailViewListeners(this.state.selectedApplicationId);\r\n                        break;\r\n                    case 'compliance':\r\n                        header.textContent = 'National Compliance Dashboard';\r\n                        contentContainer.innerHTML = this.getComplianceDashboardHTML();\r\n                        this.renderComplianceCharts();\r\n                        break;\r\n                    case 'new_application':\r\n                        header.textContent = 'New Indigent Application';\r\n                        contentContainer.innerHTML = this.getNewApplicationFormHTML();\r\n                        this.attachNewApplicationFormListeners();\r\n                        break;\r\n                    case 'user_management':\r\n                        header.textContent = 'User Management';\r\n                        contentContainer.innerHTML = this.getUserManagementViewHTML();\r\n                        this.renderUserManagementTable();\r\n                        break;\r\n                }\r\n            },\r\n            \r\n            showLoginModal() {\r\n                const modal = document.getElementById('login-modal');\r\n                modal.classList.remove('hidden');\r\n                modal.classList.add('flex');\r\n                setTimeout(() => {\r\n                    modal.querySelector('.modal-content').classList.remove('scale-95', 'opacity-0');\r\n                    modal.querySelector('.modal-content').classList.add('scale-100', 'opacity-100');\r\n                }, 10);\r\n            },\r\n            \r\n            hideLoginModal() {\r\n                const modal = document.getElementById('login-modal');\r\n                modal.querySelector('.modal-content').classList.add('scale-95', 'opacity-0');\r\n                setTimeout(() => modal.classList.add('hidden'), 300);\r\n            },\r\n            \r\n            handleLogin() {\r\n                this.state.isLoggedIn = true;\r\n                const role = document.getElementById('login-role-switcher').value;\r\n                this.state.currentUser = this.state.users.find(u => u.role === role);\r\n                this.state.currentPortalView = this.state.currentUser.role === 'admin' ? 'compliance' : 'dashboard';\r\n                this.hideLoginModal();\r\n                this.render();\r\n            },\r\n            \r\n            handleLogout() {\r\n                this.state.isLoggedIn = false;\r\n                this.state.currentUser = null;\r\n                this.render();\r\n            },\r\n            \r\n            getDashboardViewHTML() {\r\n                const user = this.state.currentUser;\r\n                const canCreate = user.role === 'clerk' || user.role === 'agent';\r\n                let summaryHTML = '';\r\n                if (user.role !== 'admin') {\r\n                    let counts = {\r\n                        'Pending Verification': 0,\r\n                        'Pending Assessment': 0,\r\n                        'Pending Approval': 0,\r\n                    };\r\n                    this.state.applications.forEach(app => {\r\n                        if (counts.hasOwnProperty(app.status)) {\r\n                            counts[app.status]++;\r\n                        }\r\n                    });\r\n\r\n                    let relevantCount = 0;\r\n                    let relevantStatus = '';\r\n                    if (user.role === 'clerk' || user.role === 'agent') {\r\n                        relevantCount = counts['Pending Verification'];\r\n                        relevantStatus = 'Pending Verification';\r\n                    } else if (user.role === 'assessor') {\r\n                        relevantCount = counts['Pending Assessment'];\r\n                        relevantStatus = 'Pending Assessment';\r\n                    } else if (user.role === 'manager') {\r\n                        relevantCount = counts['Pending Approval'];\r\n                        relevantStatus = 'Pending Approval';\r\n                    }\r\n                    \r\n                    summaryHTML = `\r\n                        <div class=\"mb-8 grid grid-cols-1 md:grid-cols-3 gap-6\">\r\n                            <div class=\"bg-white p-6 rounded-lg shadow\">\r\n                                <h3 class=\"text-gray-500 text-sm font-medium\">Your Action Items<\/h3>\r\n                                <p class=\"text-3xl font-bold text-blue-600\">${relevantCount}<\/p>\r\n                                <p class=\"text-gray-600\">${relevantStatus}<\/p>\r\n                            <\/div>\r\n                        <\/div>\r\n                    `;\r\n                }\r\n\r\n                const html = `\r\n                    ${summaryHTML}\r\n                    <div class=\"bg-white shadow-md rounded-lg overflow-hidden\">\r\n                        <div class=\"p-6 flex justify-between items-center\">\r\n                            <h2 class=\"text-xl font-semibold\">All Applications<\/h2>\r\n                            ${canCreate ? `<button id=\"new-application-btn\" class=\"bg-blue-600 text-white font-bold py-2 px-4 rounded-md hover:bg-blue-700 transition-colors\">&#43; New Application<\/button>` : ''}\r\n                        <\/div>\r\n                        <div class=\"overflow-x-auto\">\r\n                            <table class=\"min-w-full divide-y divide-gray-200\">\r\n                                <thead class=\"bg-gray-50\"><tr>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Application ID<\/th>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Applicant Name<\/th>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Date Submitted<\/th>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Status<\/th>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Assigned To<\/th>\r\n                                    <th scope=\"col\" class=\"relative px-6 py-3\"><span class=\"sr-only\">View<\/span><\/th>\r\n                                <\/tr><\/thead>\r\n                                <tbody id=\"applications-table-body\" class=\"bg-white divide-y divide-gray-200\"><\/tbody>\r\n                            <\/table>\r\n                        <\/div>\r\n                    <\/div>\r\n                `;\r\n                setTimeout(() => {\r\n                    const newAppBtn = document.getElementById('new-application-btn');\r\n                    if (newAppBtn) {\r\n                        newAppBtn.addEventListener('click', () => {\r\n                            this.state.currentPortalView = 'new_application';\r\n                            this.state.newApplicationCurrentStep = 1;\r\n                            this.state.newApplicationData = {};\r\n                            this.render();\r\n                        });\r\n                    }\r\n                    const tableBody = document.getElementById('applications-table-body');\r\n                    if (tableBody) {\r\n                        tableBody.addEventListener('click', (e) => {\r\n                            const row = e.target.closest('tr');\r\n                            if (row && row.dataset.id) {\r\n                                this.state.selectedApplicationId = row.dataset.id;\r\n                                this.state.currentPortalView = 'detail';\r\n                                this.render();\r\n                            }\r\n                        });\r\n                    }\r\n                }, 0);\r\n                return html;\r\n            },\r\n            \r\n            \/\/ --- NEW APPLICATION FORM LOGIC ---\r\n            getNewApplicationFormHTML() {\r\n                const step = this.state.newApplicationCurrentStep;\r\n                const steps = [\r\n                    { id: 1, name: 'Personal Details' },\r\n                    { id: 2, name: 'Household Info' },\r\n                    { id: 3, name: 'Financial Status' },\r\n                    { id: 4, name: 'Address' },\r\n                    { id: 5, name: 'Documents' },\r\n                    { id: 6, name: 'Consent' },\r\n                    { id: 7, name: 'Review' },\r\n                ];\r\n                let indicatorHTML = '';\r\n                steps.forEach(s => {\r\n                    let statusClass = 'border-gray-300 bg-white';\r\n                    if (s.id < step) statusClass = 'completed';\r\n                    if (s.id === step) statusClass = 'active';\r\n                    indicatorHTML += `<div class=\"step-indicator w-10 h-10 flex items-center justify-center font-bold border-2 rounded-full ${statusClass}\">${s.id}<\/div>`;\r\n                    if (s.id < steps.length) indicatorHTML += `<div class=\"flex-1 h-1 ${s.id < step ? 'bg-green-500' : 'bg-gray-300'}\"><\/div>`;\r\n                });\r\n\r\n                return `\r\n                    <div class=\"bg-white p-8 rounded-lg shadow-lg max-w-4xl mx-auto\">\r\n                        <div class=\"flex items-center mb-8\">${indicatorHTML}<\/div>\r\n                        <div id=\"form-step-content\"><\/div>\r\n                        <div id=\"form-navigation\" class=\"mt-8 pt-6 border-t flex justify-between items-center\"><\/div>\r\n                    <\/div>\r\n                `;\r\n            },\r\n\r\n            renderFormStep() {\r\n                const step = this.state.newApplicationCurrentStep;\r\n                const contentEl = document.getElementById('form-step-content');\r\n                const navEl = document.getElementById('form-navigation');\r\n                let content = '';\r\n                let nav = '';\r\n\r\n                const data = this.state.newApplicationData;\r\n\r\n                switch(step) {\r\n                    case 1:\r\n                        content = `\r\n                            <h3 class=\"text-xl font-semibold mb-4\">Step 1: Personal Details<\/h3>\r\n                            <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\r\n                                <div><label class=\"block text-sm font-medium\">First Name<\/label><input type=\"text\" id=\"firstName\" class=\"mt-1 block w-full p-2 border rounded-md\" value=\"${data.firstName || ''}\"><\/div>\r\n                                <div><label class=\"block text-sm font-medium\">Last Name<\/label><input type=\"text\" id=\"lastName\" class=\"mt-1 block w-full p-2 border rounded-md\" value=\"${data.lastName || ''}\"><\/div>\r\n                                <div><label class=\"block text-sm font-medium\">SA ID Number<\/label><input type=\"text\" id=\"idNumber\" class=\"mt-1 block w-full p-2 border rounded-md\" value=\"${data.idNumber || ''}\"><\/div>\r\n                                <div><label class=\"block text-sm font-medium\">Mobile Number<\/label><input type=\"text\" id=\"mobileNumber\" class=\"mt-1 block w-full p-2 border rounded-md\" value=\"${data.mobileNumber || ''}\"><\/div>\r\n                            <\/div>`;\r\n                        nav = `<button class=\"form-nav-btn\" data-action=\"exit\">Save & Continue<\/button><button class=\"form-nav-btn bg-blue-600 text-white font-bold py-2 px-4 rounded-md\" data-action=\"next\">Next &rarr;<\/button>`;\r\n                        break;\r\n                    case 2:\r\n                        content = `<h3 class=\"text-xl font-semibold mb-4\">Step 2: Household Information<\/h3>\r\n                                   <div><label class=\"block text-sm font-medium\">Number of Dependents<\/label><input type=\"number\" id=\"dependents\" class=\"mt-1 block w-full p-2 border rounded-md\" value=\"${data.dependents || ''}\"><\/div>`;\r\n                        nav = `<button class=\"form-nav-btn\" data-action=\"prev\">&larr; Previous<\/button><button class=\"form-nav-btn bg-blue-600 text-white font-bold py-2 px-4 rounded-md\" data-action=\"next\">Next &rarr;<\/button>`;\r\n                        break;\r\n                    case 3:\r\n                        content = `<h3 class=\"text-xl font-semibold mb-4\">Step 3: Financial Status<\/h3>\r\n                                   <div><label class=\"block text-sm font-medium\">Total Monthly Household Income (ZAR)<\/label><input type=\"number\" id=\"income\" class=\"mt-1 block w-full p-2 border rounded-md\" value=\"${data.income || ''}\"><\/div>`;\r\n                        nav = `<button class=\"form-nav-btn\" data-action=\"prev\">&larr; Previous<\/button><button class=\"form-nav-btn bg-blue-600 text-white font-bold py-2 px-4 rounded-md\" data-action=\"next\">Next &rarr;<\/button>`;\r\n                        break;\r\n                    case 4:\r\n                        content = `<h3 class=\"text-xl font-semibold mb-4\">Step 4: Physical Address<\/h3>\r\n                                   <div class=\"space-y-4\">\r\n                                       <div><label class=\"block text-sm font-medium\">Street Address<\/label><input type=\"text\" id=\"streetAddress\" class=\"mt-1 block w-full p-2 border rounded-md\" value=\"${data.streetAddress || ''}\"><\/div>\r\n                                       <button id=\"capture-gps\" class=\"bg-gray-200 p-2 rounded-md\">Capture GPS Location<\/button>\r\n                                       <div id=\"gps-coords\" class=\"text-sm text-gray-500\">${data.gps || ''}<\/div>\r\n                                   <\/div>`;\r\n                        nav = `<button class=\"form-nav-btn\" data-action=\"prev\">&larr; Previous<\/button><button class=\"form-nav-btn bg-blue-600 text-white font-bold py-2 px-4 rounded-md\" data-action=\"next\">Next &rarr;<\/button>`;\r\n                        break;\r\n                    case 5:\r\n                        content = `<h3 class=\"text-xl font-semibold mb-4\">Step 5: Supporting Documents<\/h3>\r\n                                   <div class=\"space-y-4\">\r\n                                       <div><label class=\"block text-sm font-medium\">Applicant Photo<\/label><button id=\"capture-photo\" class=\"bg-gray-200 p-2 rounded-md\">Take Photo<\/button><span id=\"photo-status\" class=\"ml-2 text-sm text-green-600\">${data.photo ? 'Captured' : ''}<\/span><\/div>\r\n                                       <div><label class=\"block text-sm font-medium\">ID Document<\/label><input type=\"file\" id=\"id-doc\"><\/div>\r\n                                       <div><label class=\"block text-sm font-medium\">Proof of Residence<\/label><input type=\"file\" id=\"residence-doc\"><\/div>\r\n                                   <\/div>`;\r\n                        nav = `<button class=\"form-nav-btn\" data-action=\"prev\">&larr; Previous<\/button><button class=\"form-nav-btn bg-blue-600 text-white font-bold py-2 px-4 rounded-md\" data-action=\"next\">Next &rarr;<\/button>`;\r\n                        break;\r\n                    case 6:\r\n                        content = `<h3 class=\"text-xl font-semibold mb-4\">Step 6: Consent & Declaration (POPIA)<\/h3>\r\n                                   <div class=\"space-y-6\">\r\n                                       <p class=\"text-sm text-gray-600\">I hereby declare that the information provided is true and correct. I consent to the municipality verifying this information with third parties as required by the Indigent Policy.<\/p>\r\n                                       <label class=\"flex items-center\"><input type=\"checkbox\" id=\"declaration\" class=\"h-4 w-4\" ${data.declaration ? 'checked' : ''}><span class=\"ml-2\">I Agree<\/span><\/label>\r\n                                       <div class=\"flex space-x-4\">\r\n                                            <button id=\"capture-fingerprint\" class=\"p-2 rounded-md ${data.fingerprint ? 'bg-green-500 text-white' : 'bg-gray-200'}\">${data.fingerprint ? 'Fingerprint Captured &#10003;' : 'Capture Fingerprint'}<\/button>\r\n                                            <button id=\"capture-signature\" class=\"p-2 rounded-md ${data.signatureDataURL ? 'bg-green-500 text-white' : 'bg-gray-200'}\">${data.signatureDataURL ? 'Signature Captured &#10003;' : 'Capture Signature'}<\/button>\r\n                                       <\/div>\r\n                                   <\/div>`;\r\n                        nav = `<button class=\"form-nav-btn\" data-action=\"prev\">&larr; Previous<\/button><button class=\"form-nav-btn bg-blue-600 text-white font-bold py-2 px-4 rounded-md\" data-action=\"next\">Review Application<\/button>`;\r\n                        break;\r\n                    case 7:\r\n                        content = `<h3 class=\"text-xl font-semibold mb-4\">Step 7: Review & Submit<\/h3>\r\n                                   <div class=\"space-y-2 text-sm border-b pb-4 mb-4\">\r\n                                       <p><strong>Name:<\/strong> ${data.firstName || ''} ${data.lastName || ''}<\/p>\r\n                                       <p><strong>ID:<\/strong> ${data.idNumber || ''}<\/p>\r\n                                       <p><strong>Income:<\/strong> R ${data.income || 0}<\/p>\r\n                                       <p><strong>Address:<\/strong> ${data.streetAddress || ''}<\/p>\r\n                                       <p><strong>Consent:<\/strong> ${data.declaration ? 'Given' : 'Not Given'}<\/p>\r\n                                   <\/div>\r\n                                   <div class=\"bg-yellow-50 p-4 rounded-lg\">\r\n                                        <h4 class=\"font-bold text-gray-800\">Agent\/Clerk Declaration<\/h4>\r\n                                        <label class=\"flex items-center mt-2\"><input type=\"checkbox\" id=\"agent-declaration\" class=\"h-4 w-4\"><span class=\"ml-2 text-sm\">I, ${this.state.currentUser.name}, declare that the information captured is true and correct to the best of my knowledge.<\/span><\/label>\r\n                                   <\/div>`;\r\n                        nav = `<button class=\"form-nav-btn\" data-action=\"prev\">&larr; Previous<\/button><button id=\"submit-app-btn\" class=\"form-nav-btn bg-green-600 text-white font-bold py-2 px-4 rounded-md\" data-action=\"submit\">Submit Application<\/button>`;\r\n                        break;\r\n                }\r\n                contentEl.innerHTML = content;\r\n                navEl.innerHTML = nav;\r\n            },\r\n\r\n            attachNewApplicationFormListeners() {\r\n                this.renderFormStep();\r\n                \r\n                const contentEl = document.getElementById('form-step-content');\r\n                if (this.state.newApplicationCurrentStep === 4) {\r\n                    contentEl.querySelector('#capture-gps').addEventListener('click', () => {\r\n                        contentEl.querySelector('#gps-coords').textContent = 'GPS Captured: -25.7479\u00b0 S, 28.2293\u00b0 E';\r\n                    });\r\n                }\r\n                 if (this.state.newApplicationCurrentStep === 5) {\r\n                    contentEl.querySelector('#capture-photo').addEventListener('click', () => {\r\n                         contentEl.querySelector('#photo-status').textContent = 'Captured';\r\n                    });\r\n                }\r\n                if (this.state.newApplicationCurrentStep === 6) {\r\n                    contentEl.querySelector('#capture-fingerprint').addEventListener('click', (e) => {\r\n                        e.target.textContent = 'Fingerprint Captured \u2713';\r\n                        e.target.classList.add('bg-green-500', 'text-white');\r\n                    });\r\n                     contentEl.querySelector('#capture-signature').addEventListener('click', (e) => {\r\n                        this.showSignatureModal(() => {\r\n                            const sigButton = document.getElementById('capture-signature');\r\n                            if (sigButton) {\r\n                                sigButton.textContent = 'Signature Captured \u2713';\r\n                                sigButton.classList.add('bg-green-500', 'text-white');\r\n                            }\r\n                        });\r\n                    });\r\n                }\r\n\r\n                document.getElementById('form-navigation').addEventListener('click', (e) => {\r\n                    const action = e.target.dataset.action;\r\n                    this.saveCurrentStepData();\r\n                    if (action === 'next') {\r\n                        this.state.newApplicationCurrentStep++;\r\n                        this.render();\r\n                    }\r\n                    if (action === 'prev') {\r\n                        this.state.newApplicationCurrentStep--;\r\n                        this.render();\r\n                    }\r\n                    if (action === 'exit') {\r\n                        this.state.currentPortalView = 'dashboard';\r\n                        this.render();\r\n                    }\r\n                    if (action === 'submit') {\r\n                        const declaration = document.getElementById('agent-declaration');\r\n                        if (!declaration || !declaration.checked) {\r\n                            alert('You must declare that the information is correct before submitting.');\r\n                            return;\r\n                        }\r\n                        this.showOtpModal(() => {\r\n                            this.submitNewApplication();\r\n                        });\r\n                    }\r\n                });\r\n            },\r\n            \r\n            saveCurrentStepData() {\r\n                const step = this.state.newApplicationCurrentStep;\r\n                switch(step) {\r\n                    case 1:\r\n                        this.state.newApplicationData.firstName = document.getElementById('firstName').value;\r\n                        this.state.newApplicationData.lastName = document.getElementById('lastName').value;\r\n                        this.state.newApplicationData.idNumber = document.getElementById('idNumber').value;\r\n                        this.state.newApplicationData.mobileNumber = document.getElementById('mobileNumber').value;\r\n                        break;\r\n                    case 2:\r\n                        this.state.newApplicationData.dependents = document.getElementById('dependents').value;\r\n                        break;\r\n                    case 3:\r\n                        this.state.newApplicationData.income = document.getElementById('income').value;\r\n                        break;\r\n                    case 4:\r\n                        this.state.newApplicationData.streetAddress = document.getElementById('streetAddress').value;\r\n                        if(document.getElementById('gps-coords').textContent.includes('Captured')) {\r\n                             this.state.newApplicationData.gps = document.getElementById('gps-coords').textContent;\r\n                        }\r\n                        break;\r\n                    case 5:\r\n                        if(document.getElementById('photo-status').textContent === 'Captured') {\r\n                            this.state.newApplicationData.photo = true;\r\n                        }\r\n                        break;\r\n                    case 6:\r\n                        this.state.newApplicationData.declaration = document.getElementById('declaration').checked;\r\n                        if (document.getElementById('capture-fingerprint').textContent.includes('Captured')) this.state.newApplicationData.fingerprint = true;\r\n                        \/\/ Signature data is saved from the modal\r\n                        break;\r\n                }\r\n            },\r\n\r\n            submitNewApplication() {\r\n                const data = this.state.newApplicationData;\r\n                const newId = 'IVMS-2025-' + String(this.state.applications.length + 1).padStart(3, '0');\r\n                const newApp = {\r\n                    id: newId,\r\n                    applicantName: `${data.firstName} ${data.lastName}`,\r\n                    dateSubmitted: new Date().toISOString().slice(0, 10),\r\n                    status: 'Pending Verification',\r\n                    assignedTo: 'Clerk',\r\n                    history: [{\r\n                        timestamp: new Date().toISOString().replace('T', ' ').substring(0, 16),\r\n                        user: 'IVMS Agent\/Clerk',\r\n                        action: 'Application Captured',\r\n                        notes: 'New application submitted via portal.'\r\n                    }]\r\n                };\r\n                this.state.applications.unshift(newApp);\r\n                this.state.currentPortalView = 'dashboard';\r\n                this.render();\r\n            },\r\n\r\n            \/\/ --- USER MANAGEMENT LOGIC ---\r\n            getUserManagementViewHTML() {\r\n                return `\r\n                    <div class=\"bg-white shadow-md rounded-lg overflow-hidden\">\r\n                        <div class=\"p-6 flex justify-between items-center\">\r\n                            <h2 class=\"text-xl font-semibold\">System Users<\/h2>\r\n                            <button id=\"new-user-btn\" class=\"bg-blue-600 text-white font-bold py-2 px-4 rounded-md hover:bg-blue-700 transition-colors\">&#43; Create User<\/button>\r\n                        <\/div>\r\n                        <div class=\"overflow-x-auto\">\r\n                            <table class=\"min-w-full divide-y divide-gray-200\">\r\n                                <thead class=\"bg-gray-50\"><tr>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Name<\/th>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Role<\/th>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Municipality<\/th>\r\n                                    <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">Province<\/th>\r\n                                <\/tr><\/thead>\r\n                                <tbody id=\"users-table-body\" class=\"bg-white divide-y divide-gray-200\"><\/tbody>\r\n                            <\/table>\r\n                        <\/div>\r\n                    <\/div>\r\n                `;\r\n            },\r\n\r\n            renderUserManagementTable() {\r\n                const tableBody = document.getElementById('users-table-body');\r\n                if (!tableBody) return;\r\n                tableBody.innerHTML = '';\r\n                this.state.users.forEach(user => {\r\n                    tableBody.innerHTML += `\r\n                        <tr>\r\n                            <td class=\"px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900\">${user.name}<\/td>\r\n                            <td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">${user.role.charAt(0).toUpperCase() + user.role.slice(1)}<\/td>\r\n                            <td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">${user.municipality || 'N\/A'}<\/td>\r\n                            <td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">${user.province || 'N\/A'}<\/td>\r\n                        <\/tr>\r\n                    `;\r\n                });\r\n                document.getElementById('new-user-btn').addEventListener('click', () => this.showNewUserModal());\r\n            },\r\n\r\n            showNewUserModal() {\r\n                let provinceOptions = Object.keys(this.state.municipalities).map(p => `<option value=\"${p}\">${p}<\/option>`).join('');\r\n\r\n                this.showActionModal({\r\n                    title: 'Create New User',\r\n                    body: `\r\n                        <div class=\"space-y-4\">\r\n                            <div><label class=\"block text-sm font-medium\">Full Name<\/label><input type=\"text\" id=\"new-user-name\" class=\"mt-1 block w-full p-2 border rounded-md\"><\/div>\r\n                            <div><label class=\"block text-sm font-medium\">Role<\/label><select id=\"new-user-role\" class=\"mt-1 block w-full p-2 border rounded-md\"><option value=\"agent\">IVMS Agent<\/option><option value=\"clerk\">Clerk<\/option><option value=\"assessor\">Assessor<\/option><option value=\"manager\">Manager<\/option><\/select><\/div>\r\n                            <div><label class=\"block text-sm font-medium\">Province<\/label><select id=\"new-user-province\" class=\"mt-1 block w-full p-2 border rounded-md\"><option value=\"\">Select Province<\/option>${provinceOptions}<\/select><\/div>\r\n                            <div><label class=\"block text-sm font-medium\">Municipality<\/label><select id=\"new-user-municipality\" class=\"mt-1 block w-full p-2 border rounded-md\" disabled><option>Select Province First<\/option><\/select><\/div>\r\n                        <\/div>\r\n                    `,\r\n                    onConfirm: () => this.handleCreateUser()\r\n                });\r\n\r\n                const provinceSelect = document.getElementById('new-user-province');\r\n                const muniSelect = document.getElementById('new-user-municipality');\r\n                provinceSelect.addEventListener('change', () => {\r\n                    const selectedProvince = provinceSelect.value;\r\n                    muniSelect.innerHTML = '<option value=\"\">Select Municipality<\/option>';\r\n                    if (selectedProvince && this.state.municipalities[selectedProvince]) {\r\n                        muniSelect.disabled = false;\r\n                        const munis = this.state.municipalities[selectedProvince];\r\n                        muniSelect.innerHTML += Object.keys(munis).map(m => `<option value=\"${m}\">${m}<\/option>`).join('');\r\n                    } else {\r\n                        muniSelect.disabled = true;\r\n                    }\r\n                });\r\n            },\r\n\r\n            handleCreateUser() {\r\n                const name = document.getElementById('new-user-name').value;\r\n                const role = document.getElementById('new-user-role').value;\r\n                const province = document.getElementById('new-user-province').value;\r\n                const municipality = document.getElementById('new-user-municipality').value;\r\n                \r\n                if (!name || !role || !province || !municipality) {\r\n                    alert('All fields are required.');\r\n                    return;\r\n                }\r\n\r\n                this.state.users.push({\r\n                    name: name,\r\n                    role: role,\r\n                    province: province,\r\n                    municipality: municipality,\r\n                    website: this.state.municipalities[province][municipality].website\r\n                });\r\n                \r\n                this.hideActionModal();\r\n                this.renderUserManagementTable();\r\n            },\r\n\r\n            handleSync(linkElement) {\r\n                if (this.state.unsyncedApplications === 0) {\r\n                    const originalText = linkElement.innerHTML;\r\n                    linkElement.innerHTML = 'Already Synced';\r\n                    setTimeout(() => { linkElement.innerHTML = originalText; }, 2000);\r\n                    return;\r\n                }\r\n                \r\n                const badge = linkElement.querySelector('span');\r\n                if(badge) badge.style.display = 'none';\r\n                linkElement.innerHTML = 'Syncing...';\r\n                \r\n                setTimeout(() => {\r\n                    this.state.unsyncedApplications = 0;\r\n                    linkElement.innerHTML = 'Sync Complete \u2713';\r\n                    setTimeout(() => {\r\n                        this.render();\r\n                    }, 1500);\r\n                }, 2000);\r\n            },\r\n\r\n\r\n            renderDashboardTable: function() { \/* ... same as before ... *\/ },\r\n            getDetailViewHTML: function(appId) { \/* ... same as before ... *\/ },\r\n            attachDetailViewListeners: function(appId) { \/* ... same as before ... *\/ },\r\n            getComplianceDashboardHTML: function() { \/* ... same as before ... *\/ },\r\n            renderComplianceCharts: function() { \/* ... same as before ... *\/ },\r\n            generateMockData: function() { \/* ... same as before ... *\/ },\r\n            getActionButtons: function(app) { \/* ... same as before ... *\/ },\r\n            getComplianceChecklist: function(status) { \/* ... same as before ... *\/ },\r\n            showVerifyModal: function(app) { \/* ... same as before ... *\/ },\r\n            showAssessModal: function(app) { \/* ... same as before ... *\/ },\r\n            showApproveModal: function(app) { \/* ... same as before ... *\/ },\r\n            showRejectModal: function(app) { \/* ... same as before ... *\/ },\r\n            updateApplicationStatus: function(appId, newStatus, newAssignedTo, historyAction, historyNotes, userRolePrefix) { \/* ... same as before ... *\/ },\r\n            showActionModal: function({ title, body, onConfirm }) { \/* ... same as before, renamed from showModal ... *\/ },\r\n            hideActionModal: function() { \/* ... same as before, renamed from hideModal ... *\/ },\r\n            showSignatureModal: function(callback) { \/* ... new ... *\/ },\r\n            hideSignatureModal: function() { \/* ... new ... *\/ },\r\n            showOtpModal: function(callback) { \/* ... new ... *\/ },\r\n            hideOtpModal: function() { \/* ... new ... *\/ },\r\n        };\r\n\r\n        \/\/ --- Start Re-integrated IVMS Logic ---\r\n        App.renderDashboardTable = function() {\r\n            const tableBody = document.getElementById('applications-table-body');\r\n            if (!tableBody) return;\r\n            tableBody.innerHTML = '';\r\n            this.state.applications.forEach(app => {\r\n                const statusClass = `status-${app.status.toLowerCase().replace(\/ \/g, '-')}`;\r\n                tableBody.innerHTML += `\r\n                    <tr data-id=\"${app.id}\" class=\"hover:bg-gray-50 cursor-pointer\">\r\n                        <td class=\"px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900\">${app.id}<\/td>\r\n                        <td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">${app.applicantName}<\/td>\r\n                        <td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">${app.dateSubmitted}<\/td>\r\n                        <td class=\"px-6 py-4 whitespace-nowrap text-sm\"><span class=\"status-badge ${statusClass}\">${app.status}<\/span><\/td>\r\n                        <td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">${app.assignedTo}<\/td>\r\n                        <td class=\"px-6 py-4 whitespace-nowrap text-right text-sm font-medium\"><a href=\"#\" class=\"text-blue-600 hover:text-blue-900\">View<\/a><\/td>\r\n                    <\/tr>\r\n                `;\r\n            });\r\n        };\r\n        App.getDetailViewHTML = function(appId) {\r\n            const app = this.state.applications.find(a => a.id === appId);\r\n            if (!app) return `<p>Application not found.<\/p>`;\r\n            const statusClass = `status-${app.status.toLowerCase().replace(\/ \/g, '-')}`;\r\n            let timelineHTML = '';\r\n            app.history.forEach(item => {\r\n                timelineHTML += `\r\n                    <div class=\"relative timeline-item pb-8 pl-12\">\r\n                        <div class=\"absolute left-0 top-0 flex items-center justify-center w-10 h-10 bg-gray-200 rounded-full\"><span class=\"text-xl\">&#128196;<\/span><\/div>\r\n                        <p class=\"font-semibold text-gray-800\">${item.action}<\/p>\r\n                        <p class=\"text-sm text-gray-500\">${item.user} on ${item.timestamp}<\/p>\r\n                        <p class=\"mt-2 text-sm text-gray-600 bg-gray-50 p-3 rounded-md\">${item.notes}<\/p>\r\n                    <\/div>\r\n                `;\r\n            });\r\n            return `<div class=\"bg-white p-8 rounded-lg shadow-lg\">\r\n                        <div class=\"flex flex-col md:flex-row justify-between md:items-start\">\r\n                            <div><h2 class=\"text-2xl font-bold\">${app.applicantName}<\/h2><p class=\"text-gray-500\">${app.id}<\/p><div class=\"mt-2\"><span class=\"status-badge ${statusClass}\">${app.status}<\/span><\/div><\/div>\r\n                            <div id=\"action-buttons-container\" class=\"mt-4 md:mt-0\">${this.getActionButtons(app)}<\/div>\r\n                        <\/div>\r\n                        <div class=\"border-t my-8\"><\/div>\r\n                        <div class=\"grid grid-cols-1 md:grid-cols-3 gap-8\">\r\n                            <div class=\"md:col-span-2\"><h3 class=\"text-xl font-semibold mb-4\">Application History<\/h3><div>${timelineHTML}<\/div><\/div>\r\n                            <div id=\"compliance-checklist-container\">${this.getComplianceChecklist(app.status)}<\/div>\r\n                        <\/div>\r\n                    <\/div>`;\r\n        };\r\n        App.attachDetailViewListeners = function(appId) {\r\n            const app = this.state.applications.find(a => a.id === appId);\r\n            const container = document.getElementById('action-buttons-container');\r\n            if (!container) return;\r\n            container.addEventListener('click', e => {\r\n                const button = e.target.closest('button');\r\n                if (!button) return;\r\n                const action = button.dataset.action;\r\n                if (action === 'verify') this.showVerifyModal(app);\r\n                if (action === 'assess') this.showAssessModal(app);\r\n                if (action === 'approve') this.showApproveModal(app);\r\n                if (action === 'reject') this.showRejectModal(app);\r\n            });\r\n        };\r\n        App.getComplianceDashboardHTML = function() {\r\n            return `\r\n                <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-8\">\r\n                    <div class=\"lg:col-span-2 bg-white p-6 rounded-lg shadow\"><h3 class=\"font-semibold\">Application Status by Municipality<\/h3><canvas id=\"status-by-municipality-chart\" class=\"mt-4\"><\/canvas><\/div>\r\n                    <div class=\"bg-white p-6 rounded-lg shadow\"><h3 class=\"font-semibold\">Overall Compliance<\/h3><canvas id=\"compliance-donut-chart\" class=\"mt-4\"><\/canvas><\/div>\r\n                <\/div>\r\n                <div class=\"mt-8 bg-white p-6 rounded-lg shadow\">\r\n                    <h3 class=\"font-semibold\">Pilot Municipality Metrics<\/h3>\r\n                    <table class=\"min-w-full divide-y divide-gray-200 mt-4\">\r\n                        <thead class=\"bg-gray-50\"><tr>\r\n                            <th class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">Municipality<\/th>\r\n                            <th class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">Avg. Turnaround (Days)<\/th>\r\n                            <th class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">Approval Rate<\/th>\r\n                            <th class=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase\">Active Applications<\/th>\r\n                        <\/tr><\/thead>\r\n                        <tbody class=\"bg-white divide-y divide-gray-200\">\r\n                            <tr><td class=\"px-6 py-4\">City of Tshwane<\/td><td class=\"px-6 py-4\">4.2<\/td><td class=\"px-6 py-4\">85%<\/td><td class=\"px-6 py-4\">1,204<\/td><\/tr>\r\n                            <tr><td class=\"px-6 py-4\">Ekurhuleni Metro<\/td><td class=\"px-6 py-4\">5.1<\/td><td class=\"px-6 py-4\">81%<\/td><td class=\"px-6 py-4\">2,530<\/td><\/tr>\r\n                            <tr><td class=\"px-6 py-4\">eThekwini Metro<\/td><td class=\"px-6 py-4\">3.9<\/td><td class=\"px-6 py-4\">91%<\/td><td class=\"px-6 py-4\">3,108<\/td><\/tr>\r\n                        <\/tbody>\r\n                    <\/table>\r\n                <\/div>`;\r\n        };\r\n        App.renderComplianceCharts = function() {\r\n            new Chart(document.getElementById('status-by-municipality-chart'), { type: 'bar', data: { labels: ['City of Tshwane', 'Ekurhuleni Metro', 'eThekwini Metro'], datasets: [{ label: 'Approved', data: [1023, 2049, 2828], backgroundColor: '#22c55e' }, { label: 'Pending', data: [181, 481, 280], backgroundColor: '#f97316' }, { label: 'Rejected', data: [153, 244, 253], backgroundColor: '#ef4444' }] }, options: { responsive: true, scales: { x: { stacked: true }, y: { stacked: true } } } });\r\n            new Chart(document.getElementById('compliance-donut-chart'), { type: 'doughnut', data: { labels: ['Fully Compliant', 'Minor Non-Compliance', 'At Risk'], datasets: [{ data: [88, 9, 3], backgroundColor: ['#22c55e', '#f97316', '#ef4444'] }] }, options: { responsive: true } });\r\n        };\r\n        App.generateMockData = function() {\r\n             this.state.applications = [\r\n                { id: 'IVMS-2025-001', applicantName: 'Naledi Khumalo', dateSubmitted: '2025-07-15', status: 'Pending Verification', assignedTo: 'Clerk', history: [{ timestamp: '2025-07-15 09:12', user: 'IVMS Agent', action: 'Application Captured', notes: 'Captured on-site via tablet. All initial documents provided.' }] },\r\n                { id: 'IVMS-2025-002', applicantName: 'Pieter van der Merwe', dateSubmitted: '2025-07-12', status: 'Pending Assessment', assignedTo: 'Assessor', history: [{ timestamp: '2025-07-12 11:30', user: 'Municipal Clerk', action: 'Application Captured', notes: 'Walk-in applicant at municipal office.' }, { timestamp: '2025-07-13 14:05', user: 'Clerk (T. Dube)', action: 'Application Verified', notes: 'Initial document check complete. ID and address verified against internal systems. No immediate flags.' }] },\r\n                { id: 'IVMS-2025-003', applicantName: 'Fatima Ismail', dateSubmitted: '2025-07-10', status: 'Pending Approval', assignedTo: 'Manager', history: [{ timestamp: '2025-07-10 10:00', user: 'IVMS Agent', action: 'Application Captured', notes: 'On-site capture.' }, { timestamp: '2025-07-11 16:20', user: 'Clerk (T. Dube)', action: 'Application Verified', notes: 'Initial checks passed.' }, { timestamp: '2025-07-14 11:45', user: 'Assessor (S. Naidoo)', action: 'Assessment Complete', notes: 'Affordability and external checks confirm applicant meets indigent criteria. Recommending for approval.' }] },\r\n                { id: 'IVMS-2025-004', applicantName: 'John Smith', dateSubmitted: '2025-07-08', status: 'Approved', assignedTo: 'System', history: [{ timestamp: '2025-07-08 15:00', user: 'Municipal Clerk', action: 'Application Captured', notes: 'Walk-in.' }, { timestamp: '2025-07-09 10:10', user: 'Clerk (T. Dube)', action: 'Application Verified', notes: 'Verified.' }, { timestamp: '2025-07-11 09:30', user: 'Assessor (S. Naidoo)', action: 'Assessment Complete', notes: 'Recommended for approval.' }, { timestamp: '2025-07-14 16:00', user: 'Manager (J. Botha)', action: 'Application Approved', notes: 'All checks and balances are in order. Applicant approved for FBS.' }] }\r\n            ];\r\n            this.state.municipalities = {\r\n                \"Gauteng\": {\r\n                    \"City of Tshwane\": { website: \"https:\/\/www.tshwane.gov.za\" },\r\n                    \"City of Johannesburg\": { website: \"https:\/\/www.joburg.org.za\" }\r\n                },\r\n                \"Western Cape\": {\r\n                    \"City of Cape Town\": { website: \"https:\/\/www.capetown.gov.za\" },\r\n                    \"Stellenbosch Municipality\": { website: \"https:\/\/www.stellenbosch.gov.za\" }\r\n                }\r\n            };\r\n            this.state.users = [\r\n                { name: 'Admin User', role: 'admin' },\r\n                { name: 'Field Agent One', role: 'agent', province: 'Western Cape', municipality: 'Stellenbosch Municipality', website: 'https:\/\/www.stellenbosch.gov.za' },\r\n                { name: 'Thabo Dube', role: 'clerk', province: 'Gauteng', municipality: 'City of Tshwane', website: 'https:\/\/www.tshwane.gov.za' },\r\n                { name: 'Sipho Naidoo', role: 'assessor', province: 'Western Cape', municipality: 'City of Cape Town', website: 'https:\/\/www.capetown.gov.za' },\r\n                { name: 'Jane Botha', role: 'manager', province: 'Gauteng', municipality: 'City of Johannesburg', website: 'https:\/\/www.joburg.org.za' }\r\n            ];\r\n        };\r\n        App.getActionButtons = function(app) {\r\n            const { status } = app;\r\n            const { role } = this.state.currentUser;\r\n            let buttons = '';\r\n            if (status === 'Pending Verification' && role === 'clerk') { buttons = `<button data-action=\"verify\" class=\"bg-blue-600 text-white font-bold py-2 px-4 rounded-md hover:bg-blue-700\">Verify Application<\/button>`; }\r\n            if (status === 'Pending Assessment' && role === 'assessor') { buttons = `<button data-action=\"assess\" class=\"bg-purple-600 text-white font-bold py-2 px-4 rounded-md hover:bg-purple-700\">Assess Application<\/button>`; }\r\n            if (status === 'Pending Approval' && role === 'manager') { buttons = `<button data-action=\"approve\" class=\"bg-green-600 text-white font-bold py-2 px-4 rounded-md hover:bg-green-700\">Approve<\/button> <button data-action=\"reject\" class=\"bg-red-600 text-white font-bold py-2 px-4 rounded-md hover:bg-red-700 ml-2\">Reject<\/button>`; }\r\n            return buttons;\r\n        };\r\n        App.getComplianceChecklist = function(status) {\r\n            let checklist = '';\r\n            if (status === 'Pending Verification') { checklist = `<h3 class=\"text-xl font-semibold mb-4\">Verification Checklist<\/h3><ul class=\"space-y-3 text-sm\"><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-green-500\">&#9745;<\/span>POPIA Consent Obtained<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-gray-400\">&#9744;<\/span>ID Document Verified<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-gray-400\">&#9744;<\/span>Proof of Residence Checked<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-gray-400\">&#9744;<\/span>Internal System Cross-Reference<\/li><\/ul><p class=\"text-xs text-gray-500 mt-4\">Ref: Indigent Policy Sec. 4.1, MSA Ch. 5<\/p>`; }\r\n            if (status === 'Pending Assessment') { checklist = `<h3 class=\"text-xl font-semibold mb-4\">Assessment Checklist<\/h3><ul class=\"space-y-3 text-sm\"><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-green-500\">&#9745;<\/span>Initial Verification Complete<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-gray-400\">&#9744;<\/span>External Credit Bureau Check<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-gray-400\">&#9744;<\/span>Affordability Scoring<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-gray-400\">&#9744;<\/span>Household Income vs. Threshold<\/li><\/ul><p class=\"text-xs text-gray-500 mt-4\">Ref: National Treasury Guideline, MFMA Sec. 62<\/p>`; }\r\n            if (status === 'Pending Approval' || status === 'Approved' || status === 'Rejected') { checklist = `<h3 class=\"text-xl font-semibold mb-4\">Final Approval Checklist<\/h3><ul class=\"space-y-3 text-sm\"><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-green-500\">&#9745;<\/span>Clerk Verification Complete<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-green-500\">&#9745;<\/span>Assessor Recommendation Received<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-gray-400\">&#9744;<\/span>Manager Final Review<\/li><li class=\"flex items-center\"><span class=\"mr-2 text-lg text-gray-400\">&#9744;<\/span>Audit Trail Verified<\/li><\/ul><p class=\"text-xs text-gray-500 mt-4\">Ref: AGSA Best Practice, Municipal Delegation of Authority<\/p>`; }\r\n            return `<div class=\"bg-gray-50 p-6 rounded-lg border\">${checklist}<\/div>`;\r\n        };\r\n        App.showVerifyModal = function(app) { this.showActionModal({ title: 'Verify Application: ' + app.applicantName, body: `<p class=\"mb-4\">Confirm that all initial document checks are complete and the applicant's details have been verified against internal systems.<\/p><textarea id=\"action-notes\" class=\"w-full p-2 border rounded-md\" placeholder=\"Add verification notes...\"><\/textarea>`, onConfirm: () => { const notes = document.getElementById('action-notes').value || 'Initial document check complete. ID and address verified against internal systems. No immediate flags.'; this.updateApplicationStatus(app.id, 'Pending Assessment', 'Assessor', 'Application Verified', notes, 'Clerk'); } }); };\r\n        App.showAssessModal = function(app) {\r\n            this.showActionModal({\r\n                title: 'Assess Application: ' + app.applicantName,\r\n                body: `\r\n                    <div class=\"space-y-4\">\r\n                        <h4 class=\"font-semibold\">Simulated External Checks:<\/h4>\r\n                        <div class=\"bg-gray-100 p-3 rounded-md text-sm\">\r\n                            <p><strong>Credit Bureau Check:<\/strong> <span class=\"text-green-600 font-medium\">Clear<\/span><\/p>\r\n                            <p><strong>Affordability Score:<\/strong> <span class=\"text-green-600 font-medium\">78\/100 (Meets Indigent Criteria)<\/span><\/p>\r\n                        <\/div>\r\n                        <div>\r\n                            <label class=\"block text-sm font-medium\">Recommendation<\/label>\r\n                            <select id=\"assessment-recommendation\" class=\"mt-1 block w-full p-2 border rounded-md\">\r\n                                <option value=\"Recommend for Approval\">Recommend for Approval<\/option>\r\n                                <option value=\"Recommend for Rejection\">Recommend for Rejection<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n                        <div>\r\n                            <label class=\"block text-sm font-medium\">Assessment Notes<\/label>\r\n                            <textarea id=\"action-notes\" class=\"w-full p-2 border rounded-md\" placeholder=\"Justify your recommendation...\"><\/textarea>\r\n                        <\/div>\r\n                        <div>\r\n                            <label class=\"block text-sm font-medium\">Attach Assessment Report<\/label>\r\n                            <input type=\"file\" class=\"mt-1\">\r\n                        <\/div>\r\n                        <button id=\"assessment-signature-btn\" class=\"p-2 rounded-md bg-gray-200\">Sign Assessment<\/button>\r\n                    <\/div>\r\n                `,\r\n                onConfirm: () => {\r\n                    this.showOtpModal(() => {\r\n                        const recommendation = document.getElementById('assessment-recommendation').value;\r\n                        const notes = document.getElementById('action-notes').value || 'Affordability and external checks confirm applicant meets indigent criteria.';\r\n                        const fullNotes = `Recommendation: ${recommendation}. Notes: ${notes}`;\r\n                        this.updateApplicationStatus(app.id, 'Pending Approval', 'Manager', 'Assessment Complete', fullNotes, 'Assessor');\r\n                    });\r\n                }\r\n            });\r\n            document.getElementById('assessment-signature-btn').addEventListener('click', () => {\r\n                this.showSignatureModal(() => {\r\n                    document.getElementById('assessment-signature-btn').textContent = 'Signature Captured \u2713';\r\n                    document.getElementById('assessment-signature-btn').classList.add('bg-green-500', 'text-white');\r\n                }, 'Assessor Signature');\r\n            });\r\n        };\r\n        App.showApproveModal = function(app) { this.showActionModal({ title: 'Approve Application: ' + app.applicantName, body: `<div class=\"space-y-4\"><p class=\"mb-4\">You are about to approve this application for indigent benefits. This action is final and will be recorded in the audit trail.<\/p><textarea id=\"action-notes\" class=\"w-full p-2 border rounded-md\" placeholder=\"Add final approval notes...\"><\/textarea><div><label class=\"block text-sm font-medium\">Attach Decision Letter<\/label><input type=\"file\" class=\"mt-1\"><\/div><button id=\"manager-signature-btn\" class=\"p-2 rounded-md bg-gray-200\">Sign Decision<\/button><\/div>`, onConfirm: () => { this.showOtpModal(() => { const notes = document.getElementById('action-notes').value || 'All checks and balances are in order. Applicant approved for FBS.'; this.updateApplicationStatus(app.id, 'Approved', 'System', 'Application Approved', notes, 'Manager'); }); } }); document.getElementById('manager-signature-btn').addEventListener('click', () => { this.showSignatureModal(() => { document.getElementById('manager-signature-btn').textContent = 'Signature Captured \u2713'; document.getElementById('manager-signature-btn').classList.add('bg-green-500', 'text-white'); }, 'Manager Signature'); });};\r\n        App.showRejectModal = function(app) { this.showActionModal({ title: 'Reject Application: ' + app.applicantName, body: `<div class=\"space-y-4\"><p class=\"mb-4 text-red-700 font-semibold\">You are about to reject this application. Please provide a clear reason for rejection. This action is final.<\/p><textarea id=\"action-notes\" class=\"w-full p-2 border rounded-md\" placeholder=\"Reason for rejection...\"><\/textarea><div><label class=\"block text-sm font-medium\">Attach Decision Letter<\/label><input type=\"file\" class=\"mt-1\"><\/div><button id=\"manager-signature-btn\" class=\"p-2 rounded-md bg-gray-200\">Sign Decision<\/button><\/div>`, onConfirm: () => { let notes = document.getElementById('action-notes').value; if (!notes) { alert('A reason for rejection is required.'); return; } this.showOtpModal(() => { this.updateApplicationStatus(app.id, 'Rejected', 'System', 'Application Rejected', notes, 'Manager'); }); } }); document.getElementById('manager-signature-btn').addEventListener('click', () => { this.showSignatureModal(() => { document.getElementById('manager-signature-btn').textContent = 'Signature Captured \u2713'; document.getElementById('manager-signature-btn').classList.add('bg-green-500', 'text-white'); }, 'Manager Signature'); });};\r\n        App.updateApplicationStatus = function(appId, newStatus, newAssignedTo, historyAction, historyNotes, userRolePrefix) {\r\n            const app = this.state.applications.find(a => a.id === appId); if (!app) return;\r\n            app.status = newStatus; app.assignedTo = newAssignedTo;\r\n            const userFullName = this.state.currentUser.name;\r\n            app.history.push({ timestamp: new Date().toISOString().replace('T', ' ').substring(0, 16), user: `${userRolePrefix} (${userFullName})`, action: historyAction, notes: historyNotes });\r\n            this.hideActionModal(); this.state.currentPortalView = 'detail'; this.render();\r\n        };\r\n        App.showActionModal = function({ title, body, onConfirm }) {\r\n            document.getElementById('modal-title').innerText = title; document.getElementById('modal-body').innerHTML = body;\r\n            const modal = document.getElementById('action-modal'); modal.classList.remove('hidden'); modal.classList.add('flex');\r\n            const confirmBtn = document.getElementById('modal-confirm-btn'); const newConfirmBtn = confirmBtn.cloneNode(true);\r\n            confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn); newConfirmBtn.addEventListener('click', onConfirm);\r\n            setTimeout(() => {\r\n                const content = modal.querySelector('.modal-content');\r\n                if (content) {\r\n                    modal.classList.remove('opacity-0');\r\n                    content.classList.remove('scale-95', 'opacity-0');\r\n                    content.classList.add('scale-100', 'opacity-100');\r\n                }\r\n            }, 10);\r\n        };\r\n        App.hideActionModal = function() {\r\n            const modal = document.getElementById('action-modal');\r\n            const content = modal.querySelector('.modal-content');\r\n            if (content) {\r\n                modal.classList.add('opacity-0');\r\n                content.classList.add('scale-95', 'opacity-0');\r\n            }\r\n            setTimeout(() => {\r\n                modal.classList.add('hidden');\r\n                modal.classList.remove('flex');\r\n            }, 300);\r\n        };\r\n        App.showSignatureModal = function(callback, title = 'Signature') {\r\n            this.state.signatureCallback = callback;\r\n            document.getElementById('signature-modal-title').textContent = title;\r\n            const modal = document.getElementById('signature-modal');\r\n            modal.classList.remove('hidden');\r\n            modal.classList.add('flex');\r\n            setTimeout(() => {\r\n                modal.querySelector('.modal-content').classList.remove('scale-95', 'opacity-0');\r\n                modal.querySelector('.modal-content').classList.add('scale-100', 'opacity-100');\r\n            }, 10);\r\n\r\n            const canvas = document.getElementById('signature-pad');\r\n            const ctx = canvas.getContext('2d');\r\n            ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n            let drawing = false;\r\n\r\n            function getMousePos(canvas, evt) {\r\n                const rect = canvas.getBoundingClientRect();\r\n                return { x: evt.clientX - rect.left, y: evt.clientY - rect.top };\r\n            }\r\n\r\n            const startDrawing = (e) => { drawing = true; const pos = getMousePos(canvas, e); ctx.beginPath(); ctx.moveTo(pos.x, pos.y); };\r\n            const stopDrawing = () => { drawing = false; };\r\n            const draw = (e) => { if (!drawing) return; e.preventDefault(); const pos = getMousePos(canvas, e.touches ? e.touches[0] : e); ctx.lineTo(pos.x, pos.y); ctx.stroke(); };\r\n\r\n            canvas.addEventListener('mousedown', startDrawing);\r\n            canvas.addEventListener('mouseup', stopDrawing);\r\n            canvas.addEventListener('mousemove', draw);\r\n            canvas.addEventListener('touchstart', startDrawing);\r\n            canvas.addEventListener('touchend', stopDrawing);\r\n            canvas.addEventListener('touchmove', draw);\r\n            \r\n            document.getElementById('sig-clear-btn').onclick = () => {\r\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n            };\r\n\r\n            document.getElementById('sig-save-btn').onclick = () => {\r\n                if (this.state.signatureCallback) {\r\n                    this.state.signatureCallback(canvas.toDataURL());\r\n                }\r\n                this.hideSignatureModal();\r\n            };\r\n        };\r\n        App.hideSignatureModal = function() {\r\n            const modal = document.getElementById('signature-modal');\r\n            modal.querySelector('.modal-content').classList.add('scale-95', 'opacity-0');\r\n            setTimeout(() => {\r\n                modal.classList.add('hidden');\r\n                modal.classList.remove('flex');\r\n            }, 300);\r\n        };\r\n        App.showOtpModal = function(callback) {\r\n            this.state.otpCallback = callback;\r\n            const modal = document.getElementById('otp-modal');\r\n            modal.classList.remove('hidden');\r\n            modal.classList.add('flex');\r\n            setTimeout(() => {\r\n                modal.querySelector('.modal-content').classList.remove('scale-95', 'opacity-0');\r\n                modal.querySelector('.modal-content').classList.add('scale-100', 'opacity-100');\r\n            }, 10);\r\n\r\n            document.getElementById('otp-cancel-btn').onclick = () => this.hideOtpModal();\r\n            document.getElementById('otp-verify-btn').onclick = () => {\r\n                const otpInput = document.getElementById('otp-input');\r\n                const otpError = document.getElementById('otp-error');\r\n                if (otpInput.value === '1234') {\r\n                    otpError.classList.add('hidden');\r\n                    this.hideOtpModal();\r\n                    if(this.state.otpCallback) this.state.otpCallback();\r\n                } else {\r\n                    otpError.classList.remove('hidden');\r\n                }\r\n            };\r\n        };\r\n        App.hideOtpModal = function() {\r\n            const modal = document.getElementById('otp-modal');\r\n            modal.querySelector('.modal-content').classList.add('scale-95', 'opacity-0');\r\n            setTimeout(() => {\r\n                modal.classList.add('hidden');\r\n                modal.classList.remove('flex');\r\n            }, 300);\r\n        };\r\n        \/\/ --- End Re-integrated IVMS Logic ---\r\n\r\n        App.init();\r\n    });\r\n    <\/script>\r\n<\/body>\r\n<\/html>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>IVMS IVMS Login to Portal The National Standard for Municipal Sustainability. Engineered for compliance and designed for impact, the Indigent Verification Management System (IVMS) is restoring data integrity and financial viability to local government across South Africa. Solving the Core of Systemic Failure The municipal crisis was never a lack of will, but a crisis of data integrity. IVMS was designed to solve the four key systemic disconnections that undermined past efforts. Fragmented Systems IVMS replaces siloed processes with a single, authoritative platform, creating one source of truth for indigent management. Compliance as an Afterthought Built on a foundation of MSA and MFMA policy, IVMS ensures every step of the workflow is inherently compliant. No Actionable Intelligence The system translates verified data into clear, prioritized actions that enable effective decision-making. Citizen Disengagement By ensuring a fair, transparent, and auditable process, IVMS rebuilds the trust that is essential for a healthy culture of payment. The IVMS: A Proven Foundational Solution IVMS creates a single, auditable source of truth for indigent management\u2014the cornerstone of fair service delivery and revenue protection for municipalities nationwide. &#10003; Engineered for Compliance Built from the ground up on MSA, MFMA, and POPIA principles, ensuring every workflow is inherently compliant. &#10003; Restores Data Integrity As a Registered Credit Bureau, we verify applicant data against trusted national sources, eliminating &#8220;garbage in, garbage out.&#8221; &#10003; Bridges the Digital Divide A hybrid model using field agents with tablets ensures all citizens can apply, regardless of digital access or literacy. &#10003; Creates an Auditable Trail Every action, from capture to final decision, is logged, creating a clean, defensible record for the Auditor-General. Powered by SALGA j-cred.co.za Login to IVMS Select Your Role IVMS AgentMunicipal ClerkAssessorManagerSALGA Admin Password Login IVMS Logout Dashboard Action Title Cancel Confirm Signature Please sign in the box above. Clear Save Signature Final Submission An OTP has been sent to your device. Please enter it below to submit the application. Enter 4-Digit OTP Invalid OTP. Please try again. Cancel Verify &#038; Submit<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_eb_attr":"","footnotes":""},"class_list":["post-3208","page","type-page","status-publish","hentry"],"blocksy_meta":{"page_structure_type":"default","disable_header":"yes","styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":6}},"_links":{"self":[{"href":"https:\/\/j-cred.co.za\/zh\/wp-json\/wp\/v2\/pages\/3208","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/j-cred.co.za\/zh\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/j-cred.co.za\/zh\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/j-cred.co.za\/zh\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/j-cred.co.za\/zh\/wp-json\/wp\/v2\/comments?post=3208"}],"version-history":[{"count":20,"href":"https:\/\/j-cred.co.za\/zh\/wp-json\/wp\/v2\/pages\/3208\/revisions"}],"predecessor-version":[{"id":3380,"href":"https:\/\/j-cred.co.za\/zh\/wp-json\/wp\/v2\/pages\/3208\/revisions\/3380"}],"wp:attachment":[{"href":"https:\/\/j-cred.co.za\/zh\/wp-json\/wp\/v2\/media?parent=3208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}