diff --git a/components/base/ListImageItem.vue b/components/base/ListImageItem.vue index 4a40a1d..fc4ac3b 100644 --- a/components/base/ListImageItem.vue +++ b/components/base/ListImageItem.vue @@ -13,7 +13,7 @@ /> <h1 v-if="itemIndex" - class="text-center text-black text-4xl my-auto absolute bottom-2 left-2" + class="text-center text-black text-4xl! my-auto absolute bottom-2 left-2" style="text-shadow: 2px 2px 4px white" > {{ itemIndex }}. diff --git a/components/base/ListItem.vue b/components/base/ListItem.vue index 2be7a84..3746e9e 100644 --- a/components/base/ListItem.vue +++ b/components/base/ListItem.vue @@ -4,7 +4,7 @@ :class="allowNavigation ? '' : 'pointer-events-none'" :to="`${urlOverwrite ?? $route.path}/${data?.slug}`" > - <h1 v-if="itemIndex" class="min-w-20 w-20 sm:min-w-24 sm:w-24 text-center text-black text-4xl my-auto"> + <h1 v-if="itemIndex" class="min-w-20 w-20 sm:min-w-24 sm:w-24 text-center text-black text-4xl! my-auto"> {{ itemIndex }}. </h1> diff --git a/composables/calculateSitemap.ts b/composables/calculateSitemap.ts new file mode 100644 index 0000000..dbd7f51 --- /dev/null +++ b/composables/calculateSitemap.ts @@ -0,0 +1,71 @@ +import type Article from "../types/collection/article"; +import type Page from "../types/collection/page"; +import type { ComponentTypes } from "../types/component/baseComponent"; +import type SharedList from "../types/component/shared/list"; +import type Global from "../types/single/global"; + +export default async function () { + const { find } = useStrapi(); + + const nuxtApp = useNuxtApp(); + const { navbar, footer } = await nuxtApp.runWithContext(() => provideGlobal()); + const { data: page_res } = await nuxtApp.runWithContext(() => + useAsyncData("sitemap_pages", () => + find<Page>("pages", { + filters: { + ref_only_access: false, + }, + }) + ) + ); + const pages = page_res.value?.data ?? []; + + const accessableURLs: Array<{ path: string; origin: string; document?: string; hasCollection?: boolean }> = []; + + for (const element of navbar?.navbar_items ?? []) { + if (!element.default_active_child) { + accessableURLs.push({ + path: element.URL.startsWith("/") ? element.URL : `/${element.URL}`, + origin: "navbar", + document: element?.page?.documentId, + hasCollection: element.page.content.filter((c: ComponentTypes) => c.__component == "shared.list").length != 0, + }); + } + for (const subelement of element.navbar_sub_items) { + let url = `${element.URL}/${subelement.URL}`; + accessableURLs.push({ + path: url.startsWith("/") ? url : `/${url}`, + origin: "navbar", + document: subelement?.page?.documentId, + hasCollection: + subelement?.page?.content.filter((c: ComponentTypes) => c.__component == "shared.list").length != 0, + }); + } + } + + for (const element of pages) { + let url = element.slug.replaceAll("~", "/"); + if (!accessableURLs.find((a) => a.path == url)) { + accessableURLs.push({ + path: url.startsWith("/") ? url : `/${url}`, + origin: "page", + document: element.documentId, + hasCollection: element.content.filter((c: ComponentTypes) => c.__component == "shared.list").length != 0, + }); + } + } + + for (const element of footer?.links ?? []) { + let url = element.URL.startsWith("/") ? element.URL : `/${element.URL}`; + if (!accessableURLs.find((a) => a.path == url) && !element.URL.startsWith("http")) { + accessableURLs.push({ + path: url, + origin: "footer", + document: undefined, + hasCollection: undefined, + }); + } + } + + return accessableURLs; +} diff --git a/pages/[...slug].vue b/pages/[...slug].vue index ea7b66c..da75b80 100644 --- a/pages/[...slug].vue +++ b/pages/[...slug].vue @@ -1,5 +1,4 @@ <template> - {{ collectionDetail }} <NuxtLayout name="default"> <NotFound v-if="notFound" /> <ContentBuilder v-else-if="showContentBuilder" :hero="page?.hero" :content="page?.content" /> @@ -20,25 +19,38 @@ const { } = useRoute(); const { findOne, find } = useStrapi(); -const { navbar } = await provideGlobal(); -const navbar_items = computed(() => { - return navbar?.navbar_items ?? []; +const sitemap = await calculateSitemap(); + +const detail = ref<Article | Operation | Event | Vehicle | undefined>(undefined); + +const activePath = computed(() => { + return "/" + (Array.isArray(params) ? params.join("/") : params); }); -const navbar_sub_items = computed(() => { - return navbar_items.value.find((ni) => ni.URL == params[0])?.navbar_sub_items ?? []; +const activePageBySitemap = computed(() => { + return sitemap.find((s) => s.path == activePath.value); }); -const active_item = computed(() => { - return navbar_items.value.find((ni) => ni.URL == params[0])?.page; -}); -const active_sub_item = computed(() => { - return navbar_sub_items.value.find((si) => si.URL == params[1])?.page; -}); -const active_page_id = computed(() => { - return active_sub_item.value?.documentId ?? active_item.value?.documentId ?? ""; +const similarestPage = computed(() => { + return sitemap.reduce( + (bestMatch, current) => { + const currentMatchLength = current.path + .split("/") + .filter((segment, index) => segment != "" && segment == activePath.value.split("/")[index]).length; + const bestMatchLength = bestMatch.path + .split("/") + .filter((segment, index) => segment != "" && segment == activePath.value.split("/")[index]).length; + + if (currentMatchLength > bestMatchLength) { + return current; + } else { + return bestMatch; + } + }, + { path: "", origin: "", hasCollection: false } + ); }); const { data: pages } = await useAsyncData("pages", () => - findOne<Page | Array<Page>>("pages", active_page_id.value, { + findOne<Page | Array<Page>>("pages", similarestPage.value?.document, { populate: { populate: "*", content: { @@ -49,7 +61,9 @@ const { data: pages } = await useAsyncData("pages", () => }, }, filters: { - ...(active_page_id.value == "" ? { slug: params.join("_"), ref_only_access: false } : {}), + ...(!similarestPage.value?.document + ? { slug: Array.isArray(params) ? params.join("~") : params, ref_only_access: false } + : {}), }, }) ); @@ -57,12 +71,25 @@ const page = computed(() => { return Array.isArray(pages.value?.data) ? pages.value.data[0] : pages.value?.data; }); -let detail = ref<Article | Operation | Event | Vehicle | undefined>(undefined); -const collectionDetail = computed(() => { - if (!active_sub_item.value) return params[1]; - return params[2]; +const isCollectionDetail = computed(() => { + return activePath.value != similarestPage.value.path && similarestPage.value.hasCollection; }); -if (collectionDetail.value) { + +const notFound = computed(() => { + if (isCollectionDetail.value && detail.value) return !detail.value; + else + return ( + !page.value || + (page.value && !(page.value.content.length != 0 || (page.value.hero.title && page.value.hero.banner))) + ); +}); + +const showContentBuilder = computed(() => { + if (isCollectionDetail.value && detail.value) return !detail.value; + else return page.value && (page.value.content.length != 0 || (page.value.hero.title && page.value.hero.banner)); +}); + +if (isCollectionDetail) { let collectionOfDetail = [ ...new Set( page.value?.content @@ -77,7 +104,7 @@ if (collectionDetail.value) { find<Article | Operation | Event | Vehicle>(element ?? "", { populate: "*", filters: { - slug: params[2] ?? params[1], + slug: activePath.value.substring(activePath.value.lastIndexOf("/") + 1), }, }) ); @@ -87,14 +114,4 @@ if (collectionDetail.value) { } } } - -const notFound = computed(() => { - if (collectionDetail.value && detail.value) return !detail.value; - else return active_page_id.value == "" && !page.value?.content && !page.value?.hero; -}); - -const showContentBuilder = computed(() => { - if (collectionDetail.value && detail.value) return !detail.value; - else return !!page.value?.content || !!page.value?.hero; -}); </script> diff --git a/pages/sitemap.vue b/pages/sitemap.vue new file mode 100644 index 0000000..33d79e4 --- /dev/null +++ b/pages/sitemap.vue @@ -0,0 +1,17 @@ +<template> + <NuxtLayout name="default"> + <div class="min-h-[calc(100vh-9rem)] w-full"> + <div class="container mx-auto py-12 px-2 min-h-[50vh] flex flex-col gap-2"> + <NuxtLink v-for="item in sitemap" :key="item.path" :to="`${item.path}`"> + {{ item.path }} {{ item.hasCollection ? "(...)" : "" }} + </NuxtLink> + </div> + </div> + </NuxtLayout> +</template> + +<script setup lang="ts"> +import calculateSitemap from "~/composables/calculateSitemap"; + +const sitemap = await calculateSitemap(); +</script>