Updated main app
This commit is contained in:
parent
91df929dad
commit
a10632e4bf
1 changed files with 247 additions and 69 deletions
316
src/app/App.vue
316
src/app/App.vue
|
|
@ -1,83 +1,261 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// Navbar is intentionally simple — authentication is not handled here.
|
import { computed, onMounted } from 'vue';
|
||||||
|
import { Layout, Menu, Button, Space, Typography } from 'ant-design-vue';
|
||||||
|
import type { MenuProps } from 'ant-design-vue';
|
||||||
|
import {
|
||||||
|
HomeOutlined,
|
||||||
|
InfoCircleOutlined,
|
||||||
|
RocketOutlined,
|
||||||
|
ReadOutlined,
|
||||||
|
TeamOutlined,
|
||||||
|
RobotOutlined,
|
||||||
|
BulbOutlined,
|
||||||
|
AppstoreOutlined,
|
||||||
|
DashboardOutlined,
|
||||||
|
LoginOutlined,
|
||||||
|
UserAddOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { useAuthStore } from '../stores/authStore';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const navItems = [
|
||||||
|
{
|
||||||
|
key: '/',
|
||||||
|
label: 'Home',
|
||||||
|
icon: HomeOutlined,
|
||||||
|
path: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '/about',
|
||||||
|
label: 'About',
|
||||||
|
icon: InfoCircleOutlined,
|
||||||
|
path: '/about',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '/onboarding',
|
||||||
|
label: 'Onboarding',
|
||||||
|
icon: RocketOutlined,
|
||||||
|
path: '/onboarding',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '/training',
|
||||||
|
label: 'Training',
|
||||||
|
icon: ReadOutlined,
|
||||||
|
path: '/training',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '/roles',
|
||||||
|
label: 'Roles',
|
||||||
|
icon: TeamOutlined,
|
||||||
|
path: '/roles',
|
||||||
|
roles: ['manager', 'admin'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '/agents',
|
||||||
|
label: 'Agents',
|
||||||
|
icon: RobotOutlined,
|
||||||
|
path: '/agents',
|
||||||
|
roles: ['manager', 'admin'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '/assessments',
|
||||||
|
label: 'Assessments',
|
||||||
|
icon: BulbOutlined,
|
||||||
|
path: '/assessments',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '/resources',
|
||||||
|
label: 'Resources',
|
||||||
|
icon: AppstoreOutlined,
|
||||||
|
path: '/resources',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '/progress',
|
||||||
|
label: 'Progress',
|
||||||
|
icon: DashboardOutlined,
|
||||||
|
path: '/progress',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const visibleNavItems = computed(() =>
|
||||||
|
navItems.filter((item) =>
|
||||||
|
item.roles ? authStore.hasRole(item.roles) : true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedKeys = computed(() => {
|
||||||
|
const match = visibleNavItems.value.find((item) =>
|
||||||
|
route.path.startsWith(item.key)
|
||||||
|
);
|
||||||
|
return match ? [match.key] : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSelect: MenuProps['onSelect'] = ({ key }) => {
|
||||||
|
const item = visibleNavItems.value.find((n) => n.key === key);
|
||||||
|
if (item) router.push(item.path);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogout = async () => {
|
||||||
|
await authStore.logout();
|
||||||
|
router.push('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
authStore.fetchSession();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<Layout class="shell">
|
||||||
<nav class="topnav">
|
<Layout.Header class="shell-header">
|
||||||
<div class="brand">
|
<div class="brand" @click="router.push('/')">Dynavera</div>
|
||||||
<router-link to="/">Agentic Trainers</router-link>
|
<Menu
|
||||||
</div>
|
mode="horizontal"
|
||||||
|
theme="dark"
|
||||||
|
:selectedKeys="selectedKeys"
|
||||||
|
class="shell-menu"
|
||||||
|
@select="onSelect"
|
||||||
|
>
|
||||||
|
<Menu.Item v-for="item in visibleNavItems" :key="item.key">
|
||||||
|
<Space size="small">
|
||||||
|
<component :is="item.icon" />
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
</Space>
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
<Space>
|
||||||
|
<template v-if="authStore.isAuthenticated">
|
||||||
|
<Typography.Text class="user-chip" strong>
|
||||||
|
{{ authStore.displayName || 'Account' }}
|
||||||
|
</Typography.Text>
|
||||||
|
<Button
|
||||||
|
ghost
|
||||||
|
:loading="authStore.loading"
|
||||||
|
@click="handleLogout"
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<Button ghost @click="router.push('/login')">
|
||||||
|
<LoginOutlined /> Login
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" @click="router.push('/register')">
|
||||||
|
<UserAddOutlined /> Register
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Space>
|
||||||
|
</Layout.Header>
|
||||||
|
|
||||||
<ul class="navlinks">
|
<Layout class="shell-body">
|
||||||
<li><router-link to="/">Home</router-link></li>
|
<Layout.Content class="shell-content">
|
||||||
<li><router-link to="/about">About</router-link></li>
|
<router-view />
|
||||||
<li><router-link to="/onboarding">Onboarding</router-link></li>
|
</Layout.Content>
|
||||||
<li><router-link to="/training">Training</router-link></li>
|
<Layout.Footer class="shell-footer">
|
||||||
<li><router-link to="/roles">Roles</router-link></li>
|
<Typography.Text type="secondary">
|
||||||
<li><router-link to="/agents">Agents</router-link></li>
|
<strong>Project Disclaimer:</strong> This is a
|
||||||
<li>
|
proof-of-concept demo project for educational purposes. All
|
||||||
<router-link to="/assessments">Assessments</router-link>
|
testimonials, statistics, and company names are fictional
|
||||||
</li>
|
placeholders.
|
||||||
<li><router-link to="/resources">Resources</router-link></li>
|
</Typography.Text>
|
||||||
<li><router-link to="/progress">Progress</router-link></li>
|
</Layout.Footer>
|
||||||
</ul>
|
</Layout>
|
||||||
|
</Layout>
|
||||||
<ul class="navlinks">
|
|
||||||
<li><router-link to="/login">Login</router-link></li>
|
|
||||||
<li><router-link to="/register">Register</router-link></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<router-view />
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.topnav {
|
.shell {
|
||||||
display: flex;
|
min-height: 100vh;
|
||||||
justify-content: space-between;
|
background: #0b1220;
|
||||||
align-items: center;
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
background: #fff;
|
|
||||||
border-bottom: 1px solid #e5e7eb;
|
|
||||||
}
|
}
|
||||||
.brand a {
|
.shell-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0 1.25rem;
|
||||||
|
background: #0f172a;
|
||||||
|
}
|
||||||
|
.brand {
|
||||||
|
color: #e5e7eb;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #111827;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.navlinks {
|
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
|
||||||
gap: 0.75rem;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.navlinks a {
|
|
||||||
color: #4b5563;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
}
|
|
||||||
.navlinks a.router-link-active {
|
|
||||||
color: #4f46e5;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.user {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
color: #374151;
|
|
||||||
}
|
|
||||||
.link-logout {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: #ef4444;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 0.5rem;
|
font-size: 1.05rem;
|
||||||
}
|
}
|
||||||
main {
|
.shell-menu {
|
||||||
padding: 1rem;
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.shell-body {
|
||||||
|
background: #0b1220;
|
||||||
|
min-height: calc(100vh - 64px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.shell-content {
|
||||||
|
padding: 24px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: calc(100vh - 64px - 64px);
|
||||||
|
}
|
||||||
|
.shell-footer {
|
||||||
|
text-align: center;
|
||||||
|
background: #0f172a;
|
||||||
|
}
|
||||||
|
:deep(.ant-menu-dark) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
:deep(.ant-menu-dark .ant-menu-item-selected) {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
:deep(.ant-typography),
|
||||||
|
:deep(.ant-typography p),
|
||||||
|
:deep(.ant-typography span),
|
||||||
|
:deep(.ant-list-item),
|
||||||
|
:deep(.ant-list-item-meta-title),
|
||||||
|
:deep(.ant-list-item-meta-description),
|
||||||
|
:deep(.ant-statistic-title),
|
||||||
|
:deep(.ant-statistic-content),
|
||||||
|
:deep(.ant-card-meta-title),
|
||||||
|
:deep(.ant-card-meta-description) {
|
||||||
|
color: #e5e7eb;
|
||||||
|
}
|
||||||
|
:deep(.ant-typography-secondary) {
|
||||||
|
color: #cbd5e1 !important;
|
||||||
|
}
|
||||||
|
:deep(.ant-form-item-label > label) {
|
||||||
|
color: #e5e7eb;
|
||||||
|
}
|
||||||
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-select-selector),
|
||||||
|
:deep(.ant-select-selection-item),
|
||||||
|
:deep(.ant-picker-input input) {
|
||||||
|
background: #111827;
|
||||||
|
color: #e5e7eb;
|
||||||
|
border-color: #334155;
|
||||||
|
}
|
||||||
|
:deep(.ant-input::placeholder),
|
||||||
|
:deep(.ant-select-selection-placeholder),
|
||||||
|
:deep(.ant-picker-input input::placeholder) {
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
:deep(.ant-card) {
|
||||||
|
background: #0f172a;
|
||||||
|
border-color: #1f2937;
|
||||||
|
}
|
||||||
|
:deep(.ant-btn:not(.ant-btn-primary)) {
|
||||||
|
color: #e5e7eb;
|
||||||
|
border-color: #334155;
|
||||||
|
background: #111827;
|
||||||
|
}
|
||||||
|
:deep(.ant-btn-primary) {
|
||||||
|
background: linear-gradient(90deg, #6366f1, #8b5cf6);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.user-chip {
|
||||||
|
color: #e5e7eb;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue