Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 159 additions & 78 deletions client/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,42 +1,79 @@
<template>
<div class="app">
<header class="top-nav">
<div class="nav-container">
<div class="logo">
<h1>{{ t('nav.companyName') }}</h1>
<span class="subtitle">{{ t('nav.subtitle') }}</span>
<aside class="sidebar">
<div class="sidebar-top">
<div class="sidebar-logo">
<div class="logo-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<rect width="24" height="24" rx="6" fill="rgba(255,255,255,0.2)"/>
<path d="M7 8h10M7 12h10M7 16h6" stroke="white" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
<div class="logo-text">
<span class="logo-name">{{ t('nav.companyName') }}</span>
<span class="logo-subtitle">{{ t('nav.subtitle') }}</span>
</div>
</div>
<nav class="nav-tabs">
<router-link to="/" :class="{ active: $route.path === '/' }">
{{ t('nav.overview') }}

<nav class="sidebar-nav">
<router-link to="/" :class="['nav-item', { active: $route.path === '/' }]">
<svg class="nav-icon" viewBox="0 0 20 20" fill="currentColor">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
<span>{{ t('nav.overview') }}</span>
</router-link>
<router-link to="/inventory" :class="{ active: $route.path === '/inventory' }">
{{ t('nav.inventory') }}
<router-link to="/inventory" :class="['nav-item', { active: $route.path === '/inventory' }]">
<svg class="nav-icon" viewBox="0 0 20 20" fill="currentColor">
<path d="M4 3a2 2 0 100 4h12a2 2 0 100-4H4z"/>
<path fill-rule="evenodd" d="M3 8h14v7a2 2 0 01-2 2H5a2 2 0 01-2-2V8zm5 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" clip-rule="evenodd"/>
</svg>
<span>{{ t('nav.inventory') }}</span>
</router-link>
<router-link to="/orders" :class="{ active: $route.path === '/orders' }">
{{ t('nav.orders') }}
<router-link to="/orders" :class="['nav-item', { active: $route.path === '/orders' }]">
<svg class="nav-icon" viewBox="0 0 20 20" fill="currentColor">
<path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"/>
<path fill-rule="evenodd" d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z" clip-rule="evenodd"/>
</svg>
<span>{{ t('nav.orders') }}</span>
</router-link>
<router-link to="/spending" :class="{ active: $route.path === '/spending' }">
{{ t('nav.finance') }}
<router-link to="/spending" :class="['nav-item', { active: $route.path === '/spending' }]">
<svg class="nav-icon" viewBox="0 0 20 20" fill="currentColor">
<path d="M4 4a2 2 0 00-2 2v1h16V6a2 2 0 00-2-2H4z"/>
<path fill-rule="evenodd" d="M18 9H2v5a2 2 0 002 2h12a2 2 0 002-2V9zM4 13a1 1 0 011-1h1a1 1 0 110 2H5a1 1 0 01-1-1zm5-1a1 1 0 100 2h1a1 1 0 100-2H9z" clip-rule="evenodd"/>
</svg>
<span>{{ t('nav.finance') }}</span>
</router-link>
<router-link to="/demand" :class="{ active: $route.path === '/demand' }">
{{ t('nav.demandForecast') }}
<router-link to="/demand" :class="['nav-item', { active: $route.path === '/demand' }]">
<svg class="nav-icon" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M12 7a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0V8.414l-4.293 4.293a1 1 0 01-1.414 0L8 10.414l-4.293 4.293a1 1 0 01-1.414-1.414l5-5a1 1 0 011.414 0L11 10.586 14.586 7H12z" clip-rule="evenodd"/>
</svg>
<span>{{ t('nav.demandForecast') }}</span>
</router-link>
<router-link to="/reports" :class="{ active: $route.path === '/reports' }">
Reports
<router-link to="/reports" :class="['nav-item', { active: $route.path === '/reports' }]">
<svg class="nav-icon" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M6 2a2 2 0 00-2 2v12a2 2 0 002 2h8a2 2 0 002-2V7.414A2 2 0 0015.414 6L12 2.586A2 2 0 0010.586 2H6zm2 10a1 1 0 10-2 0v3a1 1 0 102 0v-3zm2-3a1 1 0 011 1v5a1 1 0 11-2 0v-5a1 1 0 011-1zm4-1a1 1 0 10-2 0v7a1 1 0 102 0V8z" clip-rule="evenodd"/>
</svg>
<span>Reports</span>
</router-link>
</nav>
</div>

<div class="sidebar-bottom">
<LanguageSwitcher />
<div class="sidebar-divider"></div>
<ProfileMenu
@show-profile-details="showProfileDetails = true"
@show-tasks="showTasks = true"
/>
</div>
</header>
<FilterBar />
<main class="main-content">
<router-view />
</main>
</aside>

<div class="main-wrapper">
<FilterBar />
<main class="main-content">
<router-view />
</main>
</div>

<ProfileDetailsModal
:is-open="showProfileDetails"
Expand Down Expand Up @@ -170,107 +207,151 @@ export default {

body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: #f8fafc;
background: #f1f5f9;
color: #1e293b;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

.app {
display: flex;
flex-direction: column;
flex-direction: row;
min-height: 100vh;
}

.top-nav {
background: #ffffff;
border-bottom: 1px solid #e2e8f0;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05);
position: sticky;
/* ─── Sidebar ─── */
.sidebar {
width: 240px;
min-width: 240px;
background: linear-gradient(180deg, #1e3a8a 0%, #1e40af 100%);
display: flex;
flex-direction: column;
justify-content: space-between;
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: 100;
overflow-y: auto;
}

.nav-container {
max-width: 1600px;
margin: 0 auto;
display: flex;
align-items: center;
padding: 0 2rem;
height: 70px;
.sidebar-top {
padding: 1.25rem 0.75rem 0;
}

.nav-container > .nav-tabs {
margin-left: auto;
margin-right: 1rem;
.sidebar-logo {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0 0.5rem;
margin-bottom: 1.5rem;
}

.nav-container > .language-switcher {
margin-right: 1rem;
.logo-icon {
flex-shrink: 0;
}

.logo {
.logo-text {
display: flex;
align-items: baseline;
gap: 0.75rem;
flex-direction: column;
min-width: 0;
}

.logo h1 {
font-size: 1.375rem;
.logo-name {
font-size: 1rem;
font-weight: 700;
color: #0f172a;
color: #ffffff;
letter-spacing: -0.025em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.subtitle {
font-size: 0.813rem;
color: #64748b;
.logo-subtitle {
font-size: 0.688rem;
color: rgba(255, 255, 255, 0.5);
font-weight: 400;
padding-left: 0.75rem;
border-left: 1px solid #e2e8f0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.nav-tabs {
.sidebar-nav {
display: flex;
gap: 0.25rem;
flex-direction: column;
gap: 2px;
}

.nav-tabs a {
padding: 0.625rem 1.25rem;
color: #64748b;
.nav-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.625rem 0.75rem;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
font-weight: 500;
font-size: 0.938rem;
border-radius: 6px;
transition: all 0.2s ease;
position: relative;
font-size: 0.875rem;
border-radius: 8px;
transition: all 0.15s ease;
}

.nav-tabs a:hover {
color: #0f172a;
background: #f1f5f9;
.nav-item:hover {
color: #ffffff;
background: rgba(255, 255, 255, 0.1);
}

.nav-tabs a.active {
color: #2563eb;
background: #eff6ff;
.nav-item.active {
color: #ffffff;
background: rgba(255, 255, 255, 0.18);
font-weight: 600;
}

.nav-tabs a.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 2px;
background: #2563eb;
.nav-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
opacity: 0.85;
}

.nav-item.active .nav-icon {
opacity: 1;
}

/* ─── Sidebar Bottom ─── */
.sidebar-bottom {
padding: 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.sidebar-divider {
height: 1px;
background: rgba(255, 255, 255, 0.12);
margin: 0.25rem 0;
}

.sidebar-bottom :deep(.language-switcher) {
color: rgba(255, 255, 255, 0.7);
}

.sidebar-bottom :deep(.profile-menu) {
color: rgba(255, 255, 255, 0.7);
}

/* ─── Main Content ─── */
.main-wrapper {
flex: 1;
margin-left: 240px;
display: flex;
flex-direction: column;
min-height: 100vh;
}

.main-content {
flex: 1;
max-width: 1600px;
max-width: 1400px;
width: 100%;
margin: 0 auto;
padding: 1.5rem 2rem;
}

Expand Down
7 changes: 3 additions & 4 deletions client/src/components/FilterBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,16 @@ export default {

<style scoped>
.filters-bar {
background: #f8fafc;
background: #ffffff;
border-bottom: 1px solid #e2e8f0;
padding: 0.75rem 0;
position: sticky;
top: 70px;
top: 0;
z-index: 90;
}

.filters-container {
max-width: 1600px;
margin: 0 auto;
max-width: 1400px;
padding: 0 2rem;
display: flex;
align-items: center;
Expand Down
Loading