big refactor
This commit is contained in:
@@ -3,15 +3,13 @@
|
||||
* Global command palette — searches customers, policies, claims, and app pages.
|
||||
* Keyboard: Ctrl/Cmd+K to focus, Escape to close, ↑↓ to navigate, Enter to go.
|
||||
*/
|
||||
import { MOCK_CUSTOMERS, fmtMoney } from '~/data/mock-customers'
|
||||
|
||||
const router = useRouter()
|
||||
const open = ref(false)
|
||||
const q = ref('')
|
||||
const inputRef = ref<HTMLElement | null>(null)
|
||||
const activeIndex = ref(-1)
|
||||
|
||||
/* ── Build searchable records from mock data ── */
|
||||
/* ── Build searchable records from API data ── */
|
||||
|
||||
type SearchHit = {
|
||||
id: string
|
||||
@@ -24,48 +22,9 @@ type SearchHit = {
|
||||
}
|
||||
|
||||
const allRecords = computed<SearchHit[]>(() => {
|
||||
const hits: SearchHit[] = []
|
||||
|
||||
for (const c of MOCK_CUSTOMERS) {
|
||||
// Customer record
|
||||
hits.push({
|
||||
id: `cust-${c.id}`,
|
||||
kind: 'customer',
|
||||
icon: 'i-heroicons-user',
|
||||
title: c.name,
|
||||
meta: `${c.type} · ${c.documentId}`,
|
||||
detail: `${c.policies.length} policies · ${fmtMoney(c.policies.reduce((s, p) => s + p.premium, 0))}/yr · Agent: ${c.agent}`,
|
||||
to: `/customers/${c.id}`
|
||||
})
|
||||
|
||||
// Each policy
|
||||
for (const p of c.policies) {
|
||||
hits.push({
|
||||
id: `pol-${p.id}`,
|
||||
kind: 'policy',
|
||||
icon: p.icon,
|
||||
title: p.id,
|
||||
meta: `${p.line} · ${p.carrier} · ${c.name}`,
|
||||
detail: p.product,
|
||||
to: `/customers/${c.id}`
|
||||
})
|
||||
}
|
||||
|
||||
// Each claim
|
||||
for (const cl of c.claims) {
|
||||
hits.push({
|
||||
id: `claim-${cl.id}`,
|
||||
kind: 'claim',
|
||||
icon: 'i-heroicons-shield-exclamation',
|
||||
title: cl.id,
|
||||
meta: `${cl.type} · ${cl.status} · ${c.name}`,
|
||||
detail: `Policy ${cl.policy} · $${cl.amount.toLocaleString()}`,
|
||||
to: `/customers/${c.id}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return hits
|
||||
// TODO: Replace with API calls to fetch customers, policies, and claims
|
||||
// For now, return empty array since mock data has been removed
|
||||
return []
|
||||
})
|
||||
|
||||
/* ── App pages / destinations ── */
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import type { AppThemeId } from '~/types/app-theme'
|
||||
import { APP_THEME_OPTIONS } from '~/types/app-theme'
|
||||
|
||||
defineProps<{
|
||||
sidebarCollapsed: boolean
|
||||
brandTitle?: string
|
||||
@@ -15,19 +12,18 @@ const emit = defineEmits<{
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const isHome = computed(() => route.path === '/')
|
||||
const { themeId, applyTheme } = useAppTheme()
|
||||
|
||||
const themeIcons: Record<string, string> = {
|
||||
light: 'i-heroicons-sun',
|
||||
purple: 'i-heroicons-sparkles',
|
||||
dark: 'i-heroicons-moon',
|
||||
'dark-purple': 'i-heroicons-star',
|
||||
}
|
||||
const colorMode = useColorMode()
|
||||
const isDark = computed({
|
||||
get () {
|
||||
return colorMode.value === 'dark'
|
||||
},
|
||||
set () {
|
||||
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
|
||||
}
|
||||
})
|
||||
|
||||
const userMenuOpen = ref(false)
|
||||
const userMenuRoot = ref<HTMLElement | null>(null)
|
||||
const themeMenuOpen = ref(false)
|
||||
const themeMenuRoot = ref<HTMLElement | null>(null)
|
||||
|
||||
function closeUserMenu() {
|
||||
userMenuOpen.value = false
|
||||
@@ -38,10 +34,6 @@ function onDocClick(e: MouseEvent) {
|
||||
if (userEl && userMenuOpen.value && !userEl.contains(e.target as Node)) {
|
||||
userMenuOpen.value = false
|
||||
}
|
||||
const themeEl = themeMenuRoot.value
|
||||
if (themeEl && themeMenuOpen.value && !themeEl.contains(e.target as Node)) {
|
||||
themeMenuOpen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => document.addEventListener('click', onDocClick))
|
||||
@@ -107,58 +99,19 @@ onUnmounted(() => document.removeEventListener('click', onDocClick))
|
||||
<UIcon name="i-heroicons-arrow-path" style="width: 16px; height: 16px;" />
|
||||
</button>
|
||||
|
||||
<!-- Quick theme switcher -->
|
||||
<div ref="themeMenuRoot" class="relative">
|
||||
<button
|
||||
type="button"
|
||||
class="app-topbar-icon-btn"
|
||||
title="Switch theme"
|
||||
<!-- Theme toggle -->
|
||||
<ClientOnly>
|
||||
<UButton
|
||||
:icon="isDark ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
aria-label="Theme"
|
||||
@click.stop="themeMenuOpen = !themeMenuOpen"
|
||||
>
|
||||
<UIcon :name="themeIcons[themeId] ?? 'i-heroicons-swatch'" style="width: 16px; height: 16px;" />
|
||||
</button>
|
||||
<Transition
|
||||
enter-active-class="transition duration-150 ease-out"
|
||||
enter-from-class="opacity-0 scale-95 translate-y-1"
|
||||
enter-to-class="opacity-100 scale-100 translate-y-0"
|
||||
leave-active-class="transition duration-100 ease-in"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0 scale-95"
|
||||
>
|
||||
<div
|
||||
v-show="themeMenuOpen"
|
||||
class="absolute right-0 top-[calc(100%+8px)] z-50 w-52 overflow-hidden rounded-xl border border-[var(--sidebar-border)] bg-[var(--surface)] py-1.5 shadow-xl ring-1 ring-black/5"
|
||||
>
|
||||
<p class="px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-[var(--text-muted)]">Theme</p>
|
||||
<button
|
||||
v-for="opt in APP_THEME_OPTIONS"
|
||||
:key="opt.id"
|
||||
type="button"
|
||||
class="flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm transition hover:bg-[var(--brand-faint)]"
|
||||
:class="themeId === opt.id ? 'text-[var(--brand)] font-medium' : 'text-[var(--text-primary)]'"
|
||||
@click="applyTheme(opt.id as AppThemeId); themeMenuOpen = false"
|
||||
>
|
||||
<UIcon :name="themeIcons[opt.id]" class="h-4 w-4 shrink-0" :class="themeId === opt.id ? 'text-[var(--brand)]' : 'opacity-60'" />
|
||||
<span class="flex-1">{{ opt.label }}</span>
|
||||
<UIcon
|
||||
v-if="themeId === opt.id"
|
||||
name="i-heroicons-check"
|
||||
class="h-3.5 w-3.5 text-[var(--brand)]"
|
||||
/>
|
||||
</button>
|
||||
<div class="mx-3 my-1.5 border-t border-[var(--sidebar-border)]" />
|
||||
<NuxtLink
|
||||
to="/account"
|
||||
class="flex items-center gap-2.5 px-3 py-1.5 text-xs text-[var(--text-muted)] transition hover:text-[var(--brand)]"
|
||||
@click="themeMenuOpen = false"
|
||||
>
|
||||
<UIcon name="i-heroicons-cog-6-tooth" class="h-3.5 w-3.5" />
|
||||
All appearance settings
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
@click="isDark = !isDark"
|
||||
/>
|
||||
<template #fallback>
|
||||
<div class="w-8 h-8" />
|
||||
</template>
|
||||
</ClientOnly>
|
||||
|
||||
<NuxtLink to="/settings" class="inline-flex" title="Software settings">
|
||||
<span class="app-topbar-icon-btn">
|
||||
|
||||
Reference in New Issue
Block a user