Adopt logistics visual system across webapp

This commit is contained in:
Ruslan Bakiev
2026-04-11 08:31:34 +07:00
parent ebe72907a4
commit a74e75049c
28 changed files with 1434 additions and 240 deletions

88
app/layouts/manager.vue Normal file
View File

@@ -0,0 +1,88 @@
<script setup lang="ts">
const route = useRoute()
const localePath = useLocalePath()
const { signOut } = useAuth()
const userData = useState<{
activeTeam?: { name?: string | null }
firstName?: string | null
} | null>('me', () => null)
const navItems = [
{ label: 'Orders', path: '/manager/orders', icon: 'lucide:package' },
{ label: 'Quotations', path: '/manager/quotations', icon: 'lucide:file-text' },
{ label: 'Tariffs', path: '/manager/tariffs', icon: 'lucide:waypoints' },
]
function isActive(path: string) {
const localized = localePath(path)
return route.path === localized || route.path.startsWith(`${localized}/`)
}
</script>
<template>
<div class="manager-logistics-shell text-[#2f2418]">
<div class="sticky top-0 z-50 px-3 pt-3 md:px-4">
<div class="mx-auto max-w-[1440px]">
<header class="rounded-[30px] border border-[#e1d7c7] bg-[#efe6d8]/95 px-4 py-4 shadow-[0_18px_40px_rgba(47,36,24,0.08)] backdrop-blur">
<div class="flex flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
<div class="flex items-center gap-3">
<NuxtLink
:to="localePath('/')"
class="flex h-12 min-w-[88px] items-center justify-center rounded-full bg-[#2f2418] px-5 text-sm font-black uppercase tracking-[0.2em] text-white"
>
Optovia
</NuxtLink>
<div>
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-[#8a7761]">Logistics manager</p>
<h1 class="text-xl font-black leading-tight text-[#2f2418] md:text-2xl">Control tower</h1>
</div>
</div>
<nav class="flex flex-wrap items-center gap-2">
<NuxtLink
v-for="item in navItems"
:key="item.path"
:to="localePath(item.path)"
class="inline-flex items-center gap-2 rounded-full px-4 py-2 text-sm font-bold transition"
:class="isActive(item.path) ? 'bg-[#2f2418] text-white shadow-[0_10px_24px_rgba(47,36,24,0.16)]' : 'bg-white text-[#5f4b33] hover:bg-[#f8f3ec]'"
>
<Icon :name="item.icon" size="16" />
<span>{{ item.label }}</span>
</NuxtLink>
</nav>
<div class="flex flex-wrap items-center gap-2">
<div class="rounded-full bg-white px-4 py-2 text-sm font-semibold text-[#5f4b33]">
{{ userData?.activeTeam?.name || userData?.firstName || 'Active workspace' }}
</div>
<NuxtLink
:to="localePath('/clientarea/orders')"
class="inline-flex items-center gap-2 rounded-full bg-white px-4 py-2 text-sm font-bold text-[#5f4b33] transition hover:bg-[#f8f3ec]"
>
<Icon name="lucide:arrow-left" size="16" />
<span>Client area</span>
</NuxtLink>
<button
type="button"
class="inline-flex items-center gap-2 rounded-full bg-[#2f2418] px-4 py-2 text-sm font-bold text-white transition hover:bg-[#493824]"
@click="signOut()"
>
<Icon name="lucide:log-out" size="16" />
<span>Sign out</span>
</button>
</div>
</div>
</header>
</div>
</div>
<main class="px-3 pb-5 pt-3 md:px-4 md:pb-6">
<div class="mx-auto max-w-[1440px]">
<section class="rounded-[34px] bg-[#f3eee6] px-4 py-4 shadow-[0_24px_72px_rgba(3,8,20,0.18)] md:px-5 md:py-5 lg:px-6 lg:py-6">
<slot />
</section>
</div>
</main>
</div>
</template>

View File

@@ -1,5 +1,5 @@
<template>
<div class="min-h-screen flex flex-col bg-base-300">
<div class="min-h-screen flex flex-col manager-logistics-shell">
<AiChatSidebar
:open="isChatOpen"
:width="chatWidth"
@@ -121,7 +121,7 @@ const {
} = useHeroScroll()
// Theme state
const theme = useState<'cupcake' | 'night'>('theme', () => 'cupcake')
const theme = useState<'silk' | 'night'>('theme', () => 'silk')
// User data state (shared across layouts)
interface SelectedLocation {
@@ -313,7 +313,7 @@ const onClickSignOut = () => {
signOut(siteUrl)
}
const applyTheme = (value: 'cupcake' | 'night') => {
const applyTheme = (value: 'silk' | 'night') => {
if (import.meta.client) {
document.documentElement.setAttribute('data-theme', value)
localStorage.setItem('theme', value)
@@ -322,8 +322,8 @@ const applyTheme = (value: 'cupcake' | 'night') => {
onMounted(() => {
const stored = import.meta.client ? localStorage.getItem('theme') : null
if (stored === 'night' || stored === 'cupcake') {
theme.value = stored as 'cupcake' | 'night'
if (stored === 'night' || stored === 'silk') {
theme.value = stored as 'silk' | 'night'
}
applyTheme(theme.value)
})
@@ -331,7 +331,7 @@ onMounted(() => {
watch(theme, (value) => applyTheme(value))
const toggleTheme = () => {
theme.value = theme.value === 'night' ? 'cupcake' : 'night'
theme.value = theme.value === 'night' ? 'silk' : 'night'
}
// Search handler for Quote mode - triggers search via shared state