bilhej/frontend/src/router/index.ts
Joakim Mörling 7a95c1423c
Some checks failed
CI / Lint, type check, unit tests, coverage (pull_request) Successful in 2m22s
CI / E2E browser tests (pull_request) Failing after 1m3s
Make customer-facing UI usable on smartphones.
Mobile traffic was breaking on narrow viewports because the header nav
overflowed and several pages used desktop-only spacing. This adds a
shared phone breakpoint, a hamburger menu, and scroll-to-top on route
changes so footer and menu navigation always land at the top of the page.

- Add --page-gutter and max-width 639px rules in base.css
- AppHeader: hamburger panel on small screens; flat account links on mobile
- AppFooter: stack footer links vertically on phones
- Home, compose, edit order, orders, auth, and legal pages: tighter gutters
  and responsive layout (orders card actions stack; home grids single-column)
- Router scrollBehavior: scroll to top on navigation; restore on browser back
- Tests: AppHeader menu toggle, Router scrollBehavior, mobile Playwright checks

Admin page is intentionally unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 13:03:35 +02:00

159 lines
4 KiB
TypeScript

import {
createRouter,
createWebHistory,
type RouteLocationNormalized,
} from 'vue-router'
import HomePage from '@/pages/HomePage.vue'
import ComposePage from '@/pages/ComposePage.vue'
import AboutPage from '@/pages/AboutPage.vue'
import ContactPage from '@/pages/ContactPage.vue'
import PrivacyPolicyPage from '@/pages/PrivacyPolicyPage.vue'
import TermsOfServicePage from '@/pages/TermsOfServicePage.vue'
import RegisterPage from '@/pages/RegisterPage.vue'
import LoginPage from '@/pages/LoginPage.vue'
import ForgotPasswordPage from '@/pages/ForgotPasswordPage.vue'
import ResetPasswordPage from '@/pages/ResetPasswordPage.vue'
import ChangePasswordPage from '@/pages/ChangePasswordPage.vue'
import ChangeEmailPage from '@/pages/ChangeEmailPage.vue'
import ConfirmEmailChangePage from '@/pages/ConfirmEmailChangePage.vue'
import OrdersPage from '@/pages/OrdersPage.vue'
import EditOrderPage from '@/pages/EditOrderPage.vue'
import AdminPage from '@/pages/AdminPage.vue'
import PaymentRedirect from '@/pages/PaymentRedirect.vue'
import { useAuthStore } from '@/stores/authStore'
import { getActivePinia } from 'pinia'
export function scrollBehavior(
to: RouteLocationNormalized,
_from: RouteLocationNormalized,
savedPosition: { left: number; top: number } | null,
) {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { el: to.hash, top: 0, behavior: 'smooth' as const }
}
return { top: 0, left: 0 }
}
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
scrollBehavior,
routes: [
{
path: '/',
name: 'home',
component: HomePage,
},
{
path: '/compose',
name: 'compose',
component: ComposePage,
meta: { requiresAuth: true },
},
{
path: '/orders',
name: 'orders',
component: OrdersPage,
meta: { requiresAuth: true },
},
{
path: '/bestallning/:orderId/redigera',
name: 'edit-order',
component: EditOrderPage,
meta: { requiresAuth: true },
},
{
path: '/andra-losenord',
name: 'change-password',
component: ChangePasswordPage,
meta: { requiresAuth: true },
},
{
path: '/andra-epost',
name: 'change-email',
component: ChangeEmailPage,
meta: { requiresAuth: true },
},
{
path: '/admin',
name: 'admin',
component: AdminPage,
meta: { requiresAuth: true, requiresAdmin: true },
},
{
path: '/betalning/:orderId',
name: 'payment',
component: PaymentRedirect,
meta: { requiresAuth: true },
},
{
path: '/registrera',
name: 'register',
component: RegisterPage,
meta: { guestOnly: true },
},
{
path: '/logga-in',
name: 'login',
component: LoginPage,
meta: { guestOnly: true },
},
{
path: '/glomt-losenord',
name: 'forgot-password',
component: ForgotPasswordPage,
meta: { guestOnly: true },
},
{
path: '/aterstall-losenord',
name: 'reset-password',
component: ResetPasswordPage,
meta: { guestOnly: true },
},
{
path: '/bekrafta-epost',
name: 'confirm-email-change',
component: ConfirmEmailChangePage,
},
{
path: '/om-oss',
name: 'about',
component: AboutPage,
},
{
path: '/om',
redirect: '/om-oss',
},
{
path: '/kontakt',
name: 'contact',
component: ContactPage,
},
{
path: '/integritetspolicy',
name: 'privacy',
component: PrivacyPolicyPage,
},
{
path: '/villkor',
name: 'terms',
component: TermsOfServicePage,
},
],
})
router.beforeEach((to) => {
if (!getActivePinia()) return
const auth = useAuthStore()
if (to.meta.guestOnly && auth.isAuthenticated) return { name: 'home' }
if (to.meta.requiresAuth && !auth.isAuthenticated) {
return { name: 'login', query: { redirect: to.fullPath } }
}
if (to.meta.requiresAdmin && !auth.isAdmin) return { name: 'home' }
})
export default router