Updated main app
This commit is contained in:
parent
91df929dad
commit
a10632e4bf
1 changed files with 247 additions and 69 deletions
314
src/app/App.vue
314
src/app/App.vue
|
|
@ -1,83 +1,261 @@
|
|||
<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>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<nav class="topnav">
|
||||
<div class="brand">
|
||||
<router-link to="/">Agentic Trainers</router-link>
|
||||
</div>
|
||||
<Layout class="shell">
|
||||
<Layout.Header class="shell-header">
|
||||
<div class="brand" @click="router.push('/')">Dynavera</div>
|
||||
<Menu
|
||||
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">
|
||||
<li><router-link to="/">Home</router-link></li>
|
||||
<li><router-link to="/about">About</router-link></li>
|
||||
<li><router-link to="/onboarding">Onboarding</router-link></li>
|
||||
<li><router-link to="/training">Training</router-link></li>
|
||||
<li><router-link to="/roles">Roles</router-link></li>
|
||||
<li><router-link to="/agents">Agents</router-link></li>
|
||||
<li>
|
||||
<router-link to="/assessments">Assessments</router-link>
|
||||
</li>
|
||||
<li><router-link to="/resources">Resources</router-link></li>
|
||||
<li><router-link to="/progress">Progress</router-link></li>
|
||||
</ul>
|
||||
|
||||
<ul class="navlinks">
|
||||
<li><router-link to="/login">Login</router-link></li>
|
||||
<li><router-link to="/register">Register</router-link></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
<Layout class="shell-body">
|
||||
<Layout.Content class="shell-content">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
</Layout.Content>
|
||||
<Layout.Footer class="shell-footer">
|
||||
<Typography.Text type="secondary">
|
||||
<strong>Project Disclaimer:</strong> This is a
|
||||
proof-of-concept demo project for educational purposes. All
|
||||
testimonials, statistics, and company names are fictional
|
||||
placeholders.
|
||||
</Typography.Text>
|
||||
</Layout.Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.topnav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1rem;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
.shell {
|
||||
min-height: 100vh;
|
||||
background: #0b1220;
|
||||
}
|
||||
.brand a {
|
||||
.shell-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0 1.25rem;
|
||||
background: #0f172a;
|
||||
}
|
||||
.brand {
|
||||
color: #e5e7eb;
|
||||
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;
|
||||
margin-left: 0.5rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
main {
|
||||
padding: 1rem;
|
||||
.shell-menu {
|
||||
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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue