Add permanent responsive role navigation and breadcrumbs

This commit is contained in:
A R R R Associates
2026-07-10 10:55:51 +05:30
parent b7c772f6e1
commit 0e00141ded
11 changed files with 364 additions and 148 deletions
@@ -1,12 +1 @@
<div class="mb-6 overflow-x-auto rounded-2xl border border-slate-200 bg-white p-2 shadow-soft"> {# Replaced by the permanent permission-aware navigation in base/layout.html. #}
<div class="flex min-w-max items-center gap-2">
{% set path = request.url.path %}
<a href="/client/dashboard" class="rounded-xl px-4 py-2 text-sm font-semibold transition {{ 'bg-brand-600 text-white' if path == '/client/dashboard' else 'text-slate-700 hover:bg-slate-100' }}">Overview</a>
<a href="/client/compliance" class="rounded-xl px-4 py-2 text-sm font-semibold transition {{ 'bg-brand-600 text-white' if path.startswith('/client/compliance') or path.startswith('/client/engagements') else 'text-slate-700 hover:bg-slate-100' }}">My Compliance</a>
<a href="/client/documents" class="rounded-xl px-4 py-2 text-sm font-semibold transition {{ 'bg-brand-600 text-white' if path.startswith('/client/documents') else 'text-slate-700 hover:bg-slate-100' }}">My Documents</a>
<a href="/client/messages" class="rounded-xl px-4 py-2 text-sm font-semibold transition {{ 'bg-brand-600 text-white' if path.startswith('/client/messages') else 'text-slate-700 hover:bg-slate-100' }}">My Messages</a>
<a href="/client/billing" class="rounded-xl px-4 py-2 text-sm font-semibold transition {{ 'bg-brand-600 text-white' if path.startswith('/client/billing') else 'text-slate-700 hover:bg-slate-100' }}">My Bills</a>
<a href="/client/profile" class="rounded-xl px-4 py-2 text-sm font-semibold transition {{ 'bg-brand-600 text-white' if path.startswith('/client/profile') else 'text-slate-700 hover:bg-slate-100' }}">My Profile</a>
<a href="/alerts" class="rounded-xl px-4 py-2 text-sm font-semibold transition {{ 'bg-brand-600 text-white' if path == '/alerts' else 'text-slate-700 hover:bg-slate-100' }}">My Alert</a>
</div>
</div>
@@ -1,14 +1 @@
{% set path = request.url.path %} {# Replaced by the permanent permission-aware navigation in base/layout.html. #}
<div class="mb-5 overflow-x-auto rounded-2xl border border-slate-200 bg-white p-2 shadow-soft">
<nav class="flex min-w-max gap-2 text-sm font-semibold">
<a href="/consultant/dashboard" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path == '/consultant/dashboard' else 'text-slate-700 hover:bg-slate-100' }}">Overview</a>
<a href="/consultant/work" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path.startswith('/consultant/work') or path.startswith('/consultant/assignments') else 'text-slate-700 hover:bg-slate-100' }}">My Work Board</a>
<a href="/consultant/communications" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path.startswith('/consultant/communications') else 'text-slate-700 hover:bg-slate-100' }}">My Messages</a>
<a href="/consultant/documents" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path.startswith('/consultant/documents') else 'text-slate-700 hover:bg-slate-100' }}">Shared Documents</a>
<a href="/consultant/service-requests" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path.startswith('/consultant/service-requests') else 'text-slate-700 hover:bg-slate-100' }}">Service Requests</a>
<a href="/consultant/managed-clients" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path.startswith('/consultant/managed-clients') else 'text-slate-700 hover:bg-slate-100' }}">Managed Clients</a>
<a href="/consultant/workspace" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path.startswith('/consultant/workspace') else 'text-slate-700 hover:bg-slate-100' }}">Workspace</a>
<a href="/consultant/profile" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path.startswith('/consultant/profile') else 'text-slate-700 hover:bg-slate-100' }}">My Profile</a>
<a href="/alerts" class="rounded-xl px-3 py-2 {{ 'bg-slate-900 text-white' if path.startswith('/alerts') else 'text-slate-700 hover:bg-slate-100' }}">My Alert</a>
</nav>
</div>
@@ -1,38 +1 @@
{% set current_path = request.url.path %} {# Replaced by the permanent permission-aware navigation in base/layout.html. #}
{% set _tab_base = "inline-flex shrink-0 items-center rounded-xl px-3 py-2 text-sm font-semibold whitespace-nowrap transition" %}
{% set _tab_active = "bg-brand-600 text-white shadow-soft" %}
{% set _tab_idle = "border border-slate-300 bg-white text-slate-700 hover:bg-slate-50" %}
<div class="rounded-2xl border border-slate-200 bg-white p-3 shadow-soft">
<div class="mb-2 px-1 text-xs font-semibold uppercase tracking-wide text-slate-500">My Workspace</div>
<div class="overflow-x-auto pb-1">
<div class="flex min-w-max flex-nowrap gap-2">
{% if can_view_employee_portal(current_user, current_user_permissions, current_user_roles) %}
<a href="/employee/dashboard" class="{{ _tab_base }} {{ _tab_active if current_path == '/employee/dashboard' else _tab_idle }}">Overview</a>
{% endif %}
{% if can_view_own_employee_work(current_user, current_user_permissions, current_user_roles) %}
<a href="/employee/work" class="{{ _tab_base }} {{ _tab_active if current_path.startswith('/employee/work') else _tab_idle }}">My Work Board</a>
{% endif %}
{% if can_view_own_employee_attendance(current_user, current_user_permissions, current_user_roles) %}
<a href="/employee/attendance" class="{{ _tab_base }} {{ _tab_active if current_path.startswith('/employee/attendance') else _tab_idle }}">My Attendance</a>
{% endif %}
{% if can_view_employee_portal(current_user, current_user_permissions, current_user_roles) %}
<a href="/employee/profile" class="{{ _tab_base }} {{ _tab_active if current_path.startswith('/employee/profile') else _tab_idle }}">My Profile</a>
{% endif %}
{% if can_view_own_employee_leave(current_user, current_user_permissions, current_user_roles) %}
<a href="/employee/leave" class="{{ _tab_base }} {{ _tab_active if current_path.startswith('/employee/leave') else _tab_idle }}">My Leave</a>
{% endif %}
{% if can_view_own_employee_documents(current_user, current_user_permissions, current_user_roles) %}
<a href="/employee/documents" class="{{ _tab_base }} {{ _tab_active if current_path.startswith('/employee/documents') else _tab_idle }}">My Documents</a>
{% endif %}
{% if can_view_own_employee_payslips(current_user, current_user_permissions, current_user_roles) %}
<a href="/employee/payslips" class="{{ _tab_base }} {{ _tab_active if current_path.startswith('/employee/payslips') else _tab_idle }}">My Payslips</a>
{% endif %}
{% if can_request_own_employee_offboarding(current_user, current_user_permissions, current_user_roles) %}
<a href="/employee/offboarding" class="{{ _tab_base }} {{ _tab_active if current_path.startswith('/employee/offboarding') else _tab_idle }}">My Offboarding</a>
{% endif %}
<a href="/alerts" class="{{ _tab_base }} {{ _tab_active if current_path.startswith('/alerts') else _tab_idle }}">
My Alert{% if unread_alert_count is defined and unread_alert_count > 0 %}<span class="ml-2 rounded-full bg-white/20 px-2 py-0.5 text-[10px]">{{ unread_alert_count }}</span>{% endif %}
</a>
</div>
</div>
</div>
@@ -1,12 +1 @@
{% set manager_path = request.url.path %} {# Replaced by the permanent permission-aware navigation in base/layout.html. #}
<div class="mb-6 overflow-x-auto rounded-2xl border border-slate-200 bg-white p-2 shadow-soft">
<div class="flex min-w-max items-center gap-2">
<a href="/manager/dashboard" class="rounded-xl px-4 py-2 text-sm font-semibold transition {% if manager_path == '/manager/dashboard' %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">Overview</a>
<a href="/manager/work" class="rounded-xl px-4 py-2 text-sm font-semibold transition {% if manager_path.startswith('/manager/work') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">Team Work Board</a>
<a href="/employees/work" class="rounded-xl px-4 py-2 text-sm font-semibold transition {% if manager_path.startswith('/employees/work') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">Detailed Allocation</a>
<a href="/employees/progress" class="rounded-xl px-4 py-2 text-sm font-semibold transition {% if manager_path.startswith('/employees/progress') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">Engagement Progress</a>
<a href="/employees/attendance" class="rounded-xl px-4 py-2 text-sm font-semibold transition {% if manager_path.startswith('/employees/attendance') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">Team Attendance</a>
<a href="/employees/leave" class="rounded-xl px-4 py-2 text-sm font-semibold transition {% if manager_path.startswith('/employees/leave') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">Team Leave</a>
<a href="/alerts" class="rounded-xl px-4 py-2 text-sm font-semibold transition {% if manager_path.startswith('/alerts') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">My Alert</a>
</div>
</div>
@@ -1,12 +1 @@
{% set path = request.url.path %} {# Replaced by the permanent permission-aware navigation in base/layout.html. #}
<div class="mb-6 overflow-x-auto rounded-2xl border border-slate-200 bg-white p-2 shadow-soft">
<div class="flex min-w-max gap-2 text-sm font-medium">
<a href="/partner/dashboard" class="rounded-xl px-4 py-2 transition {% if path == '/partner/dashboard' %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">Overview</a>
<a href="/partner/reviews" class="rounded-xl px-4 py-2 transition {% if path.startswith('/partner/reviews') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">Review Board</a>
<a href="/partner/clients" class="rounded-xl px-4 py-2 transition {% if path.startswith('/partner/clients') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">My Clients</a>
<a href="/services/engagements" class="rounded-xl px-4 py-2 text-slate-700 transition hover:bg-slate-100">Engagements</a>
<a href="/documents" class="rounded-xl px-4 py-2 text-slate-700 transition hover:bg-slate-100">Documents</a>
<a href="/billing" class="rounded-xl px-4 py-2 text-slate-700 transition hover:bg-slate-100">Billing</a>
<a href="/alerts" class="rounded-xl px-4 py-2 transition {% if path.startswith('/alerts') %}bg-brand-600 text-white{% else %}text-slate-700 hover:bg-slate-100{% endif %}">My Alert</a>
</div>
</div>
+98
View File
@@ -0,0 +1,98 @@
(function () {
'use strict';
function textOf(el) {
return (el.textContent || '').replace(/\s+/g, ' ').trim();
}
function prepareMobileNavigation() {
var desktop = document.querySelector('nav[aria-label="Primary navigation"]');
var drawer = document.getElementById('af-mobile-nav');
var target = drawer && drawer.querySelector('[data-af-mobile-links]');
var open = document.querySelector('[data-af-mobile-open]');
if (!desktop || !drawer || !target || !open) return;
var clone = desktop.cloneNode(true);
clone.className = 'block';
clone.removeAttribute('aria-label');
clone.querySelectorAll('.af-nav-menu').forEach(function (menu) {
menu.classList.remove('af-nav-menu');
menu.classList.add('ml-3', 'mt-1', 'space-y-1', 'border-l', 'border-slate-200', 'pl-3');
});
clone.querySelectorAll('.af-nav-dropdown').forEach(function (item) {
item.classList.add('rounded-xl', 'border', 'border-slate-200', 'p-1');
});
clone.querySelectorAll('.af-nav-link, .af-nav-summary').forEach(function (item) {
item.classList.add('block', 'w-full');
});
target.appendChild(clone);
function show() {
drawer.classList.remove('hidden');
drawer.setAttribute('aria-hidden', 'false');
document.documentElement.classList.add('overflow-hidden');
}
function hide() {
drawer.classList.add('hidden');
drawer.setAttribute('aria-hidden', 'true');
document.documentElement.classList.remove('overflow-hidden');
}
open.addEventListener('click', show);
drawer.querySelectorAll('[data-af-mobile-close]').forEach(function (button) { button.addEventListener('click', hide); });
drawer.querySelectorAll('a').forEach(function (link) { link.addEventListener('click', hide); });
document.addEventListener('keydown', function (event) { if (event.key === 'Escape') hide(); });
}
function normalizeBackAction() {
var slot = document.getElementById('af-shared-back-slot');
var main = document.querySelector('main');
if (!slot || !main) return;
var candidates = Array.prototype.slice.call(main.querySelectorAll('a, button'));
var existing = candidates.find(function (el) {
if (el.closest('#af-page-context')) return false;
var text = textOf(el).toLowerCase();
return text === 'back' || text.indexOf('back to ') === 0 || text.indexOf('← back') === 0;
});
if (existing) {
existing.className = 'inline-flex items-center rounded-xl border border-slate-300 bg-white px-3 py-2 text-xs font-semibold text-slate-700 shadow-sm transition hover:border-brand-300 hover:text-brand-700';
if (textOf(existing).indexOf('←') !== 0) existing.textContent = '← ' + textOf(existing);
slot.appendChild(existing);
return;
}
var path = window.location.pathname.replace(/\/$/, '');
var segments = path.split('/').filter(Boolean);
var isRoot = segments.length <= 1 || /\/(dashboard|workspaces)$/.test(path);
if (isRoot) return;
var referrerIsLocal = false;
try { referrerIsLocal = !!document.referrer && new URL(document.referrer).origin === window.location.origin; } catch (e) {}
if (!referrerIsLocal && window.history.length <= 1) return;
var button = document.createElement('button');
button.type = 'button';
button.className = 'inline-flex items-center rounded-xl border border-slate-300 bg-white px-3 py-2 text-xs font-semibold text-slate-700 shadow-sm transition hover:border-brand-300 hover:text-brand-700';
button.textContent = '← Back';
button.addEventListener('click', function () {
if (referrerIsLocal || window.history.length > 1) window.history.back();
else window.location.href = '/workspaces';
});
slot.appendChild(button);
}
function closeDesktopDropdowns() {
document.addEventListener('click', function (event) {
document.querySelectorAll('[data-af-nav-dropdown][open]').forEach(function (item) {
if (!item.contains(event.target)) item.removeAttribute('open');
});
});
}
document.addEventListener('DOMContentLoaded', function () {
prepareMobileNavigation();
normalizeBackAction();
closeDesktopDropdowns();
});
})();
+15 -1
View File
@@ -83,6 +83,18 @@
--af-color-brand-900: {{ __firm_branding.accent_color or '#172554' }}; --af-color-brand-900: {{ __firm_branding.accent_color or '#172554' }};
} }
</style> </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> </head>
<body class="min-h-screen bg-slate-100 text-slate-900"> <body class="min-h-screen bg-slate-100 text-slate-900">
{% set otp_ok = request.session.get("otp_verified", False) %} {% set otp_ok = request.session.get("otp_verified", False) %}
@@ -111,7 +123,7 @@
Sidebar is retained only for System Admin users. Sidebar is retained only for System Admin users.
All other authenticated users use the floating top workspace bar on every page. All other authenticated users use the floating top workspace bar on every page.
Existing routes and old sidebar code are kept intact. #} Existing routes and old sidebar code are kept intact. #}
{% set use_clean_top_nav = full_auth and not is_system_admin_user %} {% set use_clean_top_nav = full_auth %}
<div class="min-h-screen {% if not use_clean_top_nav %}lg:grid lg:grid-cols-[260px_minmax(0,1fr)]{% endif %}"> <div class="min-h-screen {% if not use_clean_top_nav %}lg:grid lg:grid-cols-[260px_minmax(0,1fr)]{% endif %}">
@@ -503,6 +515,7 @@
{% endif %} {% endif %}
<main class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8"> <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 %} {% 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"> <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 }} {{ flash }}
@@ -676,5 +689,6 @@
})(); })();
</script> </script>
<script src="/static/js/navigation_shell.js?v=uiux-navigation-v1"></script>
</body> </body>
</html> </html>
@@ -0,0 +1,21 @@
{% 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>
@@ -0,0 +1,182 @@
{% 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,85 +1,72 @@
<header class="sticky top-0 z-40 border-b border-slate-200/70 bg-slate-100/85 backdrop-blur-xl"> <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-[1500px] px-4 py-3 sm:px-6 lg:px-8"> <div class="mx-auto max-w-[1600px] px-3 py-2 sm:px-5 lg:px-8">
<div class="rounded-3xl border border-slate-200 bg-white/95 px-4 py-3 shadow-soft"> <div class="rounded-2xl border border-slate-200 bg-white/95 shadow-soft">
<div class="flex flex-wrap items-center gap-3"> <div class="flex items-center gap-3 px-3 py-2.5 sm:px-4">
<a href="/workspaces" class="flex min-w-[220px] items-center gap-3 rounded-2xl px-2 py-1 transition hover:bg-slate-50"> <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 firm_branding.logo_url %} {% 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" /> <img src="{{ firm_branding.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" />
{% else %} {% else %}
<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> <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>
{% endif %} {% endif %}
<div class="min-w-0"> <div class="hidden min-w-0 sm:block">
<div class="truncate text-sm font-bold text-slate-900">{{ current_firm_name }}</div> <div class="max-w-[220px] 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 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> </div>
</a> </a>
<div class="flex min-w-0 flex-1 flex-wrap items-center gap-2"> <div class="hidden min-w-0 flex-1 items-center gap-2 md:flex">
{% include "ui/templates/components/workspace_switcher.html" %} {% 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 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) %} {% 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) %} {% set context_tenants = get_context_tenants(request, current_user, ui_perms, ui_roles) %}
<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"> <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 %} {% 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 %}
<option value="/system-settings/context/tenant/{{ tenant.id }}" {% if tenant.id == active_tenant_id %}selected{% endif %}>{{ tenant.name }}</option>
{% endfor %}
</select> </select>
{% endif %} {% endif %}
{% if can_switch_service_branch(current_user, ui_perms, ui_roles) or can_switch_employee_branch(current_user, ui_perms, ui_roles) %} {% 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) %} {% set context_branches = get_context_branches(request, current_user, ui_perms, ui_roles) %}
<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"> <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">
<option value="/system-settings/context/branch/0" {% if not active_branch_id %}selected{% endif %}>All Branches</option> <option value="/system-settings/context/branch/0" {% if not active_branch_id %}selected{% endif %}>All Branches</option>
{% for branch in context_branches %} {% 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 %}
<option value="/system-settings/context/branch/{{ branch.id }}" {% if branch.id == active_branch_id %}selected{% endif %}>{{ branch.name }}</option>
{% endfor %}
</select> </select>
{% endif %} {% endif %}
{% if can_view_settings(current_user, ui_perms, ui_roles) %} {% 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) %} {% 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-[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"> <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 %} {% 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 %}
<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> </select>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
<div class="ml-auto flex items-center gap-2"> <div class="ml-auto flex items-center gap-2">
<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"> <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">
Alerts <span class="hidden sm:inline">Alerts</span><span class="sm:hidden" aria-hidden="true">🔔</span>
{% 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 %} {% 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> </a>
<details class="group relative"> <details class="group relative hidden sm:block">
<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"> <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 %} {% 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 %}
<img src="{{ current_user_photo_url }}" alt="Profile photo" class="h-8 w-8 rounded-xl object-cover" /> <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>
{% 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> </summary>
<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"> <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 %} {% 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="/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-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> <a class="block rounded-xl px-3 py-2 text-red-600 hover:bg-red-50" href="/logout">Logout</a>
</div> </div>
</details> </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> </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>
</div> </div>
</header> </header>
@@ -3,18 +3,15 @@
{% set perms = ui_perms if ui_perms is defined else [] %} {% set perms = ui_perms if ui_perms is defined else [] %}
{% set current = current_path if current_path is defined else request.url.path %} {% 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) %} {% 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) %}
<div class="mt-2 flex flex-wrap justify-end gap-2">
<label class="sr-only" for="workspace_switcher">Workspace</label> <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"> <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> <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 '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 Operations</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 '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 '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 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 '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 %} {% if 'Consultant' in roles %}<option value="/consultant/dashboard" {% if current.startswith('/consultant') %}selected{% endif %}>Consultant Workspace</option>{% endif %}
</select> </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 %} {% endif %}