big refactor
This commit is contained in:
@@ -1,10 +1,115 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const { saved: branding, sidebarTitle } = useBrokerageBranding()
|
||||
const { isSuperAdmin } = useSuperAdmin()
|
||||
useAppTheme()
|
||||
const { sidebarCollapsed, toggleSidebar } = useAppShellLayout()
|
||||
const sidebarFeatures = useSidebarFeatures()
|
||||
|
||||
const STORAGE_KEY_BRANDING = 'policy-ui.branding'
|
||||
const STORAGE_KEY_SUPERADMIN = 'policy-ui.superadmin'
|
||||
const STORAGE_KEY_SIDEBAR_WORKSTATIONS = 'policy-ui.sidebar.workstations'
|
||||
const STORAGE_KEY_SIDEBAR_AI_TOOLS = 'policy-ui.sidebar.ai-tools'
|
||||
const STORAGE_KEY_SIDEBAR_LEADS_HUB = 'policy-ui.sidebar.leads-hub'
|
||||
const STORAGE_KEY_SIDEBAR_COLLAPSED = 'policy-ui.sidebar.collapsed'
|
||||
|
||||
interface BrokerageBrandingState {
|
||||
companyName: string
|
||||
logoDataUrl: string | null
|
||||
logoFileName: string
|
||||
reportPageHeader: string
|
||||
reportPageFooter: string
|
||||
}
|
||||
|
||||
function loadBranding(): BrokerageBrandingState {
|
||||
if (import.meta.client) {
|
||||
const stored = localStorage.getItem(STORAGE_KEY_BRANDING)
|
||||
if (stored) {
|
||||
try {
|
||||
return JSON.parse(stored)
|
||||
} catch {
|
||||
return defaultBranding()
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultBranding()
|
||||
}
|
||||
|
||||
function defaultBranding(): BrokerageBrandingState {
|
||||
return {
|
||||
companyName: '',
|
||||
logoDataUrl: null,
|
||||
logoFileName: '',
|
||||
reportPageHeader: '',
|
||||
reportPageFooter: ''
|
||||
}
|
||||
}
|
||||
|
||||
const branding = ref<BrokerageBrandingState>(loadBranding())
|
||||
const sidebarTitle = computed(() => branding.value.companyName || 'Segur-OS')
|
||||
|
||||
const isSuperAdmin = computed(() => {
|
||||
if (import.meta.client) {
|
||||
const stored = localStorage.getItem(STORAGE_KEY_SUPERADMIN)
|
||||
return stored !== '0'
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
const sidebarCollapsed = computed({
|
||||
get: () => {
|
||||
if (import.meta.client) {
|
||||
return localStorage.getItem(STORAGE_KEY_SIDEBAR_COLLAPSED) === 'true'
|
||||
}
|
||||
return false
|
||||
},
|
||||
set: (value: boolean) => {
|
||||
if (import.meta.client) {
|
||||
localStorage.setItem(STORAGE_KEY_SIDEBAR_COLLAPSED, String(value))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function toggleSidebar() {
|
||||
sidebarCollapsed.value = !sidebarCollapsed.value
|
||||
}
|
||||
|
||||
const sidebarFeatures = reactive({
|
||||
showWorkstations: computed({
|
||||
get: () => {
|
||||
if (import.meta.client) {
|
||||
return localStorage.getItem(STORAGE_KEY_SIDEBAR_WORKSTATIONS) !== 'false'
|
||||
}
|
||||
return true
|
||||
},
|
||||
set: (value: boolean) => {
|
||||
if (import.meta.client) {
|
||||
localStorage.setItem(STORAGE_KEY_SIDEBAR_WORKSTATIONS, String(value))
|
||||
}
|
||||
}
|
||||
}),
|
||||
showAiTools: computed({
|
||||
get: () => {
|
||||
if (import.meta.client) {
|
||||
return localStorage.getItem(STORAGE_KEY_SIDEBAR_AI_TOOLS) !== 'false'
|
||||
}
|
||||
return true
|
||||
},
|
||||
set: (value: boolean) => {
|
||||
if (import.meta.client) {
|
||||
localStorage.setItem(STORAGE_KEY_SIDEBAR_AI_TOOLS, String(value))
|
||||
}
|
||||
}
|
||||
}),
|
||||
showLeadsHub: computed({
|
||||
get: () => {
|
||||
if (import.meta.client) {
|
||||
return localStorage.getItem(STORAGE_KEY_SIDEBAR_LEADS_HUB) !== 'false'
|
||||
}
|
||||
return true
|
||||
},
|
||||
set: (value: boolean) => {
|
||||
if (import.meta.client) {
|
||||
localStorage.setItem(STORAGE_KEY_SIDEBAR_LEADS_HUB, String(value))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const openGroups = ref({
|
||||
quotes: false,
|
||||
@@ -12,10 +117,10 @@ const openGroups = ref({
|
||||
cartera: false,
|
||||
customerService: false,
|
||||
workstation: false,
|
||||
aiTools: false
|
||||
aiTools: false,
|
||||
backOffice: false
|
||||
})
|
||||
|
||||
// Auto-open the group matching the current route (but never close others)
|
||||
watch(() => route.path, (p) => {
|
||||
if (p.startsWith('/quotes') && p !== '/quotes/new' && p !== '/quotes/compare') openGroups.value.quotes = true
|
||||
if (p.startsWith('/onboarding') || (p.startsWith('/sales') && !p.startsWith('/sales/leads')) || p === '/quotes/new' || p === '/quotes/compare' || p.startsWith('/registration')) openGroups.value.sales = true
|
||||
@@ -23,6 +128,7 @@ watch(() => route.path, (p) => {
|
||||
if (p.startsWith('/support') || p.startsWith('/claims') || p.startsWith('/collections') || p.startsWith('/renewals') || p.startsWith('/sales/leads')) openGroups.value.customerService = true
|
||||
if (p.startsWith('/workstation')) openGroups.value.workstation = true
|
||||
if (p.startsWith('/ai-tools')) openGroups.value.aiTools = true
|
||||
if (p.startsWith('/back-office')) openGroups.value.backOffice = true
|
||||
}, { immediate: true })
|
||||
|
||||
function toggleGroup(key: string) {
|
||||
@@ -34,7 +140,6 @@ function isActive(path: string, exact = false) {
|
||||
return exact ? p === path : p === path || p.startsWith(`${path}/`)
|
||||
}
|
||||
|
||||
/* ── Parent link class (40px row, 20px icon, 10px gap) ── */
|
||||
function linkClass(path: string, exact = false) {
|
||||
const active = isActive(path, exact)
|
||||
return [
|
||||
@@ -45,7 +150,6 @@ function linkClass(path: string, exact = false) {
|
||||
]
|
||||
}
|
||||
|
||||
/* ── Child link class (text only, 32px row, 13px, indented) ── */
|
||||
function subLinkClass(path: string, exact = false) {
|
||||
const active = isActive(path, exact)
|
||||
return [
|
||||
@@ -73,7 +177,8 @@ function groupBtnClass(key: string) {
|
||||
route.path.startsWith('/renewals') ||
|
||||
route.path.startsWith('/sales/leads'))) ||
|
||||
(key === 'workstation' && route.path.startsWith('/workstation')) ||
|
||||
(key === 'aiTools' && route.path.startsWith('/ai-tools'))
|
||||
(key === 'aiTools' && route.path.startsWith('/ai-tools')) ||
|
||||
(key === 'backOffice' && route.path.startsWith('/back-office'))
|
||||
return [
|
||||
'app-sidebar-link sidebar-parent-link flex w-full items-center text-left',
|
||||
hasActive
|
||||
@@ -90,7 +195,6 @@ async function onTopRefresh() {
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard shortcut: Ctrl/Cmd + B to toggle sidebar
|
||||
function onKeydown(e: KeyboardEvent) {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'b') {
|
||||
e.preventDefault()
|
||||
@@ -150,12 +254,10 @@ onUnmounted(() => {
|
||||
/>
|
||||
</button>
|
||||
<div v-if="openGroups.quotes" class="sidebar-children">
|
||||
<NuxtLink to="/quotes" active-class="" exact-active-class="" :class="subLinkClass('/quotes', true)">Mission Control</NuxtLink>
|
||||
<NuxtLink to="/quotes/auto" active-class="" exact-active-class="" :class="subLinkClass('/quotes/auto')">Auto</NuxtLink>
|
||||
<NuxtLink to="/quotes/health" active-class="" exact-active-class="" :class="subLinkClass('/quotes/health')">Health</NuxtLink>
|
||||
<NuxtLink to="/quotes/life" active-class="" exact-active-class="" :class="subLinkClass('/quotes/life')">Life</NuxtLink>
|
||||
<NuxtLink to="/quotes/general-risk" active-class="" exact-active-class="" :class="subLinkClass('/quotes/general-risk')">General Risk</NuxtLink>
|
||||
<NuxtLink to="/quotes/custom" active-class="" exact-active-class="" :class="subLinkClass('/quotes/custom')">Custom</NuxtLink>
|
||||
<NuxtLink to="/quotes/new?tab=car" :class="subLinkClass('/quotes/new', true)">Auto</NuxtLink>
|
||||
<NuxtLink to="/quotes/new?tab=life" :class="subLinkClass('/quotes/new')">Life</NuxtLink>
|
||||
<NuxtLink to="/quotes/new?tab=fire_structure" :class="subLinkClass('/quotes/new')">Fire Structure</NuxtLink>
|
||||
<NuxtLink to="/quotes/new?tab=fire_contents" :class="subLinkClass('/quotes/new')">Fire Contents</NuxtLink>
|
||||
</div>
|
||||
|
||||
<button type="button" :class="groupBtnClass('sales')" @click="toggleGroup('sales')">
|
||||
@@ -169,12 +271,8 @@ onUnmounted(() => {
|
||||
<div v-if="openGroups.sales" class="sidebar-children">
|
||||
<NuxtLink to="/onboarding" :class="subLinkClass('/onboarding', true)">Sales Pipeline</NuxtLink>
|
||||
<NuxtLink to="/sales/quick-lead" :class="subLinkClass('/sales/quick-lead')">Quick Lead</NuxtLink>
|
||||
<NuxtLink to="/registration/client" :class="subLinkClass('/registration/client', true)">New Customer</NuxtLink>
|
||||
<NuxtLink to="/quotes/new" :class="subLinkClass('/quotes/new', true)">Get Quotes</NuxtLink>
|
||||
<NuxtLink to="/quotes/compare" :class="subLinkClass('/quotes/compare', true)">Present Quotes</NuxtLink>
|
||||
<NuxtLink to="/onboarding/solicitud" :class="subLinkClass('/onboarding/solicitud', true)">Solicitudes</NuxtLink>
|
||||
<NuxtLink to="/onboarding/emissions" :class="subLinkClass('/onboarding/emissions')">Emissions</NuxtLink>
|
||||
<NuxtLink to="/onboarding/policy-upload/new" :class="subLinkClass('/onboarding/policy-upload')">Nombramiento</NuxtLink>
|
||||
<NuxtLink to="/customers/new" :class="subLinkClass('/customers/new', true)">New Customer</NuxtLink>
|
||||
<NuxtLink to="/quotes/new" :class="subLinkClass('/quotes/new', true)">New Quote</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -217,9 +315,27 @@ onUnmounted(() => {
|
||||
<UIcon name="i-heroicons-chart-bar-square" class="sidebar-icon shrink-0" />
|
||||
Reports & Analysis
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── WORKSTATION section ── -->
|
||||
<!-- ── BACK OFFICE section ── -->
|
||||
<p class="app-sidebar-section-label">Back Office</p>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<button type="button" :class="groupBtnClass('backOffice')" @click="toggleGroup('backOffice')">
|
||||
<UIcon name="i-heroicons-inbox-stack" class="sidebar-icon shrink-0" />
|
||||
<span class="flex-1 text-left">Back Office</span>
|
||||
<UIcon
|
||||
:name="openGroups.backOffice ? 'i-heroicons-chevron-down' : 'i-heroicons-chevron-right'"
|
||||
class="sidebar-chevron shrink-0"
|
||||
/>
|
||||
</button>
|
||||
<div v-if="openGroups.backOffice" class="sidebar-children">
|
||||
<NuxtLink to="/back-office/workload" :class="subLinkClass('/back-office/workload', true)">Task List</NuxtLink>
|
||||
<NuxtLink to="/back-office/workload/kanban" :class="subLinkClass('/back-office/workload/kanban')">Kanban Board</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── WORKSTATION section ── -->
|
||||
<template v-if="sidebarFeatures.showWorkstations || sidebarFeatures.showAiTools">
|
||||
<p class="app-sidebar-section-label">Workstations</p>
|
||||
|
||||
@@ -291,7 +407,7 @@ onUnmounted(() => {
|
||||
class="flex min-h-0 min-w-0 flex-1 flex-col"
|
||||
:data-app-surface="isSettingsRoute ? 'settings' : undefined"
|
||||
>
|
||||
<div class="flex-1 overflow-y-auto" style="padding: 16px 24px 32px;">
|
||||
<div class="flex-1 overflow-y-auto flex flex-col" style="padding: 16px 24px 32px;">
|
||||
<NuxtPage :key="route.fullPath" />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user