test
This commit is contained in:
parent
efd21a7297
commit
441676be27
97 changed files with 10764 additions and 223 deletions
35
frontend/src/app/AppShell.vue
Normal file
35
frontend/src/app/AppShell.vue
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { RouterView } from "vue-router";
|
||||
|
||||
import AppHeader from "@/components/layout/AppHeader.vue";
|
||||
import AppNav from "@/components/layout/AppNav.vue";
|
||||
import RequestErrorPanel from "@/components/patterns/RequestErrorPanel.vue";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
const auth = useAuthStore();
|
||||
const showChrome = computed(() => auth.isAuthenticated);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen overflow-x-clip">
|
||||
<a href="#app-main" class="skip-link">Skip to main content</a>
|
||||
<RequestErrorPanel />
|
||||
|
||||
<template v-if="showChrome">
|
||||
<AppHeader />
|
||||
<div
|
||||
class="grid min-h-[calc(100dvh-4.5rem)] grid-cols-1 lg:h-[calc(100dvh-4.5rem)] lg:min-h-[calc(100dvh-4.5rem)] lg:grid-cols-[17rem_minmax(0,1fr)]"
|
||||
>
|
||||
<AppNav class="lg:min-h-0 lg:overflow-y-auto" />
|
||||
<main id="app-main" class="min-w-0 px-4 py-6 sm:px-6 lg:min-h-0 lg:overflow-y-auto lg:px-8">
|
||||
<RouterView />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<main id="app-main" v-else class="min-h-screen overflow-x-clip">
|
||||
<RouterView />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
23
frontend/src/app/guards.ts
Normal file
23
frontend/src/app/guards.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import type { Router } from "vue-router";
|
||||
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
export function applyRouteGuards(router: Router): void {
|
||||
router.beforeEach((to) => {
|
||||
const auth = useAuthStore();
|
||||
|
||||
if (to.meta.requiresAuth && !auth.isAuthenticated) {
|
||||
return { name: "login" };
|
||||
}
|
||||
|
||||
if (to.meta.requiresAdmin && !auth.isAdmin) {
|
||||
return { name: "dashboard" };
|
||||
}
|
||||
|
||||
if (to.meta.guestOnly && auth.isAuthenticated) {
|
||||
return { name: "dashboard" };
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
12
frontend/src/app/providers.ts
Normal file
12
frontend/src/app/providers.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { setCsrfTokenProvider } from "@/lib/api/client";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useThemeStore } from "@/stores/theme";
|
||||
|
||||
export async function bootstrapAppProviders(): Promise<void> {
|
||||
const theme = useThemeStore();
|
||||
theme.initialize();
|
||||
|
||||
const auth = useAuthStore();
|
||||
setCsrfTokenProvider(() => auth.csrfToken);
|
||||
await auth.bootstrapSession();
|
||||
}
|
||||
84
frontend/src/app/router.ts
Normal file
84
frontend/src/app/router.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { createRouter, createWebHistory } from "vue-router";
|
||||
|
||||
import { applyRouteGuards } from "@/app/guards";
|
||||
import LoginPage from "@/pages/LoginPage.vue";
|
||||
import DashboardPage from "@/pages/DashboardPage.vue";
|
||||
import ScholarsPage from "@/pages/ScholarsPage.vue";
|
||||
import PublicationsPage from "@/pages/PublicationsPage.vue";
|
||||
import RunsPage from "@/pages/RunsPage.vue";
|
||||
import RunDetailPage from "@/pages/RunDetailPage.vue";
|
||||
import SettingsPage from "@/pages/SettingsPage.vue";
|
||||
import AdminUsersPage from "@/pages/AdminUsersPage.vue";
|
||||
import StyleGuidePage from "@/pages/StyleGuidePage.vue";
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: "/login",
|
||||
name: "login",
|
||||
component: LoginPage,
|
||||
meta: { guestOnly: true },
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
redirect: "/dashboard",
|
||||
},
|
||||
{
|
||||
path: "/dashboard",
|
||||
name: "dashboard",
|
||||
component: DashboardPage,
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/scholars",
|
||||
name: "scholars",
|
||||
component: ScholarsPage,
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/publications",
|
||||
name: "publications",
|
||||
component: PublicationsPage,
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
name: "settings",
|
||||
component: SettingsPage,
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/admin/style-guide",
|
||||
name: "style-guide",
|
||||
component: StyleGuidePage,
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: "/admin/runs",
|
||||
name: "runs",
|
||||
component: RunsPage,
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: "/admin/runs/:id",
|
||||
name: "run-detail",
|
||||
component: RunDetailPage,
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: "/admin/users",
|
||||
name: "admin-users",
|
||||
component: AdminUsersPage,
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
redirect: "/dashboard",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
applyRouteGuards(router);
|
||||
|
||||
export default router;
|
||||
Loading…
Add table
Add a link
Reference in a new issue