import { useUserStore } from "@/store/UserStore"; import { createRouter, createWebHistory } from "vue-router"; import { api } from "../helpers/api"; import { useApplicationStore } from "../store/ApplicationStore"; import { updateSEOTags } from "../helpers/seo"; export const routes = [ { path: "/", name: "home", meta: { title: "Home", description: "STEMMechanics, a family-run company based in Cairns, Queensland, creates fantastic STEM-focused programs and activities that are both entertaining and educational.", }, component: () => import("@/views/Home.vue"), }, { path: "/blog", name: "blog", meta: { title: "Blog", }, component: () => import(/* webpackPrefetch: true */ "@/views/Blog.vue"), }, { path: "/article", redirect: "/blog", children: [ { path: ":slug", name: "article", component: () => import("@/views/Article.vue"), }, ], }, { path: "/workshops", name: "workshops", meta: { title: "Workshops", }, component: () => import(/* webpackPreload: true */ "@/views/Workshops.vue"), }, { path: "/event", redirect: "/workshops", children: [ { path: ":id", name: "event", component: () => import("@/views/Event.vue"), }, ], }, { path: "/verify-email", name: "verify-email", meta: { title: "Verify Email", }, component: () => import("@/views/EmailVerify.vue"), }, { path: "/resend-verify-email", name: "resend-verify-email", meta: { title: "Resend Verification Email", }, component: () => import("@/views/ResendEmailVerify.vue"), }, { path: "/reset-password", name: "reset-password", meta: { title: "Reset Password", }, component: () => import("@/views/ResetPassword.vue"), }, { path: "/privacy", name: "privacy", meta: { title: "Privacy Policy", }, component: () => import("@/views/Privacy.vue"), }, { path: "/rules", name: "rules", meta: { title: "Rules", }, component: () => import("@/views/Rules.vue"), }, { path: "/community", name: "community", meta: { title: "Community", }, component: () => import("@/views/Community.vue"), }, { path: "/minecraft", children: [ { path: "", name: "minecraft", meta: { title: "Minecraft", }, component: () => import("@/views/Minecraft.vue"), }, { path: "curve", name: "minecraft-curve", meta: { title: "Minecraft Curve", }, component: () => import("@/views/MinecraftCurve.vue"), }, ], }, { path: "/login", name: "login", meta: { title: "Login", middleware: "guest", }, component: () => import(/* webpackPrefetch: true */ "@/views/Login.vue"), }, { path: "/logout", name: "logout", meta: { title: "Logout", }, component: () => import("@/views/Logout.vue"), }, { path: "/contact", name: "contact", meta: { title: "Contact", }, component: () => import(/* webpackPrefetch: true */ "@/views/Contact.vue"), }, { path: "/conduct", redirect: { name: "code-of-conduct" }, }, { path: "/code-of-conduct", name: "code-of-conduct", meta: { title: "Code of Conduct", }, component: () => import("@/views/CodeOfConduct.vue"), }, { path: "/terms", redirect: { name: "terms-and-conditions" }, }, { path: "/terms-and-conditions", name: "terms-and-conditions", meta: { title: "Terms and Conditions", }, component: () => import("@/views/TermsAndConditions.vue"), }, { path: "/register", name: "register", meta: { title: "Register", }, component: () => import(/* webpackPrefetch: true */ "@/views/Register.vue"), }, { path: "/dashboard", children: [ { path: "", name: "dashboard", meta: { title: "Dashboard", middleware: "authenticated", }, component: () => import( /* webpackPrefetch: true */ "@/views/dashboard/Dashboard.vue" ), }, { path: "analytics", children: [ { path: "", name: "dashboard-analytics-list", meta: { title: "Analytics", middleware: "authenticated", }, component: () => import("@/views/dashboard/AnalyticsList.vue"), }, { path: ":id", name: "dashboard-analytics-item", meta: { title: "Analytics Session", middleware: "authenticated", }, component: () => import("@/views/dashboard/AnalyticsItem.vue"), }, ], }, { path: "articles", children: [ { path: "", name: "dashboard-article-list", meta: { title: "Articles", middleware: "authenticated", }, component: () => import("@/views/dashboard/ArticleList.vue"), }, { path: "create", name: "dashboard-article-create", meta: { title: "Create Article", middleware: "authenticated", }, component: () => import("@/views/dashboard/ArticleEdit.vue"), }, { path: ":id", name: "dashboard-article-edit", meta: { title: "Edit Article", middleware: "authenticated", }, component: () => import("@/views/dashboard/ArticleEdit.vue"), }, ], }, { path: "events", children: [ { path: "", name: "dashboard-event-list", meta: { title: "Events", middleware: "authenticated", }, component: () => import("@/views/dashboard/EventList.vue"), }, { path: "create", name: "dashboard-event-create", meta: { title: "Create Event", middleware: "authenticated", }, component: () => import("@/views/dashboard/EventEdit.vue"), }, { path: ":id", name: "dashboard-event-edit", meta: { title: "Event", middleware: "authenticated", }, component: () => import("@/views/dashboard/EventEdit.vue"), }, ], }, { path: "details", name: "dashboard-account-details", meta: { title: "Account Details", middleware: "authenticated", }, component: () => import("@/views/dashboard/UserEdit.vue"), }, { path: "users", children: [ { path: "", name: "dashboard-user-list", meta: { title: "Users", middleware: "authenticated", }, component: () => import("@/views/dashboard/UserList.vue"), }, { path: "create", name: "dashboard-user-create", meta: { title: "Create User", middleware: "authenticated", }, component: () => import("@/views/dashboard/UserEdit.vue"), }, { path: ":id", name: "dashboard-user-edit", meta: { title: "Edit User", middleware: "authenticated", }, component: () => import("@/views/dashboard/UserEdit.vue"), }, ], }, { path: "media", children: [ { path: "", name: "dashboard-media-list", meta: { title: "Media", middleware: "authenticated", }, component: () => import("@/views/dashboard/MediaList.vue"), }, { path: "create", name: "dashboard-media-create", meta: { title: "Upload Media", middleware: "authenticated", }, component: () => import("@/views/dashboard/MediaEdit.vue"), }, { path: ":id", name: "dashboard-media-edit", meta: { title: "Edit Media", middleware: "authenticated", }, component: () => import("@/views/dashboard/MediaEdit.vue"), }, ], }, { path: "shortlinks", children: [ { path: "", name: "dashboard-shortlink-list", meta: { title: "Shortlink", middleware: "authenticated", }, component: () => import("@/views/dashboard/ShortlinkList.vue"), }, { path: "create", name: "dashboard-shortlink-create", meta: { title: "Create Shortlink", middleware: "authenticated", }, component: () => import("@/views/dashboard/ShortlinkEdit.vue"), }, { path: ":id", name: "dashboard-shortlink-edit", meta: { title: "Edit Shortlink", middleware: "authenticated", }, component: () => import("@/views/dashboard/ShortlinkEdit.vue"), }, ], }, { path: "discord-bot-logs", name: "dashboard-discord-bot-logs", meta: { title: "Discord Bot Logs", middleware: "authenticated", }, component: () => import("@/views/dashboard/DiscordBotLogs.vue"), }, ], }, { path: "/forgot-password", name: "forgot-password", meta: { title: "Forgot Password", }, component: () => import("@/views/ForgotPassword.vue"), }, { path: "/:catchAll(.*)", name: "not-found", meta: { title: "Page not found", hideInEditor: true, }, component: () => import("@/views/404.vue"), }, ]; const router = createRouter({ history: createWebHistory(), routes, scrollBehavior(to, from, savedPosition) { // always scroll to top return { top: 0 }; }, }); // export let activeRoutes = []; router.beforeEach(async (to, from, next) => { const userStore = useUserStore(); const applicationStore = useApplicationStore(); applicationStore.clearDynamicTitle(); if (applicationStore.pageLoaderTimeout !== 0) { window.clearTimeout(applicationStore.pageLoaderTimeout); applicationStore.pageLoaderTimeout = window.setTimeout(() => { const pageLoadingElem = document.getElementById("sm-page-loading"); if (pageLoadingElem !== null) { pageLoadingElem.style.display = "flex"; } }, 0); } if (to.meta.middleware == "authenticated") { if (userStore.id) { api.get({ url: "/me", }) .then((res) => { userStore.setUserDetails(res.data.user); }) .catch((err) => { console.log(err); if (err.status == 401) { userStore.clearUser(); window.location.href = `/login?redirect=${to.fullPath}`; } }); } if (!userStore.id) { next({ name: "login", query: { redirect: encodeURIComponent(to.fullPath) }, }); return; } } api.post({ url: "/analytics", body: { type: "pageview", path: to.fullPath, }, }).catch(() => { /* empty */ }); next(); }); router.afterEach((to, from) => { const applicationStore = useApplicationStore(); if (from.name !== undefined) { document.body.classList.remove(`page-${from.name}`); } document.body.classList.add(`page-${to.name}`); window.setTimeout(() => { const getMetaValue = (tag, defaultValue = "") => { const getMeta = (obj, tag) => { const tagHierarchy = tag.split("."); const nearestWithMeta = obj.matched .slice() .reverse() .reduce( (acc, r) => acc || (r.meta && r.meta[tagHierarchy[0]]), null ); if (nearestWithMeta) { let result = nearestWithMeta; for (let i = 1; i < tagHierarchy.length; i++) { result = result[tagHierarchy[i]]; if (!result) break; } if (result !== undefined) return result; } return null; }; const nearestMeta = getMeta(to, tag); if (nearestMeta == null) { const previousMeta = getMeta(from, tag); if (previousMeta == null) { return defaultValue; } return previousMeta; } return nearestMeta; }; updateSEOTags({ title: getMetaValue("title"), description: getMetaValue("description"), keywords: getMetaValue("keywords", []), robots: { index: getMetaValue("robots.index", true), follow: getMetaValue("robots.follow", true), }, url: getMetaValue("url", to.path), image: getMetaValue("image", ""), }); }, 10); window.setTimeout(() => { const autofocusElement = document.querySelector("[autofocus]"); if (autofocusElement) { autofocusElement.focus(); } const hash = window.location.hash; if (hash) { const target = document.querySelector(hash); if (target) { target.scrollIntoView(); } } }, 10); if (applicationStore.pageLoaderTimeout !== 0) { window.clearTimeout(applicationStore.pageLoaderTimeout); applicationStore.pageLoaderTimeout = 0; } const pageLoadingElem = document.getElementById("sm-page-loading"); if (pageLoadingElem !== null) { pageLoadingElem.style.display = "none"; } }); export default router;