WIP jordan

This commit is contained in:
Jordan Weingarten
2026-04-16 11:11:44 -05:00
parent ff2d7b18b5
commit 67482f6629
163 changed files with 50627 additions and 728 deletions

View File

@@ -0,0 +1,205 @@
import { computed } from 'vue'
import { useLocalStorageRef } from '~/utils/useLocalStorageRef'
/* ── Types ── */
export type ProfileRole = 'sales' | 'claims' | 'renewals' | 'general_service' | 'management' | 'superadmin'
export interface ProfileSection {
id: string
label: string
visible: boolean
order: number
}
export interface ProfileLayout {
id: string
role: ProfileRole | string
name: string
description: string
icon: string
sections: ProfileSection[]
defaultTab: 'policies' | 'claims' | 'payments' | 'activity' | 'history' | 'relationships' | 'notes'
isCustom: boolean
}
/* ── Section catalog ── */
const ALL_SECTION_IDS = [
'orientation',
'kpi_strip',
'quick_policies',
'service_actions',
'personal_details',
'tabbed_content',
'documents',
] as const
const SECTION_LABELS: Record<string, string> = {
orientation: 'Account Orientation',
kpi_strip: 'KPI Strip',
quick_policies: 'Quick Policies',
service_actions: 'Service Actions',
personal_details: 'Personal Details',
tabbed_content: 'Tabbed Content',
documents: 'Documents',
}
function makeSections(order: string[], hidden: string[] = []): ProfileSection[] {
return order.map((id, i) => ({
id,
label: SECTION_LABELS[id] ?? id,
visible: !hidden.includes(id),
order: i,
}))
}
/* ── Built-in layouts ── */
function defaultLayouts(): ProfileLayout[] {
return [
{
id: 'sales',
role: 'sales',
name: 'Sales',
description: 'Focus on policies, quotes, and pipeline.',
icon: 'i-heroicons-currency-dollar',
sections: makeSections([
'orientation', 'quick_policies', 'kpi_strip', 'tabbed_content',
'service_actions', 'personal_details', 'documents',
]),
defaultTab: 'policies',
isCustom: false,
},
{
id: 'claims',
role: 'claims',
name: 'Claims',
description: 'Focus on claims and service actions.',
icon: 'i-heroicons-shield-exclamation',
sections: makeSections([
'service_actions', 'orientation', 'kpi_strip', 'tabbed_content',
'quick_policies', 'personal_details', 'documents',
]),
defaultTab: 'claims',
isCustom: false,
},
{
id: 'renewals',
role: 'renewals',
name: 'Renewals',
description: 'Focus on upcoming events and policies.',
icon: 'i-heroicons-arrow-path',
sections: makeSections([
'orientation', 'quick_policies', 'kpi_strip', 'tabbed_content',
'service_actions', 'personal_details', 'documents',
]),
defaultTab: 'policies',
isCustom: false,
},
{
id: 'general_service',
role: 'general_service',
name: 'General Service',
description: 'Balanced default for service representatives.',
icon: 'i-heroicons-lifebuoy',
sections: makeSections([
'orientation', 'kpi_strip', 'quick_policies', 'service_actions',
'personal_details', 'tabbed_content', 'documents',
]),
defaultTab: 'policies',
isCustom: false,
},
{
id: 'management',
role: 'management',
name: 'Management',
description: 'KPIs first, everything visible.',
icon: 'i-heroicons-chart-bar',
sections: makeSections([
'kpi_strip', 'orientation', 'service_actions', 'quick_policies',
'tabbed_content', 'personal_details', 'documents',
]),
defaultTab: 'history',
isCustom: false,
},
{
id: 'superadmin',
role: 'superadmin',
name: 'Superadmin',
description: 'Everything visible, history focus.',
icon: 'i-heroicons-cog-8-tooth',
sections: makeSections([
'kpi_strip', 'orientation', 'service_actions', 'quick_policies',
'tabbed_content', 'personal_details', 'documents',
]),
defaultTab: 'history',
isCustom: false,
},
]
}
const LAYOUTS_KEY = 'policy-ui-profile-layouts-v1'
const ACTIVE_KEY = 'policy-ui-active-profile-layout-v1'
/* ── Composable ── */
export function useProfileLayouts() {
const layouts = useLocalStorageRef<ProfileLayout[]>(LAYOUTS_KEY, defaultLayouts)
const activeLayoutId = useLocalStorageRef<{ id: string }>(ACTIVE_KEY, () => ({ id: 'general_service' }))
const activeLayout = computed<ProfileLayout>(() => {
const found = layouts.value.find(l => l.id === activeLayoutId.value.id)
return found ?? layouts.value[0] ?? defaultLayouts()[3] // fallback to general_service
})
const sortedSections = computed<ProfileSection[]>(() =>
[...activeLayout.value.sections]
.filter(s => s.visible)
.sort((a, b) => a.order - b.order)
)
function setActiveLayout(id: string) {
activeLayoutId.value = { id }
}
function addCustomLayout(layout: Omit<ProfileLayout, 'isCustom'>) {
layouts.value = [
...layouts.value,
{ ...layout, isCustom: true },
]
}
function updateLayout(id: string, partial: Partial<ProfileLayout>) {
layouts.value = layouts.value.map(l =>
l.id === id ? { ...l, ...partial } : l
)
}
function removeCustomLayout(id: string) {
const target = layouts.value.find(l => l.id === id)
if (!target || !target.isCustom) return
layouts.value = layouts.value.filter(l => l.id !== id)
if (activeLayoutId.value.id === id) {
activeLayoutId.value = { id: 'general_service' }
}
}
function resetToDefaults() {
layouts.value = defaultLayouts()
activeLayoutId.value = { id: 'general_service' }
}
return {
layouts,
activeLayoutId: computed(() => activeLayoutId.value.id),
activeLayout,
sortedSections,
setActiveLayout,
addCustomLayout,
updateLayout,
removeCustomLayout,
resetToDefaults,
}
}