Rollback unstable UIUX Phase 1 navigation changes

This commit is contained in:
A R R R Associates
2026-07-10 13:59:57 +05:30
parent 933a1e4a36
commit 18cfb0b8a7
12 changed files with 174 additions and 429 deletions
+17 -33
View File
@@ -4,22 +4,22 @@
<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)|default(none, true) %}
<meta name="theme-color" content="{{ (__firm_branding.primary_color|default('', true)) if __firm_branding else '#1e3a8a' }}" />
{% set __firm_branding = get_current_firm_branding(request, __title_user) %}
<meta name="theme-color" content="{{ __firm_branding.primary_color or '#1e3a8a' }}" />
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="ARRR ERP" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<link rel="apple-touch-icon" href="/static/pwa/icons/icon-192.png" />
{% if __firm_branding and (__firm_branding.favicon_url|default("", true)) %}<link rel="icon" href="{{ __firm_branding.favicon_url }}" />{% endif %}
{% 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|default("", true)) if __firm_branding else "Audit Firm ERP" }}</title>
<title>{{ title or "Welcome" }} | {{ __firm_branding.firm_name or "Audit Firm ERP" }}</title>
{% endif %}
<script src="https://cdn.tailwindcss.com"></script>
<script>
@@ -76,25 +76,13 @@
<link rel="stylesheet" href="/static/css/theme_tokens.css?v=7q-dashboard-align-strong" />
<style>
:root {
--af-color-brand-500: {{ (__firm_branding.primary_color|default('', true)) if __firm_branding else '#2563eb' }};
--af-color-brand-600: {{ (__firm_branding.primary_color|default('', true)) if __firm_branding else '#1d4ed8' }};
--af-color-brand-700: {{ (__firm_branding.accent_color|default('', true)) if __firm_branding else '#1e40af' }};
--af-color-brand-800: {{ (__firm_branding.accent_color|default('', true)) if __firm_branding else '#1e3a8a' }};
--af-color-brand-900: {{ (__firm_branding.accent_color|default('', true)) if __firm_branding else '#172554' }};
--af-color-brand-500: {{ __firm_branding.primary_color or '#2563eb' }};
--af-color-brand-600: {{ __firm_branding.primary_color or '#1d4ed8' }};
--af-color-brand-700: {{ __firm_branding.accent_color or '#1e40af' }};
--af-color-brand-800: {{ __firm_branding.accent_color or '#1e3a8a' }};
--af-color-brand-900: {{ __firm_branding.accent_color or '#172554' }};
}
</style>
<style>
.af-nav-link,.af-nav-summary{display:inline-flex;align-items:center;border-radius:.75rem;padding:.55rem .8rem;font-size:.78rem;font-weight:700;color:#475569;transition:.15s;white-space:nowrap}
.af-nav-link:hover,.af-nav-summary:hover{background:#f1f5f9;color:var(--af-color-brand-700)}
.af-nav-link-active{background:var(--af-color-brand-600)!important;color:#fff!important;box-shadow:var(--af-shadow-soft)}
.af-nav-dropdown{position:relative}
.af-nav-summary{cursor:pointer;list-style:none}
.af-nav-summary::-webkit-details-marker{display:none}
.af-nav-menu{position:absolute;left:0;top:calc(100% + .55rem);z-index:70;display:flex;min-width:15rem;flex-direction:column;gap:.15rem;border:1px solid #e2e8f0;border-radius:1rem;background:#fff;padding:.5rem;box-shadow:var(--af-shadow-card)}
.af-nav-menu .af-nav-link{display:flex;width:100%}
@media (max-width:1023px){#af-mobile-nav .af-nav-link,#af-mobile-nav .af-nav-summary{padding:.7rem .8rem}}
</style>
</head>
<body class="min-h-screen bg-slate-100 text-slate-900">
{% set otp_ok = request.session.get("otp_verified", False) %}
@@ -111,31 +99,29 @@
{% 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)|default(none, true) %}
{% 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|default("", true)) if firm_branding else "Audit Firm ERP" %}
{% if not current_firm_name %}{% set current_firm_name = "Audit Firm ERP" %}{% endif %}
{% set current_branch_name = (firm_branding.branch_name|default("", true)) if firm_branding else "" %}
{% set current_firm_logo_url = (firm_branding.logo_url|default("", true)) if firm_branding else "" %}
{% set domain_is_resolved = ((get_domain_context(request)|default({}, true)).get("is_resolved", false)) %}
{% 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) %}
{# Navigation UI Cleanup V2.1
Sidebar is retained only for System Admin users.
All other authenticated users use the floating top workspace bar on every page.
Existing routes and old sidebar code are kept intact. #}
{% set use_clean_top_nav = full_auth %}
{% set use_clean_top_nav = full_auth and not is_system_admin_user %}
<div class="min-h-screen {% if not use_clean_top_nav %}lg:grid lg:grid-cols-[260px_minmax(0,1fr)]{% endif %}">
{% if not use_clean_top_nav %}
<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 current_firm_logo_url %}<img src="{{ current_firm_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 %}
{% 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_is_resolved %}{{ current_branch_name or 'Domain Workspace' }}{% else %}Secure Practice Workspace{% endif %}</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>
@@ -458,7 +444,7 @@
{% endif %}
</div>
{% else %}
{% if domain_is_resolved %}
{% 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>
@@ -517,7 +503,6 @@
{% endif %}
<main class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
{% if full_auth %}{% include "ui/templates/components/breadcrumbs.html" %}{% endif %}
{% 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 }}
@@ -691,6 +676,5 @@
})();
</script>
<script src="/static/js/navigation_shell.js?v=uiux-navigation-v1"></script>
</body>
</html>
@@ -1,21 +0,0 @@
{% set crumb_path = request.url.path %}
{% set crumb_parts = crumb_path.strip('/').split('/') if crumb_path != '/' else [] %}
{% set crumb_labels = {
'system-admin':'System Administration','firm-admin':'Firm Administration','partner':'Partner','manager':'Manager','employee':'Employee','employees':'Team','consultant':'Consultant','consultants':'Consultants','client':'Client','clients':'Clients','services':'Services','engagements':'Engagements','documents':'Documents','billing':'Billing','system-settings':'Firm Setup','users':'Users','rbac':'Roles & Permissions','roles':'Roles','branches':'Branches','financial-years':'Financial Years','audit-logs':'Audit Logs','alerts':'Alerts','work':'Work','attendance':'Attendance','leave':'Leave','profile':'Profile','dashboard':'Dashboard','reviews':'Reviews','compliance':'Compliance','messages':'Messages','imports':'Imports','import':'Import','export':'Export','new':'New','edit':'Edit','settings':'Settings','domains':'Domains','email':'Email','aqmm':'AQMM','peer-review':'Peer Review','evidence':'Evidence'
} %}
<div id="af-page-context" class="mb-5 rounded-2xl border border-slate-200 bg-white px-4 py-3 shadow-soft" data-af-page-context>
<div class="flex flex-wrap items-center justify-between gap-3">
<nav aria-label="Breadcrumb" class="min-w-0 overflow-x-auto">
<ol class="flex min-w-max items-center gap-2 text-xs text-slate-500">
<li><a href="/workspaces" class="font-semibold text-slate-600 hover:text-brand-700">Home</a></li>
{% for part in crumb_parts %}
{% set upto = '/' ~ '/'.join(crumb_parts[:loop.index]) %}
{% set label = crumb_labels.get(part, part.replace('-', ' ')|title) %}
<li aria-hidden="true" class="text-slate-300"></li>
<li>{% if not loop.last %}<a href="{{ upto }}" class="hover:text-brand-700">{{ label }}</a>{% else %}<span class="font-semibold text-slate-800" aria-current="page">{{ title or label }}</span>{% endif %}</li>
{% endfor %}
</ol>
</nav>
<div id="af-shared-back-slot" class="shrink-0"></div>
</div>
</div>
@@ -1,182 +0,0 @@
{% set nav_path = request.url.path %}
{% set nav_roles = ui_roles if ui_roles is defined else [] %}
{% set nav_perms = ui_perms if ui_perms is defined else [] %}
{% set nav_workspace = 'employee' %}
{% if nav_path.startswith('/system-admin') %}{% set nav_workspace = 'system' %}
{% elif nav_path.startswith('/firm-admin') %}{% set nav_workspace = 'firm' %}
{% elif nav_path.startswith('/partner') %}{% set nav_workspace = 'partner' %}
{% elif nav_path.startswith('/manager') or nav_path.startswith('/employees/work') or nav_path.startswith('/employees/progress') or nav_path.startswith('/employees/attendance') or nav_path.startswith('/employees/leave') %}{% set nav_workspace = 'manager' %}
{% elif nav_path.startswith('/consultant') %}{% set nav_workspace = 'consultant' %}
{% elif nav_path.startswith('/client') %}{% set nav_workspace = 'client' %}
{% elif nav_path.startswith('/employee') %}{% set nav_workspace = 'employee' %}
{% elif 'System Admin' in nav_roles %}{% set nav_workspace = 'system' %}
{% elif 'Firm Admin' in nav_roles %}{% set nav_workspace = 'firm' %}
{% elif 'Partner' in nav_roles %}{% set nav_workspace = 'partner' %}
{% elif 'Manager' in nav_roles or 'Branch Manager' in nav_roles %}{% set nav_workspace = 'manager' %}
{% elif 'Consultant' in nav_roles %}{% set nav_workspace = 'consultant' %}
{% elif 'Client' in nav_roles %}{% set nav_workspace = 'client' %}
{% endif %}
{% macro nav_link(url, label, prefixes=None) -%}
{% set checks = prefixes if prefixes else [url] %}
{% set ns = namespace(active=false) %}
{% for prefix in checks %}{% if nav_path == prefix or nav_path.startswith(prefix ~ '/') %}{% set ns.active = true %}{% endif %}{% endfor %}
<a href="{{ url }}" class="af-nav-link {{ 'af-nav-link-active' if ns.active else '' }}" {% if ns.active %}aria-current="page"{% endif %}>{{ label }}</a>
{%- endmacro %}
{% macro dropdown(label, prefixes) -%}
{% set ns = namespace(active=false) %}
{% for prefix in prefixes %}{% if nav_path == prefix or nav_path.startswith(prefix ~ '/') %}{% set ns.active = true %}{% endif %}{% endfor %}
<details class="af-nav-dropdown group" data-af-nav-dropdown>
<summary class="af-nav-summary {{ 'af-nav-link-active' if ns.active else '' }}">{{ label }}<span aria-hidden="true" class="ml-1 text-[10px]"></span></summary>
<div class="af-nav-menu">{{ caller() }}</div>
</details>
{%- endmacro %}
<nav aria-label="Primary navigation" class="hidden lg:block">
<div class="flex min-w-0 items-center gap-1 overflow-visible">
{% if nav_workspace == 'system' %}
{{ nav_link('/system-admin/dashboard', 'Platform', ['/system-admin','/workspaces']) }}
{% call dropdown('Firms', ['/system-settings/tenants','/system-settings/branches','/wizards/system/firm']) %}
{{ nav_link('/system-settings/tenants', 'Audit Firms') }}
{{ nav_link('/system-settings/branches', 'Branches') }}
{{ nav_link('/wizards/system/firm/new', 'Create Firm Wizard') }}
{% endcall %}
{% call dropdown('Catalogue', ['/services/catalogue','/services/categories','/services/defaults','/marketplace']) %}
{{ nav_link('/services/catalogue', 'Service Catalogue') }}
{{ nav_link('/services/categories', 'Service Categories') }}
{{ nav_link('/services/defaults', 'System Default Tasks') }}
{{ nav_link('/marketplace', 'Marketplace') }}
{% endcall %}
{% call dropdown('Infrastructure', ['/domains','/email/platform-smtp','/documents/storage-nodes']) %}
{{ nav_link('/domains', 'Domain Mappings') }}
{{ nav_link('/domains/verification', 'DNS Verification') }}
{{ nav_link('/domains/ssl', 'SSL Status') }}
{{ nav_link('/email/platform-smtp', 'Platform SMTP') }}
{{ nav_link('/documents/storage-nodes', 'Storage Agents') }}
{% endcall %}
{{ nav_link('/platform-billing', 'Billing') }}
{% call dropdown('Administration', ['/system-settings/users','/system-settings/rbac','/system-settings/financial-years','/system-settings/audit-logs']) %}
{{ nav_link('/system-settings/users', 'Users') }}
{{ nav_link('/system-settings/rbac/roles', 'Roles & Permissions') }}
{{ nav_link('/system-settings/financial-years', 'Financial Years') }}
{{ nav_link('/system-settings/audit-logs', 'Audit Logs') }}
{% endcall %}
{% elif nav_workspace == 'firm' %}
{{ nav_link('/firm-admin/dashboard', 'Dashboard') }}
{% if can_view_clients(current_user, nav_perms, nav_roles) %}{% call dropdown('Clients', ['/clients']) %}
{{ nav_link('/clients', 'Client List') }}
{{ nav_link('/clients/new', 'Add Client') }}
{{ nav_link('/clients/import', 'Import Clients') }}
{{ nav_link('/clients/export', 'Export Clients') }}
{% endcall %}{% endif %}
{% call dropdown('Services', ['/services']) %}
{{ nav_link('/services', 'Firm Services') }}
{{ nav_link('/services/templates', 'Task Templates') }}
{{ nav_link('/services/bulk-imports', 'Bulk Imports') }}
{{ nav_link('/services/engagements', 'Engagements') }}
{% endcall %}
{% call dropdown('Team', ['/employees','/consultants']) %}
{{ nav_link('/employees', 'Employees') }}
{{ nav_link('/employees/work', 'Work Allocation') }}
{{ nav_link('/employees/attendance', 'Attendance') }}
{{ nav_link('/employees/leave', 'Leave') }}
{{ nav_link('/consultants', 'Consultants') }}
{% endcall %}
{% if can_view_documents_menu %}{{ nav_link('/documents', 'Documents') }}{% endif %}
{{ nav_link('/billing', 'Billing') }}
{% call dropdown('Firm Setup', ['/system-settings','/email/settings']) %}
{{ nav_link('/system-settings', 'Settings Dashboard') }}
{{ nav_link('/system-settings/branding', 'Branding') }}
{{ nav_link('/system-settings/branches', 'Branches') }}
{{ nav_link('/system-settings/users', 'Users') }}
{{ nav_link('/system-settings/rbac/roles', 'Roles & Permissions') }}
{{ nav_link('/system-settings/financial-years', 'Financial Years') }}
{{ nav_link('/email/settings', 'Email Settings') }}
{{ nav_link('/system-settings/audit-logs', 'Audit Logs') }}
{% endcall %}
{% elif nav_workspace == 'partner' %}
{{ nav_link('/partner/dashboard', 'Dashboard') }}
{{ nav_link('/partner/reviews', 'Reviews') }}
{{ nav_link('/partner/clients', 'Clients') }}
{{ nav_link('/services/engagements', 'Engagements') }}
{% if can_view_documents_menu %}{{ nav_link('/documents', 'Documents') }}{% endif %}
{{ nav_link('/billing', 'Billing') }}
{{ nav_link('/aqmm/dashboard', 'AQMM') }}
{{ nav_link('/peer-review/evidence', 'Peer Review') }}
{% elif nav_workspace == 'manager' %}
{{ nav_link('/manager/dashboard', 'Dashboard') }}
{% if can_manage_employee_work(current_user, nav_perms, nav_roles) %}{{ nav_link('/manager/work', 'Work Board') }}{{ nav_link('/employees/work', 'Allocation') }}{% endif %}
{% if can_view_employee_progress(current_user, nav_perms, nav_roles) %}{{ nav_link('/employees/progress', 'Progress') }}{% endif %}
{% if can_view_all_employee_attendance(current_user, nav_perms, nav_roles) %}{{ nav_link('/employees/attendance', 'Attendance') }}{% endif %}
{% if can_view_all_employee_leave(current_user, nav_perms, nav_roles) %}{{ nav_link('/employees/leave', 'Leave') }}{% endif %}
{% elif nav_workspace == 'employee' %}
{% if can_view_employee_portal(current_user, nav_perms, nav_roles) %}{{ nav_link('/employee/dashboard', 'Dashboard') }}{% endif %}
{% if can_view_own_employee_work(current_user, nav_perms, nav_roles) %}{{ nav_link('/employee/work', 'Work') }}{% endif %}
{% if can_view_own_employee_attendance(current_user, nav_perms, nav_roles) %}{{ nav_link('/employee/attendance', 'Attendance') }}{% endif %}
{% if can_view_own_employee_leave(current_user, nav_perms, nav_roles) %}{{ nav_link('/employee/leave', 'Leave') }}{% endif %}
{% if can_view_own_employee_documents(current_user, nav_perms, nav_roles) %}{{ nav_link('/employee/documents', 'Documents') }}{% endif %}
{% if can_view_own_employee_payslips(current_user, nav_perms, nav_roles) %}{{ nav_link('/employee/payslips', 'Payslips') }}{% endif %}
{% if can_request_own_employee_offboarding(current_user, nav_perms, nav_roles) %}{{ nav_link('/employee/offboarding', 'Offboarding') }}{% endif %}
{% elif nav_workspace == 'consultant' %}
{{ nav_link('/consultant/dashboard', 'Dashboard') }}
{{ nav_link('/consultant/work', 'Work', ['/consultant/work','/consultant/assignments']) }}
{{ nav_link('/consultant/managed-clients', 'Clients') }}
{{ nav_link('/consultant/service-requests', 'Requests') }}
{{ nav_link('/consultant/documents', 'Documents') }}
{{ nav_link('/consultant/communications', 'Messages') }}
{{ nav_link('/consultant/workspace', 'Workspace') }}
{% elif nav_workspace == 'client' %}
{{ nav_link('/client/dashboard', 'Dashboard') }}
{{ nav_link('/client/compliance', 'Compliance', ['/client/compliance','/client/engagements']) }}
{{ nav_link('/client/documents', 'Documents') }}
{{ nav_link('/client/messages', 'Messages') }}
{{ nav_link('/client/billing', 'Bills & Payments') }}
{% endif %}
</div>
</nav>
<div id="af-mobile-nav" class="fixed inset-0 z-[80] hidden lg:hidden" aria-hidden="true">
<button type="button" class="absolute inset-0 bg-slate-950/45" data-af-mobile-close aria-label="Close navigation"></button>
<div class="absolute inset-y-0 left-0 w-[86vw] max-w-sm overflow-y-auto bg-white p-4 shadow-2xl">
<div class="mb-4 flex items-center justify-between border-b border-slate-200 pb-3">
<div><div class="text-sm font-bold text-slate-900">Navigation</div><div class="text-xs text-slate-500">{{ current_firm_name }}</div></div>
<button type="button" data-af-mobile-close class="rounded-xl border border-slate-200 px-3 py-2 text-sm font-semibold text-slate-700">Close</button>
</div>
<div class="space-y-1" data-af-mobile-links></div>
<div class="mt-5 space-y-3 border-t border-slate-200 pt-4">
<div class="text-[11px] font-bold uppercase tracking-wider text-slate-400">Workspace context</div>
<a href="/workspaces" class="block rounded-xl border border-slate-200 px-3 py-2 text-sm font-semibold text-slate-700">Change workspace</a>
{% if can_switch_service_tenant(current_user, nav_perms, nav_roles) or can_switch_employee_tenant(current_user, nav_perms, nav_roles) %}
{% set mobile_tenants = get_context_tenants(request, current_user, nav_perms, nav_roles) %}
<select aria-label="Active Audit Firm" onchange="if(this.value){window.location.href=this.value;}" class="w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
{% for tenant in mobile_tenants %}<option value="/system-settings/context/tenant/{{ tenant.id }}" {% if tenant.id == active_tenant_id %}selected{% endif %}>{{ tenant.name }}</option>{% endfor %}
</select>
{% endif %}
{% if can_switch_service_branch(current_user, nav_perms, nav_roles) or can_switch_employee_branch(current_user, nav_perms, nav_roles) %}
{% set mobile_branches = get_context_branches(request, current_user, nav_perms, nav_roles) %}
<select aria-label="Active Branch" onchange="if(this.value){window.location.href=this.value;}" class="w-full rounded-xl border border-slate-200 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 mobile_branches %}<option value="/system-settings/context/branch/{{ branch.id }}" {% if branch.id == active_branch_id %}selected{% endif %}>{{ branch.name }}</option>{% endfor %}
</select>
{% endif %}
{% if can_view_settings(current_user, nav_perms, nav_roles) %}
{% set mobile_fys = get_context_financial_years(request, current_user, nav_perms, nav_roles) %}
<select aria-label="Active Financial Year" onchange="if(this.value){window.location.href=this.value;}" class="w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
{% for fy in mobile_fys %}<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 %}</option>{% endfor %}
</select>
{% endif %}
</div>
<div class="mt-5 space-y-1 border-t border-slate-200 pt-4">
{% if "Consultant" in nav_roles %}<a class="block rounded-xl px-3 py-2 text-sm text-slate-700 hover:bg-slate-50" href="/consultant/profile">My Profile</a>{% elif "Client" in nav_roles %}<a class="block rounded-xl px-3 py-2 text-sm text-slate-700 hover:bg-slate-50" href="/client/profile">My Profile</a>{% else %}<a class="block rounded-xl px-3 py-2 text-sm text-slate-700 hover:bg-slate-50" href="/employee/profile">My Profile</a>{% endif %}
<a class="block rounded-xl px-3 py-2 text-sm text-slate-700 hover:bg-slate-50" href="/change-password">Change Password</a>
<a class="block rounded-xl px-3 py-2 text-sm font-semibold text-red-600 hover:bg-red-50" href="/logout">Logout</a>
</div>
</div>
</div>
@@ -1,72 +1,85 @@
<header class="sticky top-0 z-50 border-b border-slate-200/80 bg-slate-100/95 backdrop-blur-xl">
<div class="mx-auto max-w-[1600px] px-3 py-2 sm:px-5 lg:px-8">
<div class="rounded-2xl border border-slate-200 bg-white/95 shadow-soft">
<div class="flex items-center gap-3 px-3 py-2.5 sm:px-4">
<a href="/workspaces" class="flex min-w-0 items-center gap-3 rounded-xl px-1 py-1 transition hover:bg-slate-50 lg:min-w-[220px]">
{% if current_firm_logo_url|default("", true) %}
<img src="{{ current_firm_logo_url }}" alt="{{ current_firm_name }} logo" class="h-10 w-10 shrink-0 rounded-xl border border-slate-100 bg-white object-contain p-1 shadow-sm" />
<header class="sticky top-0 z-40 border-b border-slate-200/70 bg-slate-100/85 backdrop-blur-xl">
<div class="mx-auto max-w-[1500px] px-4 py-3 sm:px-6 lg:px-8">
<div class="rounded-3xl border border-slate-200 bg-white/95 px-4 py-3 shadow-soft">
<div class="flex flex-wrap items-center gap-3">
<a href="/workspaces" class="flex min-w-[220px] items-center gap-3 rounded-2xl px-2 py-1 transition hover:bg-slate-50">
{% if firm_branding.logo_url %}
<img src="{{ firm_branding.logo_url }}" alt="{{ current_firm_name }} logo" class="h-11 w-11 rounded-2xl border border-slate-100 bg-white object-contain p-1 shadow-soft" />
{% else %}
<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-brand-600 text-sm font-bold text-white shadow-sm">{{ (current_firm_name[:2] if current_firm_name else 'AF')|upper }}</div>
<div class="flex h-11 w-11 items-center justify-center rounded-2xl bg-brand-600 text-sm font-bold text-white shadow-soft">{{ (current_firm_name[:2] if current_firm_name else 'AF')|upper }}</div>
{% endif %}
<div class="hidden min-w-0 sm:block">
<div class="max-w-[220px] truncate text-sm font-bold text-slate-900">{{ current_firm_name }}</div>
<div class="max-w-[240px] truncate text-[11px] text-slate-500">{{ current_branch_name if active_branch_id else 'All Branches' }}{% if active_financial_year %} • FY {{ active_financial_year }}{% endif %}</div>
<div class="min-w-0">
<div class="truncate text-sm font-bold text-slate-900">{{ current_firm_name }}</div>
<div class="truncate text-xs text-slate-500">{{ current_branch_name if active_branch_id else 'All Branches' }}{% if active_financial_year %} • FY {{ active_financial_year }}{% endif %}</div>
</div>
</a>
<div class="hidden min-w-0 flex-1 items-center gap-2 md:flex">
<div class="flex min-w-0 flex-1 flex-wrap items-center gap-2">
{% include "ui/templates/components/workspace_switcher.html" %}
{% 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)) %}
{% 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) %}
<select title="Active Audit Firm" aria-label="Active Audit Firm" onchange="if(this.value){window.location.href=this.value;}" class="max-w-[180px] rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-700 outline-none hover:border-brand-200 focus:ring-2 focus:ring-brand-100">
{% for tenant in context_tenants %}<option value="/system-settings/context/tenant/{{ tenant.id }}" {% if tenant.id == active_tenant_id %}selected{% endif %}>{{ tenant.name }}</option>{% endfor %}
<select title="Active Audit Firm" onchange="if(this.value){window.location.href=this.value;}" class="max-w-[210px] rounded-2xl border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-700 shadow-sm outline-none hover:border-brand-200 focus:ring-2 focus:ring-brand-100">
{% for tenant in context_tenants %}
<option value="/system-settings/context/tenant/{{ tenant.id }}" {% if tenant.id == active_tenant_id %}selected{% endif %}>{{ tenant.name }}</option>
{% endfor %}
</select>
{% 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) %}
<select title="Active Branch" aria-label="Active Branch" onchange="if(this.value){window.location.href=this.value;}" class="max-w-[160px] rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-700 outline-none hover:border-brand-200 focus:ring-2 focus:ring-brand-100">
<select title="Active Branch" onchange="if(this.value){window.location.href=this.value;}" class="max-w-[190px] rounded-2xl border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-700 shadow-sm outline-none hover:border-brand-200 focus:ring-2 focus:ring-brand-100">
<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 }}</option>{% endfor %}
{% for branch in context_branches %}
<option value="/system-settings/context/branch/{{ branch.id }}" {% if branch.id == active_branch_id %}selected{% endif %}>{{ branch.name }}</option>
{% endfor %}
</select>
{% 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) %}
<select title="Active Financial Year" data-testid="active-fy-select" aria-label="Active Financial Year" onchange="if(this.value){window.location.href=this.value;}" class="max-w-[145px] rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-700 outline-none hover:border-brand-200 focus:ring-2 focus:ring-brand-100">
{% 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 title="Active Financial Year" data-testid="active-fy-select" aria-label="Active Financial Year" onchange="if(this.value){window.location.href=this.value;}" class="max-w-[170px] rounded-2xl border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-700 shadow-sm outline-none hover:border-brand-200 focus:ring-2 focus:ring-brand-100">
{% 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>
{% endif %}
{% endif %}
</div>
<div class="ml-auto flex items-center gap-2">
<a href="/alerts" aria-label="Alerts" class="relative inline-flex h-10 items-center justify-center rounded-xl border border-slate-200 bg-white px-3 text-xs font-semibold text-slate-700 hover:border-brand-200 hover:text-brand-700">
<span class="hidden sm:inline">Alerts</span><span class="sm:hidden" aria-hidden="true">🔔</span>
{% if unread_alert_count %}<span class="ml-1.5 rounded-full bg-brand-600 px-1.5 py-0.5 text-[10px] font-bold text-white">{{ unread_alert_count }}</span>{% endif %}
<a href="/alerts" class="relative inline-flex h-10 items-center justify-center rounded-2xl border border-slate-200 bg-white px-3 text-xs font-semibold text-slate-700 shadow-sm hover:border-brand-200 hover:text-brand-700">
Alerts
{% if unread_alert_count %}<span class="ml-2 rounded-full bg-brand-600 px-2 py-0.5 text-[10px] font-bold text-white">{{ unread_alert_count }}</span>{% endif %}
</a>
<details class="group relative hidden sm:block">
<summary class="flex cursor-pointer list-none items-center gap-2 rounded-xl border border-slate-200 bg-white px-2 py-1.5 hover:border-brand-200">
{% if current_user_photo_url %}<img src="{{ current_user_photo_url }}" alt="Profile photo" class="h-8 w-8 rounded-lg object-cover" />{% else %}<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-brand-600 text-xs font-bold text-white">{{ current_user_initials }}</div>{% endif %}
<div class="hidden text-left xl:block"><div class="max-w-[140px] truncate text-xs font-bold text-slate-900">{{ current_user.full_name or current_user.email }}</div><div class="max-w-[140px] truncate text-[10px] text-slate-500">{{ current_user.email }}</div></div>
<details class="group relative">
<summary class="flex cursor-pointer list-none items-center gap-2 rounded-2xl border border-slate-200 bg-white px-2 py-2 shadow-sm hover:border-brand-200">
{% if current_user_photo_url %}
<img src="{{ current_user_photo_url }}" alt="Profile photo" class="h-8 w-8 rounded-xl object-cover" />
{% else %}
<div class="flex h-8 w-8 items-center justify-center rounded-xl bg-brand-600 text-xs font-bold text-white">{{ current_user_initials }}</div>
{% endif %}
<div class="hidden text-left sm:block">
<div class="max-w-[150px] truncate text-xs font-bold text-slate-900">{{ current_user.full_name or current_user.email }}</div>
<div class="max-w-[150px] truncate text-[11px] text-slate-500">{{ current_user.email }}</div>
</div>
</summary>
<div class="absolute right-0 z-[70] mt-2 w-56 rounded-2xl border border-slate-200 bg-white p-2 text-sm shadow-card">
{% if "Consultant" in ui_roles %}<a class="block rounded-xl px-3 py-2 text-slate-700 hover:bg-slate-50" href="/consultant/profile">My Profile</a>{% elif "Client" in ui_roles %}<a class="block rounded-xl px-3 py-2 text-slate-700 hover:bg-slate-50" href="/client/profile">My Profile</a>{% else %}<a class="block rounded-xl px-3 py-2 text-slate-700 hover:bg-slate-50" href="/employee/profile">My Profile</a>{% endif %}
<div class="absolute right-0 z-50 mt-2 w-56 rounded-2xl border border-slate-200 bg-white p-2 text-sm shadow-card">
{% if "Consultant" in ui_roles %}
<a class="block rounded-xl px-3 py-2 text-slate-700 hover:bg-slate-50" href="/consultant/profile">My Profile</a>
{% elif "Client" in ui_roles %}
<a class="block rounded-xl px-3 py-2 text-slate-700 hover:bg-slate-50" href="/client/profile">My Profile</a>
{% else %}
<a class="block rounded-xl px-3 py-2 text-slate-700 hover:bg-slate-50" href="/employee/profile">My Profile</a>
{% endif %}
<a class="block rounded-xl px-3 py-2 text-slate-700 hover:bg-slate-50" href="/change-password">Change Password</a>
<a class="block rounded-xl px-3 py-2 text-red-600 hover:bg-red-50" href="/logout">Logout</a>
</div>
</details>
<button type="button" data-af-mobile-open aria-label="Open navigation" aria-controls="af-mobile-nav" class="inline-flex h-10 w-10 items-center justify-center rounded-xl border border-slate-200 bg-white text-xl font-bold text-slate-700 hover:border-brand-200 hover:text-brand-700 lg:hidden"></button>
</div>
</div>
<div class="lg:border-t lg:border-slate-200 lg:px-4 lg:py-2">
{% include "ui/templates/components/role_navigation.html" %}
</div>
</div>
</div>
</header>
@@ -3,15 +3,18 @@
{% set perms = ui_perms if ui_perms is defined else [] %}
{% set current = current_path if current_path is defined else request.url.path %}
{% set can_employee = ('Staff' in roles) or ('Employee' in roles) or ('employees.ess.view' in perms) or ('employees.work.view_self' in perms) or ('employees.attendance.view_self' in perms) or ('employees.leave.view_self' in perms) or ('employees.documents.view_self' in perms) or ('employees.payroll.view_self' in perms) %}
<label class="sr-only" for="workspace_switcher">Workspace</label>
<select id="workspace_switcher" title="Active Workspace" onchange="if(this.value){window.location.href=this.value;}" class="min-w-[190px] max-w-[220px] rounded-xl border border-brand-200 bg-brand-50 px-3 py-2 text-xs font-semibold text-brand-900 outline-none transition hover:bg-brand-100 focus:border-brand-400 focus:ring-2 focus:ring-brand-200">
<option value="/workspaces" {% if current.startswith('/workspaces') %}selected{% endif %}>Workspace Centre</option>
{% if 'System Admin' in roles %}<option value="/system-admin/dashboard" {% if current.startswith('/system-admin') %}selected{% endif %}>System Administration</option>{% endif %}
{% if 'Partner' in roles %}<option value="/partner/dashboard" {% if current.startswith('/partner') %}selected{% endif %}>Partner Workspace</option>{% endif %}
{% if 'Firm Admin' in roles %}<option value="/firm-admin/dashboard" {% if current.startswith('/firm-admin') %}selected{% endif %}>Firm Administration</option>{% endif %}
{% if 'Manager' in roles or 'Branch Manager' in roles %}<option value="/manager/dashboard" {% if current.startswith('/manager') %}selected{% endif %}>Manager Workspace</option>{% endif %}
{% if can_employee %}<option value="/employee/dashboard" {% if current.startswith('/employee') or current.startswith('/employees') %}selected{% endif %}>Employee Workspace</option>{% endif %}
{% if 'Client' in roles %}<option value="/client/dashboard" {% if current.startswith('/client') %}selected{% endif %}>Client Portal</option>{% endif %}
{% if 'Consultant' in roles %}<option value="/consultant/dashboard" {% if current.startswith('/consultant') %}selected{% endif %}>Consultant Workspace</option>{% endif %}
</select>
<div class="mt-2 flex flex-wrap justify-end gap-2">
<label class="sr-only" for="workspace_switcher">Workspace</label>
<select id="workspace_switcher" onchange="if(this.value){window.location.href=this.value;}" class="min-w-[230px] max-w-xs rounded-xl border border-brand-200 bg-brand-50 px-3 py-1.5 text-xs font-semibold text-brand-900 shadow-sm outline-none transition hover:bg-brand-100 focus:border-brand-400 focus:ring-2 focus:ring-brand-200">
<option value="/workspaces" {% if current.startswith('/workspaces') %}selected{% endif %}>Workspace Centre</option>
{% if 'System Admin' in roles %}<option value="/system-admin/dashboard" {% if current.startswith('/system-admin') %}selected{% endif %}>System Admin - Platform Control</option>{% endif %}
{% if 'Partner' in roles %}<option value="/partner/dashboard" {% if current.startswith('/partner') %}selected{% endif %}>Partner Operations</option>{% endif %}
{% if 'Firm Admin' in roles %}<option value="/firm-admin/dashboard" {% if current.startswith('/firm-admin') %}selected{% endif %}>Firm Administration</option>{% endif %}
{% if 'Manager' in roles or 'Branch Manager' in roles %}<option value="/manager/dashboard" {% if current.startswith('/manager') %}selected{% endif %}>Manager Workspace</option>{% endif %}
{% if can_employee %}<option value="/employee/dashboard" {% if current.startswith('/employee') or current.startswith('/employees') %}selected{% endif %}>Employee Portal</option>{% endif %}
{% if 'Client' in roles %}<option value="/client/dashboard" {% if current.startswith('/client') %}selected{% endif %}>Client Portal</option>{% endif %}
{% if 'Consultant' in roles %}<option value="/consultant/dashboard" {% if current.startswith('/consultant') %}selected{% endif %}>Consultant Workspace</option>{% endif %}
</select>
<a href="/workspaces" class="inline-flex items-center rounded-xl border border-slate-200 bg-white px-3 py-1.5 text-xs font-semibold text-slate-600 shadow-sm hover:border-brand-200 hover:text-brand-700">All workspaces</a>
</div>
{% endif %}