@@ -0,0 +1,617 @@
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1" / >
{% set __title_user = current_user if current_user is defined else None %}
{% set __firm_branding = get_current_firm_branding(request, __title_user) %}
< meta name = "theme-color" content = "{{ __firm_branding.primary_color or '#1e3a8a' }}" / >
{% if __firm_branding.favicon_url %}< link rel = "icon" href = "{{ __firm_branding.favicon_url }}" / > {% endif %}
{% set __title_auth = __title_user and request.session.get("otp_verified", False) %}
{% if __title_auth %}
{% set __title_firm = get_current_tenant_name(request, __title_user) %}
{% set __title_branch = get_current_branch_name(request, __title_user) %}
< title > {{ title or "Workspace" }} | {{ __title_firm }}{% if __title_branch and __title_branch != "-" %} - {{ __title_branch }}{% endif %}< / title >
{% else %}
< title > {{ title or "Welcome" }} | {{ __firm_branding.firm_name or "Audit Firm ERP" }}< / title >
{% endif %}
< script src = "https://cdn.tailwindcss.com" > < / script >
< script >
tailwind . config = {
theme : {
extend : {
colors : {
brand : {
50 : 'var(--af-color-brand-50)' ,
100 : 'var(--af-color-brand-100)' ,
200 : 'var(--af-color-brand-200)' ,
300 : 'var(--af-color-brand-300)' ,
400 : 'var(--af-color-brand-400)' ,
500 : 'var(--af-color-brand-500)' ,
600 : 'var(--af-color-brand-600)' ,
700 : 'var(--af-color-brand-700)' ,
800 : 'var(--af-color-brand-800)' ,
900 : 'var(--af-color-brand-900)'
} ,
afsuccess : {
50 : 'var(--af-color-success-50)' ,
600 : 'var(--af-color-success-600)' ,
700 : 'var(--af-color-success-700)'
} ,
afwarning : {
50 : 'var(--af-color-warning-50)' ,
600 : 'var(--af-color-warning-600)' ,
700 : 'var(--af-color-warning-700)'
} ,
afdanger : {
50 : 'var(--af-color-danger-50)' ,
600 : 'var(--af-color-danger-600)' ,
700 : 'var(--af-color-danger-700)'
} ,
afinfo : {
50 : 'var(--af-color-info-50)' ,
600 : 'var(--af-color-info-600)' ,
700 : 'var(--af-color-info-700)'
}
} ,
boxShadow : {
soft : 'var(--af-shadow-soft)' ,
card : 'var(--af-shadow-card)' ,
focus : '0 0 0 4px rgba(37, 99, 235, 0.12)'
} ,
borderRadius : {
card : 'var(--af-radius-card)' ,
control : 'var(--af-radius-control)'
}
}
}
}
< / script >
< link rel = "stylesheet" href = "/static/css/theme_tokens.css?v=7q-dashboard-align-strong" / >
< style >
: root {
--af-color-brand-500 : { { __firm_branding . primary_color or '#2563eb' } } ;
--af-color-brand-600 : { { _ _ f i r m _ b r a n d i n g . p r i m a r y _ c o l o r o r ' # 1 d 4 e d 8 ' } } ;
--af-color-brand-700 : { { _ _ f i r m _ b r a n d i n g . a c c e n t _ c o l o r o r ' # 1 e 4 0 a f ' } } ;
--af-color-brand-800 : { { _ _ f i r m _ b r a n d i n g . a c c e n t _ c o l o r o r ' # 1 e 3 a 8 a ' } } ;
--af-color-brand-900 : { { _ _ f i r m _ b r a n d i n g . a c c e n t _ c o l o r o r ' # 1 7 2 5 5 4 ' } } ;
}
< / style >
< / head >
< body class = "min-h-screen bg-slate-100 text-slate-900" >
{% set otp_ok = request.session.get("otp_verified", False) %}
{% set full_auth = current_user and otp_ok %}
{% set ui_perms = current_user_permissions if full_auth else [] %}
{% set ui_roles = current_user_roles if full_auth else [] %}
{% set active_tenant_id = get_active_tenant_id(request, current_user) if full_auth else None %}
{% set active_branch_id = get_active_branch_id(request, current_user) if full_auth else None %}
{% set active_financial_year = get_active_financial_year(request, current_user) if full_auth else None %}
{% set active_assessment_year = get_active_assessment_year(request, current_user) if full_auth else None %}
{% set unread_alert_count = get_unread_alert_count(request, current_user) if full_auth else 0 %}
{% set current_path = request.url.path %}
{% set document_menu_roles = ["System Admin", "Firm Admin", "Partner"] %}
{% set can_view_documents_menu = full_auth and can_view_documents(current_user, ui_perms, ui_roles) and (ui_roles|select("in", document_menu_roles)|list|length > 0) %}
{% set management_menu_roles = ["System Admin", "Firm Admin", "Partner", "Manager", "Branch Manager"] %}
{% set can_view_management_menus = full_auth and (ui_roles|select("in", management_menu_roles)|list|length > 0) %}
{% set firm_branding = get_current_firm_branding(request, current_user) if full_auth else __firm_branding %}
{% set current_user_photo_url = get_user_profile_photo_url(current_user) if full_auth else None %}
{% set current_user_initials = get_user_initials(current_user) if full_auth else "U" %}
{% set current_firm_name = firm_branding.firm_name if firm_branding else "Audit Firm ERP" %}
{% set current_branch_name = firm_branding.branch_name if firm_branding else "" %}
{% set domain_context = get_domain_context(request) %}
{% set is_system_admin_user = full_auth and ("System Admin" in ui_roles) %}
{% set can_manage_local_storage_agent = full_auth and can_view_documents(current_user, ui_perms, ui_roles) and can_upload_documents(current_user, ui_perms, ui_roles) and (ui_roles|select("in", ["Firm Admin", "Partner", "Branch Manager"])|list|length > 0) %}
< div class = "min-h-screen lg:grid lg:grid-cols-[260px_minmax(0,1fr)]" >
< aside class = "border-b border-slate-200 bg-slate-900 text-slate-100 lg:min-h-screen lg:border-b-0 lg:border-r lg:border-slate-800" >
< div class = "flex items-center gap-3 px-5 py-5" >
{% if firm_branding.logo_url %}< img src = "{{ firm_branding.logo_url }}" alt = "{{ current_firm_name }} logo" class = "h-11 w-11 rounded-2xl bg-white object-contain p-1 shadow-soft" / > {% else %}< div class = "flex h-11 w-11 items-center justify-center rounded-2xl bg-brand-500 font-bold text-white shadow-soft" > {{ (current_firm_name[:2] if current_firm_name else 'AF')|upper }}< / div > {% endif %}
< div class = "min-w-0" >
< div class = "truncate text-sm font-semibold tracking-wide" > {{ current_firm_name }}< / div >
< div class = "truncate text-xs text-slate-400" > {% if full_auth %}{{ current_branch_name }}{% if current_branch_name and current_branch_name != "-" %} Branch{% endif %} • Workspace{% elif domain_context.is_resolved %}{{ current_branch_name or 'Domain Workspace' }}{% else %}Secure Practice Workspace{% endif %}< / div >
< / div >
< / div >
{% if full_auth and ('Client' in ui_roles) %}
{% set sidebar_auditor = get_client_sidebar_auditor_card(request, current_user) %}
{% if sidebar_auditor %}
< div class = "mx-4 mb-4 rounded-2xl border border-slate-700 bg-slate-800/80 p-3 shadow-soft" >
< div class = "text-[10px] font-semibold uppercase tracking-[0.20em] text-brand-200" > Your Auditor< / div >
< div class = "mt-3 flex items-center gap-3" >
{% if sidebar_auditor.photo_url %}
< img src = "{{ sidebar_auditor.photo_url }}" alt = "{{ sidebar_auditor.name }}" class = "h-11 w-11 rounded-xl border border-white/10 object-cover" / >
{% else %}
< div class = "flex h-11 w-11 items-center justify-center rounded-xl border border-white/10 bg-brand-600 text-sm font-bold text-white" > {{ sidebar_auditor.initials or 'AU' }}< / div >
{% endif %}
< div class = "min-w-0" >
< div class = "truncate text-sm font-semibold text-white" > {{ sidebar_auditor.name or 'Firm team' }}< / div >
< div class = "truncate text-xs text-slate-300" > {{ sidebar_auditor.designation or 'Auditor' }}< / div >
{% if sidebar_auditor.qualification %}< div class = "truncate text-[11px] text-slate-400" > {{ sidebar_auditor.qualification }}< / div > {% endif %}
< / div >
< / div >
< div class = "mt-3 space-y-1 text-[11px] leading-5 text-slate-300" >
{% if sidebar_auditor.mobile %}< div class = "truncate" > Mobile: {{ sidebar_auditor.mobile }}< / div > {% endif %}
{% if sidebar_auditor.email %}< a href = "mailto:{{ sidebar_auditor.email }}" class = "block truncate text-brand-200 hover:text-white" > {{ sidebar_auditor.email }}< / a > {% endif %}
< / div >
< / div >
{% endif %}
{% endif %}
{% if full_auth %}
< nav class = "space-y-4 px-4 pb-6" >
{% set has_employee_self_menu = can_view_employee_portal(current_user, ui_perms, ui_roles) or can_view_own_employee_work(current_user, ui_perms, ui_roles) or can_view_own_employee_attendance(current_user, ui_perms, ui_roles) or can_view_own_employee_leave(current_user, ui_perms, ui_roles) or can_view_own_employee_documents(current_user, ui_perms, ui_roles) or can_view_own_employee_payslips(current_user, ui_perms, ui_roles) or can_request_own_employee_offboarding(current_user, ui_perms, ui_roles) %}
{% set has_team_workspace_menu = can_manage_employee_work(current_user, ui_perms, ui_roles) or can_view_employee_progress(current_user, ui_perms, ui_roles) or can_view_all_employee_attendance(current_user, ui_perms, ui_roles) or can_view_all_employee_leave(current_user, ui_perms, ui_roles) %}
{% set has_team_admin_menu = can_view_employee_dashboard(current_user, ui_perms, ui_roles) or can_view_employees(current_user, ui_perms, ui_roles) or can_approve_employee_registrations(current_user, ui_perms, ui_roles) or can_import_employee_hr(current_user, ui_perms, ui_roles) or can_manage_employee_leave_types(current_user, ui_perms, ui_roles) or can_manage_employee_leave_balances(current_user, ui_perms, ui_roles) or can_view_all_employee_documents(current_user, ui_perms, ui_roles) or can_manage_employee_document_types(current_user, ui_perms, ui_roles) or can_view_employee_onboarding(current_user, ui_perms, ui_roles) or can_manage_employee_onboarding(current_user, ui_perms, ui_roles) or can_view_employee_offboarding(current_user, ui_perms, ui_roles) or can_manage_employee_payroll_structures(current_user, ui_perms, ui_roles) or can_run_employee_payroll(current_user, ui_perms, ui_roles) or can_view_employee_payroll(current_user, ui_perms, ui_roles) %}
{% set has_firm_users_menu = is_system_admin_user and (can_view_users(current_user, ui_perms, ui_roles) or can_manage_users(current_user, ui_perms, ui_roles) or can_view_rbac(current_user, ui_perms, ui_roles)) %}
{% set has_firm_services_menu = can_view_services(current_user, ui_perms, ui_roles) %}
{% set has_firm_clients_menu = can_view_clients(current_user, ui_perms, ui_roles) or can_manage_clients(current_user, ui_perms, ui_roles) or can_export_clients(current_user, ui_perms, ui_roles) %}
{% set has_firm_consultants_menu = can_view_consultants(current_user, ui_perms, ui_roles) or can_manage_consultants(current_user, ui_perms, ui_roles) or can_manage_consultant_service_requests(current_user, ui_perms, ui_roles) or can_manage_consultant_conversions(current_user, ui_perms, ui_roles) %}
{% set has_email_settings_menu = ("Firm Admin" in ui_roles) or is_system_admin_user %}
{% set has_firm_admin_menu = has_firm_users_menu or has_firm_services_menu or has_firm_clients_menu or has_firm_consultants_menu or can_manage_local_storage_agent or has_email_settings_menu or ("Firm Admin" in ui_roles) or is_system_admin_user %}
{% if has_employee_self_menu %}
< details class = "group rounded-xl" { % if current_path . startswith ( ' / employee ' ) or current_path . startswith ( ' / alerts ' ) % } open { % endif % } >
< summary class = "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2 text-sm font-semibold transition hover:bg-slate-800" >
< span > My Workspace< / span >
< span class = "text-xs text-slate-400 transition group-open:rotate-90" > › < / span >
< / summary >
< div class = "mt-1 space-y-1 border-l border-slate-700/70 pl-3" >
{% if can_view_employee_portal(current_user, ui_perms, ui_roles) %}
< a href = "/employee/dashboard" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/employee/dashboard' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Overview< / a >
{% endif %}
{% if can_view_own_employee_work(current_user, ui_perms, ui_roles) %}
< a href = "/employee/work" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employee/work') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > My Work Board< / a >
{% endif %}
{% if can_view_own_employee_attendance(current_user, ui_perms, ui_roles) %}
< a href = "/employee/attendance" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employee/attendance') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > My Attendance< / a >
{% endif %}
{% if can_view_own_employee_leave(current_user, ui_perms, ui_roles) %}
< a href = "/employee/leave" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employee/leave') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > My Leave< / a >
{% endif %}
{% if can_view_own_employee_documents(current_user, ui_perms, ui_roles) %}
< a href = "/employee/documents" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employee/documents') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > My Documents< / a >
{% endif %}
{% if can_view_own_employee_payslips(current_user, ui_perms, ui_roles) %}
< a href = "/employee/payslips" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employee/payslips') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > My Payslips< / a >
{% endif %}
< a href = "/alerts" class = "flex items-center justify-between rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/alerts') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > < span > My Alert< / span > {% if unread_alert_count > 0 %}< span class = "rounded-full bg-brand-600 px-2 py-0.5 text-[10px] font-semibold text-white" > {{ unread_alert_count }}< / span > {% endif %}< / a >
{% if can_request_own_employee_offboarding(current_user, ui_perms, ui_roles) %}
< a href = "/employee/offboarding" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employee/offboarding') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > My Offboarding< / a >
{% endif %}
< / div >
< / details >
{% endif %}
{% if 'Partner' in ui_roles %}
< details class = "group rounded-xl" { % if current_path . startswith ( ' / partner ' ) % } open { % endif % } >
< summary class = "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2 text-sm font-semibold transition hover:bg-slate-800" >
< span > Partner Workspace< / span >
< span class = "text-xs text-slate-400 transition group-open:rotate-90" > › < / span >
< / summary >
< div class = "mt-1 space-y-1 border-l border-slate-700/70 pl-3" >
< a href = "/partner/dashboard" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/partner/dashboard' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Overview< / a >
< a href = "/partner/reviews" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/partner/reviews') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Review Board< / a >
< a href = "/partner/clients" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/partner/clients') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > My Clients< / a >
< a href = "/alerts" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/alerts') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > My Alert< / a >
< / div >
< / details >
{% endif %}
{% if has_team_workspace_menu %}
< details class = "group rounded-xl" { % if current_path . startswith ( ' / manager ' ) or current_path . startswith ( ' / employees / work ' ) or current_path . startswith ( ' / employees / progress ' ) or current_path . startswith ( ' / employees / attendance ' ) or current_path . startswith ( ' / employees / leave ' ) % } open { % endif % } >
< summary class = "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2 text-sm font-semibold transition hover:bg-slate-800" >
< span > Team Workspace< / span >
< span class = "text-xs text-slate-400 transition group-open:rotate-90" > › < / span >
< / summary >
< div class = "mt-1 space-y-1 border-l border-slate-700/70 pl-3" >
{% if can_manage_employee_work(current_user, ui_perms, ui_roles) %}
< a href = "/manager/dashboard" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/manager/dashboard' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Manager Dashboard< / a >
< a href = "/manager/work" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/manager/work') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Team Work Board< / a >
< a href = "/employees/work" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employees/work') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Detailed Allocation< / a >
{% endif %}
{% if can_view_employee_progress(current_user, ui_perms, ui_roles) %}
< a href = "/employees/progress" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employees/progress') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Engagement Progress< / a >
{% endif %}
{% if can_view_all_employee_attendance(current_user, ui_perms, ui_roles) %}
< a href = "/employees/attendance" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/employees/attendance') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Team Attendance< / a >
{% endif %}
{% if can_view_all_employee_leave(current_user, ui_perms, ui_roles) %}
< a href = "/employees/leave" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/employees/leave' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Team Leave< / a >
{% endif %}
< / div >
< / details >
{% endif %}
{% if can_view_billing(current_user, ui_perms, ui_roles) %}
< details class = "group rounded-xl" { % if current_path . startswith ( ' / billing ' ) % } open { % endif % } >
< summary class = "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2 text-sm font-semibold transition hover:bg-slate-800" >
< span > Billing< / span >
< span class = "text-xs text-slate-400 transition group-open:rotate-90" > › < / span >
< / summary >
< div class = "mt-1 space-y-1 border-l border-slate-700/70 pl-3" >
< a href = "/billing" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/billing' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Invoices< / a >
< a href = "/billing/settings" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/billing/settings') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Billing Settings< / a >
{% if can_create_billing(current_user, ui_perms, ui_roles) %}< a href = "/billing/new" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/billing/new' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > New Invoice< / a > {% endif %}
{% if can_generate_billing_invoices(current_user, ui_perms, ui_roles) %}< a href = "/billing/generate" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Generate Bills< / a > {% endif %}
{% if can_view_billing_fee_structure(current_user, ui_perms, ui_roles) %}< a href = "/billing/fee-structures/list" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Fee Structure< / a > {% endif %}
{% if can_import_billing_fee_structure(current_user, ui_perms, ui_roles) %}< a href = "/billing/fee-structures/template" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Download Fee Template< / a > < a href = "/billing/fee-structures/import" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Import Fee Structure< / a > {% endif %}
< / div >
< / details >
{% endif %}
{% if has_team_admin_menu %}
< details class = "group rounded-xl" { % if current_path . startswith ( ' / employees ' ) and not ( current_path . startswith ( ' / employees / work ' ) or current_path . startswith ( ' / employees / progress ' ) or current_path . startswith ( ' / employees / attendance ' ) or current_path = = ' / employees / leave ' ) % } open { % endif % } >
< summary class = "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2 text-sm font-semibold transition hover:bg-slate-800" >
< span > Team Administration< / span >
< span class = "text-xs text-slate-400 transition group-open:rotate-90" > › < / span >
< / summary >
< div class = "mt-1 space-y-1 border-l border-slate-700/70 pl-3" >
{% if can_view_employee_dashboard(current_user, ui_perms, ui_roles) %}< a href = "/employees/dashboard" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > HR Dashboard< / a > {% endif %}
{% if can_view_employees(current_user, ui_perms, ui_roles) %}< a href = "/employees" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Employee Master< / a > {% endif %}
{% if can_approve_employee_registrations(current_user, ui_perms, ui_roles) %}< a href = "/employees/registration-requests" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Employee Registrations< / a > {% endif %}
{% if can_import_employee_hr(current_user, ui_perms, ui_roles) %}< a href = "/employees/imports" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > HR Imports< / a > {% endif %}
{% if can_manage_employee_leave_types(current_user, ui_perms, ui_roles) or can_manage_employee_leave_balances(current_user, ui_perms, ui_roles) %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Attendance & Leave Setup< / div >
{% if can_manage_employee_leave_types(current_user, ui_perms, ui_roles) %}< a href = "/employees/leave-types" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Leave Types< / a > {% endif %}
{% if can_manage_employee_leave_balances(current_user, ui_perms, ui_roles) %}< a href = "/employees/leave-balances" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Leave Balances< / a > {% endif %}
{% endif %}
{% if can_view_all_employee_documents(current_user, ui_perms, ui_roles) or can_manage_employee_document_types(current_user, ui_perms, ui_roles) %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Employee Documents< / div >
{% if can_view_all_employee_documents(current_user, ui_perms, ui_roles) %}< a href = "/employees/documents" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Employee Documents< / a > {% endif %}
{% if can_manage_employee_document_types(current_user, ui_perms, ui_roles) %}< a href = "/employees/document-types" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Document Types< / a > {% endif %}
{% endif %}
{% if can_view_employee_onboarding(current_user, ui_perms, ui_roles) or can_manage_employee_onboarding(current_user, ui_perms, ui_roles) or can_view_employee_offboarding(current_user, ui_perms, ui_roles) %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Employee Lifecycle< / div >
{% if can_view_employee_onboarding(current_user, ui_perms, ui_roles) %}< a href = "/employees/onboarding" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Employee Onboarding< / a > {% endif %}
{% if can_manage_employee_onboarding(current_user, ui_perms, ui_roles) %}< a href = "/employees/onboarding-checklist" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Onboarding Checklist< / a > {% endif %}
{% if can_view_employee_offboarding(current_user, ui_perms, ui_roles) %}< a href = "/employees/offboarding" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Employee Offboarding< / a > {% endif %}
{% endif %}
{% if can_manage_employee_payroll_structures(current_user, ui_perms, ui_roles) or can_run_employee_payroll(current_user, ui_perms, ui_roles) or can_view_employee_payroll(current_user, ui_perms, ui_roles) %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Payroll< / div >
{% if can_manage_employee_payroll_structures(current_user, ui_perms, ui_roles) %}< a href = "/employees/payroll/structures" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Salary Structures< / a > {% endif %}
{% if can_run_employee_payroll(current_user, ui_perms, ui_roles) %}< a href = "/employees/payroll/runs" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Payroll Runs< / a > {% endif %}
{% if can_view_employee_payroll(current_user, ui_perms, ui_roles) %}< a href = "/employees/payroll/payslips" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Employee Payslips< / a > {% endif %}
{% endif %}
< / div >
< / details >
{% endif %}
{% if has_firm_admin_menu %}
< details class = "group rounded-xl" { % if current_path . startswith ( ' / system-settings / users ' ) or current_path . startswith ( ' / system-settings / rbac ' ) or current_path . startswith ( ' / services ' ) or current_path . startswith ( ' / clients ' ) or current_path . startswith ( ' / consultants ' ) or current_path . startswith ( ' / email ' ) or current_path . startswith ( ' / notice-cases ' ) % } open { % endif % } >
< summary class = "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2 text-sm font-semibold transition hover:bg-slate-800" >
< span > Firm Administration< / span >
< span class = "text-xs text-slate-400 transition group-open:rotate-90" > › < / span >
< / summary >
< div class = "mt-1 space-y-1 border-l border-slate-700/70 pl-3" >
{% if "Firm Admin" in ui_roles or is_system_admin_user %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Firm Setup< / div >
< a href = "/system-settings/branding" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/system-settings/branding') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Branding & Identity< / a >
{% if has_email_settings_menu %}< a href = "/email/settings" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/email') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Email Settings< / a > {% endif %}
{% endif %}
{% if has_firm_users_menu %}
< div class = "pt-2 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Users< / div >
{% if can_view_users(current_user, ui_perms, ui_roles) %}< a href = "/system-settings/users" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/system-settings/users' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > User List< / a > {% endif %}
{% if can_manage_users(current_user, ui_perms, ui_roles) %}< a href = "/system-settings/users/new" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/system-settings/users/new') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Create User< / a > {% endif %}
{% if can_view_rbac(current_user, ui_perms, ui_roles) %}< a href = "/system-settings/rbac/roles" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Roles & Permissions< / a > {% endif %}
{% endif %}
{% if can_view_notice_cases(current_user, ui_perms, ui_roles) %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Compliance< / div >
< a href = "/notice-cases" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/notice-cases') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Notices & Cases< / a >
{% if can_manage_notice_cases(current_user, ui_perms, ui_roles) %}< a href = "/notice-cases/new" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > New Notice / Case< / a > {% endif %}
{% endif %}
{% if has_firm_services_menu %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Services< / div >
< a href = "/services" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Firm Services< / a >
< a href = "/services/catalogue" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Service Catalogue< / a >
{% if is_system_admin_user %}< a href = "/services/catalogue/new" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Create Catalogue Service< / a > {% endif %}
< a href = "/services/categories" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Service Categories< / a >
{% if is_system_admin_user %}< a href = "/services/defaults" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > System Default Tasks< / a > {% endif %}
< a href = "/services/templates" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Firm Task Templates< / a >
< a href = "/services/bulk-imports" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Bulk Imports< / a >
{% endif %}
{% if has_firm_clients_menu %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Clients< / div >
{% if can_view_clients(current_user, ui_perms, ui_roles) %}< a href = "/clients" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Client List< / a > {% endif %}
{% if can_manage_clients(current_user, ui_perms, ui_roles) %}< a href = "/clients/new" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Add Client< / a > {% endif %}
{% if can_manage_clients(current_user, ui_perms, ui_roles) %}< a href = "/clients/import" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Import Clients< / a > {% endif %}
{% if can_export_clients(current_user, ui_perms, ui_roles) %}< a href = "/clients/export" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Export Clients< / a > {% endif %}
{% endif %}
{% if has_firm_consultants_menu %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Consultants< / div >
{% if can_view_consultants(current_user, ui_perms, ui_roles) %}< a href = "/consultants" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Consultant List< / a > {% endif %}
{% if can_manage_consultants(current_user, ui_perms, ui_roles) %}< a href = "/consultants/new" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Add Consultant< / a > {% endif %}
{% if can_manage_consultant_service_requests(current_user, ui_perms, ui_roles) %}< a href = "/consultants/service-requests" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Service Requests< / a > {% endif %}
{% if can_manage_consultant_conversions(current_user, ui_perms, ui_roles) %}< a href = "/consultants/conversion-requests" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Conversions< / a > {% endif %}
{% endif %}
{% if can_manage_local_storage_agent %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Local Storage< / div >
< a href = "/documents/storage-nodes" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/documents/storage-nodes') or current_path.startswith('/documents/branch-storage-dashboard') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Local Storage Agent< / a >
{% endif %}
< / div >
< / details >
{% endif %}
{% if is_system_admin_user %}
< details class = "group rounded-xl" { % if current_path . startswith ( ' / domains ' ) or current_path . startswith ( ' / platform-billing ' ) or current_path . startswith ( ' / marketplace ' ) % } open { % endif % } >
< summary class = "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2 text-sm font-semibold transition hover:bg-slate-800" >
< span > Platform< / span >
< span class = "text-xs text-slate-400 transition group-open:rotate-90" > › < / span >
< / summary >
< div class = "mt-1 space-y-1 border-l border-slate-700/70 pl-3" >
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Domain Configuration< / div >
< a href = "/domains" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/domains' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Domain Mappings< / a >
< a href = "/domains/tenant-subdomains" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/domains/tenant-subdomains') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Tenant Subdomains< / a >
< a href = "/domains/firm-domain" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/domains/firm-domain') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Firm Domains< / a >
< a href = "/domains/consultant-domains" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/domains/consultant-domains') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Consultant Domains< / a >
< a href = "/domains/verification" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/domains/verification') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > DNS Verification< / a >
< a href = "/domains/ssl" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/domains/ssl') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > SSL Status< / a >
{% if can_view_platform_billing(current_user, ui_perms, ui_roles) or can_view_marketplace_leads(current_user, ui_perms, ui_roles) %}
< div class = "pt-3 text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-500" > Platform Operations< / div >
{% if can_view_platform_billing(current_user, ui_perms, ui_roles) %}< a href = "/platform-billing" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Platform Billing< / a > {% endif %}
{% if can_view_marketplace_leads(current_user, ui_perms, ui_roles) %}< a href = "/marketplace" class = "block rounded-lg px-3 py-1.5 text-sm text-slate-300 transition hover:bg-slate-800 hover:text-white" > Marketplace< / a > {% endif %}
{% endif %}
< / div >
< / details >
{% endif %}
{% if can_view_management_menus and (can_view_settings(current_user, ui_perms, ui_roles) or can_view_audit(current_user, ui_perms, ui_roles)) %}
< details class = "group rounded-xl" { % if current_path . startswith ( ' / system-settings ' ) % } open { % endif % } >
< summary class = "flex cursor-pointer list-none items-center justify-between rounded-xl px-3 py-2 text-sm font-semibold transition hover:bg-slate-800" >
< span > Core Setup< / span >
< span class = "text-xs text-slate-400 transition group-open:rotate-90" > › < / span >
< / summary >
< div class = "mt-1 space-y-1 border-l border-slate-700/70 pl-3" >
{% if can_view_settings(current_user, ui_perms, ui_roles) %}
< a href = "/system-settings" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path == '/system-settings' %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Dashboard< / a >
{% endif %}
{% if can_view_tenants(current_user, ui_perms, ui_roles) %}
< a href = "/system-settings/tenants" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/system-settings/tenants') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Audit Firms< / a >
{% endif %}
{% if can_view_branches(current_user, ui_perms, ui_roles) %}
< a href = "/system-settings/branches" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/system-settings/branches') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Branches< / a >
{% endif %}
{% if can_view_settings(current_user, ui_perms, ui_roles) %}
< a href = "/system-settings/financial-years" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/system-settings/financial-years') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Financial Years< / a >
{% endif %}
{% if can_view_audit(current_user, ui_perms, ui_roles) %}
< a href = "/system-settings/audit-logs" class = "block rounded-lg px-3 py-1.5 text-sm transition {% if current_path.startswith('/system-settings/audit-logs') %}bg-brand-600 text-white{% else %}text-slate-300 hover:bg-slate-800 hover:text-white{% endif %}" > Audit Logs< / a >
{% endif %}
< / div >
< / details >
{% endif %}
< / nav >
{% endif %}
< / aside >
< div class = "min-h-screen" >
< header class = "border-b border-slate-200 bg-white/90 backdrop-blur" >
< div class = "mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8" >
< div class = "flex items-start justify-between gap-4" >
< div >
< h1 class = "text-lg font-semibold text-slate-900" > {{ title or "Module Workspace" }}< / h1 >
< / div >
< div class = "text-right text-sm" >
{% if full_auth %}
< div class = "flex items-start justify-end gap-3" >
< div class = "min-w-0" >
< div class = "font-medium text-slate-800" > {{ current_user.full_name or current_user.email }}< / div >
< div class = "text-slate-500" > {{ current_user.email }}< / div >
{% if current_user.qualification or current_user.designation %}
< div class = "text-xs text-slate-500" > {{ current_user.qualification or '' }}{% if current_user.qualification and current_user.designation %} • {% endif %}{{ current_user.designation or '' }}< / div >
{% endif %}
< div class = "text-xs text-slate-400" >
Your Firm: {{ current_firm_name }}
• Branch: {{ current_branch_name }}{% if active_financial_year %} • FY: {{ active_financial_year }}{% endif %}
< / div >
< div class = "mt-2 flex justify-end gap-2" >
{% if "Consultant" in ui_roles %}
< a class = "inline-flex rounded-lg border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50" href = "/consultant/profile" > My Profile< / a >
{% elif "Client" in ui_roles %}
< a class = "inline-flex rounded-lg border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50" href = "/client/profile" > My Profile< / a >
{% else %}
< a class = "inline-flex rounded-lg border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50" href = "/employee/profile" > My Profile< / a >
{% endif %}
< a class = "inline-flex rounded-lg border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50" href = "/change-password" > Change Password< / a >
< a class = "inline-flex rounded-lg border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50" href = "/logout" > Logout< / a >
< / div >
< / div >
{% if current_user_photo_url %}
< img src = "{{ current_user_photo_url }}" alt = "Profile photo" class = "h-12 w-12 shrink-0 rounded-2xl border border-slate-200 object-cover shadow-soft" >
{% else %}
< div class = "flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-brand-600 text-sm font-bold text-white shadow-soft" > {{ current_user_initials }}< / div >
{% endif %}
< / div >
{% else %}
{% if domain_context.is_resolved %}
< div class = "mb-2 text-xs text-slate-500" > {{ current_firm_name }}{% if current_branch_name %} • {{ current_branch_name }}{% endif %}< / div >
{% endif %}
< a class = "inline-flex rounded-lg bg-brand-600 px-3 py-2 text-sm font-medium text-white hover:bg-brand-700" href = "/login" > Login< / a >
{% endif %}
< / div >
< / div >
{% if full_auth and (can_switch_service_tenant(current_user, ui_perms, ui_roles) or can_switch_service_branch(current_user, ui_perms, ui_roles) or can_switch_employee_tenant(current_user, ui_perms, ui_roles) or can_switch_employee_branch(current_user, ui_perms, ui_roles) or can_view_settings(current_user, ui_perms, ui_roles)) %}
< div class = "mt-4 flex flex-wrap items-end gap-4 border-t border-slate-200 pt-4" >
{% if can_switch_service_tenant(current_user, ui_perms, ui_roles) or can_switch_employee_tenant(current_user, ui_perms, ui_roles) %}
{% set context_tenants = get_context_tenants(request, current_user, ui_perms, ui_roles) %}
< div >
< label class = "mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500" > Active Audit Firm Context< / label >
< select onchange = "if(this.value){window.location.href=this.value;}" class = "rounded-xl border border-slate-300 px-3 py-2 text-sm" >
{% for tenant in context_tenants %}
< option value = "/system-settings/context/tenant/{{ tenant.id }}" { % if tenant . id = = active_tenant_id % } selected { % endif % } > {{ tenant.name }} ({{ tenant.code }})< / option >
{% endfor %}
< / select >
< / div >
{% endif %}
{% if can_switch_service_branch(current_user, ui_perms, ui_roles) or can_switch_employee_branch(current_user, ui_perms, ui_roles) %}
{% set context_branches = get_context_branches(request, current_user, ui_perms, ui_roles) %}
< div >
< label class = "mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500" > Active Branch Context< / label >
< select onchange = "if(this.value){window.location.href=this.value;}" class = "rounded-xl border border-slate-300 px-3 py-2 text-sm" >
< option value = "/system-settings/context/branch/0" { % if not active_branch_id % } selected { % endif % } > All Branches< / option >
{% for branch in context_branches %}
< option value = "/system-settings/context/branch/{{ branch.id }}" { % if branch . id = = active_branch_id % } selected { % endif % } > {{ branch.name }} ({{ branch.code }})< / option >
{% endfor %}
< / select >
< / div >
{% endif %}
{% if can_view_settings(current_user, ui_perms, ui_roles) %}
{% set context_financial_years = get_context_financial_years(request, current_user, ui_perms, ui_roles) %}
< div >
< label class = "mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500" > Active Financial Year< / label >
< select onchange = "if(this.value){window.location.href=this.value;}" class = "rounded-xl border border-slate-300 px-3 py-2 text-sm" >
{% for fy in context_financial_years %}
< option value = "/system-settings/context/financial-year/{{ fy.year_code }}" { % if fy . year_code = = active_financial_year % } selected { % endif % } > FY {{ fy.year_code }}{% if fy.is_current %} (Current){% endif %}{% if fy.is_locked %} - Locked{% endif %}< / option >
{% endfor %}
< / select >
< / div >
{% endif %}
< div class = "text-xs text-slate-500" >
Active Scope:
{{ current_firm_name }}
• {{ current_branch_name if active_branch_id else "All Branches" }}{% if active_financial_year %} • FY {{ active_financial_year }}{% endif %}
< / div >
< / div >
{% endif %}
< / div >
< / header >
< main class = "mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8" >
{% if flash %}
< div class = "mb-6 rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900 shadow-soft" >
{{ flash }}
< / div >
{% endif %}
{% block content %}{% endblock %}
< / main >
< / div >
< / div >
{% if full_auth %}
< div id = "af-alert-toast-container" class = "fixed right-4 top-4 z-50 flex w-[min(24rem,calc(100vw-2rem))] flex-col gap-3" > < / div >
< script >
( function ( ) {
const POLL _URL = "/alerts/poll" ;
const POLL _INTERVAL _MS = 45000 ;
const STORAGE _KEY = "af_popup_seen_alert_ids_v1" ;
const MAX _STORED _IDS = 100 ;
function readSeenIds ( ) {
try {
const raw = window . localStorage . getItem ( STORAGE _KEY ) ;
const parsed = raw ? JSON . parse ( raw ) : [ ] ;
return Array . isArray ( parsed ) ? parsed . map ( String ) : [ ] ;
} catch ( err ) {
return [ ] ;
}
}
function writeSeenIds ( ids ) {
try {
const unique = Array . from ( new Set ( ids . map ( String ) ) ) . slice ( - MAX _STORED _IDS ) ;
window . localStorage . setItem ( STORAGE _KEY , JSON . stringify ( unique ) ) ;
} catch ( err ) {
// Ignore localStorage issues; alerts page remains available.
}
}
function updateHeaderCount ( count ) {
const badge = document . getElementById ( "af-alerts-header-count" ) ;
if ( ! badge ) return ;
const n = Number ( count || 0 ) ;
badge . textContent = String ( n ) ;
if ( n > 0 ) {
badge . classList . remove ( "hidden" ) ;
} else {
badge . classList . add ( "hidden" ) ;
}
}
function priorityClasses ( priority ) {
if ( priority === "critical" ) return "border-red-200 bg-red-50 text-red-900" ;
if ( priority === "high" ) return "border-amber-200 bg-amber-50 text-amber-900" ;
if ( priority === "low" ) return "border-slate-200 bg-white text-slate-800" ;
return "border-blue-200 bg-blue-50 text-blue-900" ;
}
function showToast ( alert ) {
const container = document . getElementById ( "af-alert-toast-container" ) ;
if ( ! container || ! alert ) return ;
const wrapper = document . createElement ( "div" ) ;
wrapper . className = "overflow-hidden rounded-2xl border shadow-soft " + priorityClasses ( alert . priority ) ;
wrapper . innerHTML = `
<div class="flex items-start gap-3 p-4">
<div class="mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-white/80 text-base">🔔</div>
<div class="min-w-0 flex-1">
<div class="text-xs font-semibold uppercase tracking-wide opacity-75">New Alert</div>
<div class="mt-0.5 line-clamp-2 text-sm font-semibold"></div>
<div class="mt-1 line-clamp-3 text-xs opacity-80"></div>
<div class="mt-3 flex items-center gap-2">
<a class="inline-flex rounded-lg bg-white/90 px-3 py-1.5 text-xs font-semibold text-slate-800 hover:bg-white" href="#">View</a>
<button type="button" class="inline-flex rounded-lg px-3 py-1.5 text-xs font-semibold opacity-70 hover:bg-white/50">Dismiss</button>
</div>
</div>
</div> ` ;
wrapper . querySelector ( ".text-sm.font-semibold" ) . textContent = alert . title || "Alert" ;
wrapper . querySelector ( ".text-xs.opacity-80" ) . textContent = alert . message || "You have a new notification." ;
const viewLink = wrapper . querySelector ( "a" ) ;
viewLink . href = alert . target _url || "/alerts" ;
wrapper . querySelector ( "button" ) . addEventListener ( "click" , function ( ) { wrapper . remove ( ) ; } ) ;
container . prepend ( wrapper ) ;
window . setTimeout ( function ( ) {
if ( wrapper && wrapper . parentNode ) wrapper . remove ( ) ;
} , 12000 ) ;
}
async function pollAlerts ( ) {
try {
const response = await fetch ( POLL _URL , {
method : "GET" ,
credentials : "same-origin" ,
headers : { "Accept" : "application/json" }
} ) ;
if ( ! response . ok ) return ;
const data = await response . json ( ) ;
updateHeaderCount ( data . unread _count || 0 ) ;
const alerts = Array . isArray ( data . alerts ) ? data . alerts : [ ] ;
if ( ! alerts . length ) return ;
const seen = readSeenIds ( ) ;
const newlySeen = seen . slice ( ) ;
alerts . reverse ( ) . forEach ( function ( alert ) {
const id = String ( alert . id ) ;
if ( ! seen . includes ( id ) ) {
showToast ( alert ) ;
newlySeen . push ( id ) ;
}
} ) ;
writeSeenIds ( newlySeen ) ;
} catch ( err ) {
// Keep polling silent; the /alerts page remains the fallback.
}
}
if ( document . readyState === "loading" ) {
document . addEventListener ( "DOMContentLoaded" , function ( ) {
pollAlerts ( ) ;
window . setInterval ( pollAlerts , POLL _INTERVAL _MS ) ;
} ) ;
} else {
pollAlerts ( ) ;
window . setInterval ( pollAlerts , POLL _INTERVAL _MS ) ;
}
} ) ( ) ;
< / script >
{% endif %}
< / body >
< / html >