mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-07-25 05:49:13 +02:00
31 add page labels and meta information
This commit is contained in:
80
frontend/package-lock.json
generated
80
frontend/package-lock.json
generated
@@ -15,6 +15,7 @@
|
|||||||
"@fullcalendar/vue3": "^6.1.11",
|
"@fullcalendar/vue3": "^6.1.11",
|
||||||
"@tanstack/vue-query": "^5.28.9",
|
"@tanstack/vue-query": "^5.28.9",
|
||||||
"@tanstack/vue-query-devtools": "^5.28.10",
|
"@tanstack/vue-query-devtools": "^5.28.10",
|
||||||
|
"@unhead/vue": "^1.9.10",
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"primeflex": "^3.3.1",
|
"primeflex": "^3.3.1",
|
||||||
@@ -1260,6 +1261,58 @@
|
|||||||
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
|
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@unhead/dom": {
|
||||||
|
"version": "1.9.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.9.10.tgz",
|
||||||
|
"integrity": "sha512-F4sBrmd8kG8MEqcVTGL0Y6tXbJMdWK724pznUzefpZTs1GaVypFikLluaLt4EnICcVhOBSe4TkGrc8N21IJJzQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/schema": "1.9.10",
|
||||||
|
"@unhead/shared": "1.9.10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@unhead/schema": {
|
||||||
|
"version": "1.9.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.9.10.tgz",
|
||||||
|
"integrity": "sha512-3ROh0doKfA7cIcU0zmjYVvNOiJuxSOcjInL+7iOFIxQovEWr1PcDnrnbEWGJsXrLA8eqjrjmhuDqAr3JbMGsLg==",
|
||||||
|
"dependencies": {
|
||||||
|
"hookable": "^5.5.3",
|
||||||
|
"zhead": "^2.2.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@unhead/shared": {
|
||||||
|
"version": "1.9.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.9.10.tgz",
|
||||||
|
"integrity": "sha512-LBXxm/8ahY4FZ0FbWVaM1ANFO5QpPzvaYwjAQhgHANsrqFP2EqoGcOv1CfhdQbxg8vpGXkjI7m0r/8E9d3JoDA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/schema": "1.9.10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@unhead/vue": {
|
||||||
|
"version": "1.9.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-1.9.10.tgz",
|
||||||
|
"integrity": "sha512-Zi65eTU5IIaqqXAVOVJ4fnwJRR751FZIFlzYOjIekf1eNkISy+A4xyz3NIEQWSlXCrOiDNgDhT0YgKUcx5FfHQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/schema": "1.9.10",
|
||||||
|
"@unhead/shared": "1.9.10",
|
||||||
|
"hookable": "^5.5.3",
|
||||||
|
"unhead": "1.9.10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": ">=2.7 || >=3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "5.0.4",
|
"version": "5.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
|
||||||
@@ -2897,6 +2950,11 @@
|
|||||||
"he": "bin/he"
|
"he": "bin/he"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hookable": {
|
||||||
|
"version": "5.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||||
|
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
|
||||||
|
},
|
||||||
"node_modules/human-signals": {
|
"node_modules/human-signals": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
|
||||||
@@ -4338,6 +4396,20 @@
|
|||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/unhead": {
|
||||||
|
"version": "1.9.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/unhead/-/unhead-1.9.10.tgz",
|
||||||
|
"integrity": "sha512-Y3w+j1x1YFig2YuE+W2sER+SciRR7MQktYRHNqvZJ0iUNCCJTS8Z/SdSMUEeuFV28daXeASlR3fy7Ry3O2indg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@unhead/dom": "1.9.10",
|
||||||
|
"@unhead/schema": "1.9.10",
|
||||||
|
"@unhead/shared": "1.9.10",
|
||||||
|
"hookable": "^5.5.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.0.13",
|
"version": "1.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||||
@@ -4789,6 +4861,14 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zhead": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zhead/-/zhead-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/harlan-zw"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
"@fullcalendar/vue3": "^6.1.11",
|
"@fullcalendar/vue3": "^6.1.11",
|
||||||
"@tanstack/vue-query": "^5.28.9",
|
"@tanstack/vue-query": "^5.28.9",
|
||||||
"@tanstack/vue-query-devtools": "^5.28.10",
|
"@tanstack/vue-query-devtools": "^5.28.10",
|
||||||
|
"@unhead/vue": "^1.9.10",
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"primeflex": "^3.3.1",
|
"primeflex": "^3.3.1",
|
||||||
|
2
frontend/public/robots.txt
Normal file
2
frontend/public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
@@ -18,11 +18,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import MenuBar from "./components/MenuBar.vue";
|
import MenuBar from "./components/MenuBar.vue";
|
||||||
import { RouteRecordName, RouterView } from "vue-router";
|
import { RouteRecordName, RouterView, useRoute, useRouter } from "vue-router";
|
||||||
|
import { useHead } from "@unhead/vue";
|
||||||
import CalendarPreview from "./components/CalendarPreview.vue";
|
import CalendarPreview from "./components/CalendarPreview.vue";
|
||||||
import moduleStore from "./store/moduleStore.ts";
|
import moduleStore from "./store/moduleStore.ts";
|
||||||
import { provide, ref } from "vue";
|
import { computed, provide, ref } from "vue";
|
||||||
import { VueQueryDevtools } from "@tanstack/vue-query-devtools";
|
import { VueQueryDevtools } from "@tanstack/vue-query-devtools";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
|
||||||
const disabledPages = [
|
const disabledPages = [
|
||||||
"room-finder",
|
"room-finder",
|
||||||
@@ -36,6 +39,33 @@ const disabledPages = [
|
|||||||
"room-schedule",
|
"room-schedule",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Provide canonical link for SEO
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const baseUri = "https://cal.htwk-leipzig.de"; // could be stored in .env
|
||||||
|
const canonical = computed(() => `${baseUri}${router.resolve(route.name ? { name: route.name } : route).path}`);
|
||||||
|
const title = computed(() => route.meta.label?
|
||||||
|
`HTWKalender - ${t(String(route.meta.label))}`:
|
||||||
|
"HTWKalender"
|
||||||
|
);
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: title,
|
||||||
|
link: [
|
||||||
|
{ rel: "canonical", href: canonical},
|
||||||
|
],
|
||||||
|
meta: [
|
||||||
|
{
|
||||||
|
name: "description",
|
||||||
|
content: "Dein individueller Stundenplan mit Sportevents und Prüfungen. Finde kommende Veranstaltungen oder freie Räume zum Lernen und Arbeiten.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "keywords",
|
||||||
|
content: "HTWK Leipzig, Stundenplan, iCal, freie Räume, Lerngruppen, Sport, Prüfungen",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
const store = moduleStore();
|
const store = moduleStore();
|
||||||
const mobilePage = ref(true);
|
const mobilePage = ref(true);
|
||||||
provide("mobilePage", mobilePage);
|
provide("mobilePage", mobilePage);
|
||||||
|
@@ -102,12 +102,13 @@ function handleDarkModeToggled(isDarkVar: boolean) {
|
|||||||
"
|
"
|
||||||
v-bind="props.action"
|
v-bind="props.action"
|
||||||
@click="navigate"
|
@click="navigate"
|
||||||
|
:href="item.route"
|
||||||
>
|
>
|
||||||
<span :class="item.icon" />
|
<span :class="item.icon" />
|
||||||
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
||||||
</a>
|
</a>
|
||||||
</router-link>
|
</router-link>
|
||||||
<a
|
<span
|
||||||
v-else
|
v-else
|
||||||
:class="
|
:class="
|
||||||
$route.path.includes(item.info)
|
$route.path.includes(item.info)
|
||||||
@@ -115,11 +116,10 @@ function handleDarkModeToggled(isDarkVar: boolean) {
|
|||||||
: 'flex align-items-center'
|
: 'flex align-items-center'
|
||||||
"
|
"
|
||||||
v-bind="props.action"
|
v-bind="props.action"
|
||||||
:href="item.url"
|
|
||||||
>
|
>
|
||||||
<span :class="item.icon" />
|
<span :class="item.icon" />
|
||||||
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
||||||
</a>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #end>
|
<template #end>
|
||||||
<div class="flex align-items-stretch justify-content-center">
|
<div class="flex align-items-stretch justify-content-center">
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
import "source-sans/source-sans-3.css";
|
import "source-sans/source-sans-3.css";
|
||||||
|
|
||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
|
import { createHead } from "@unhead/vue";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import PrimeVue from "primevue/config";
|
import PrimeVue from "primevue/config";
|
||||||
@@ -69,6 +70,9 @@ app.use(VueQueryPlugin, {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const head = createHead();
|
||||||
|
app.use(head);
|
||||||
|
|
||||||
app.use(PrimeVue);
|
app.use(PrimeVue);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(ToastService);
|
app.use(ToastService);
|
||||||
|
@@ -37,46 +37,73 @@ const router = createRouter({
|
|||||||
path: "/",
|
path: "/",
|
||||||
name: "course-selection",
|
name: "course-selection",
|
||||||
component: CourseSelection,
|
component: CourseSelection,
|
||||||
|
meta: {
|
||||||
|
label: "",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/rooms/occupancy",
|
path: "/rooms/occupancy",
|
||||||
name: "room-schedule",
|
name: "room-schedule",
|
||||||
component: RoomFinder,
|
component: RoomFinder,
|
||||||
|
meta: {
|
||||||
|
label: "roomFinderPage.roomSchedule",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/rooms/free",
|
path: "/rooms/free",
|
||||||
name: "free-rooms",
|
name: "free-rooms",
|
||||||
component: FreeRooms,
|
component: FreeRooms,
|
||||||
|
meta: {
|
||||||
|
label: "freeRooms.freeRooms",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/faq",
|
path: "/faq",
|
||||||
name: "faq",
|
name: "faq",
|
||||||
component: Faq,
|
component: Faq,
|
||||||
|
meta: {
|
||||||
|
label: "faq",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/additional-modules",
|
path: "/additional-modules",
|
||||||
name: "additional-modules",
|
name: "additional-modules",
|
||||||
component: AdditionalModules,
|
component: AdditionalModules,
|
||||||
|
meta: {
|
||||||
|
label: "createCalendar",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/edit-additional-modules",
|
path: "/edit-additional-modules",
|
||||||
name: "edit-additional-modules",
|
name: "edit-additional-modules",
|
||||||
component: EditAdditionalModules,
|
component: EditAdditionalModules,
|
||||||
|
meta: {
|
||||||
|
label: "editCalendar",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/edit-calendar",
|
path: "/edit-calendar",
|
||||||
name: "edit-calendar",
|
name: "edit-calendar",
|
||||||
component: EditModules,
|
component: EditModules,
|
||||||
|
meta: {
|
||||||
|
label: "editCalendar",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/calendar-link",
|
path: "/calendar-link",
|
||||||
name: "calendar-link",
|
name: "calendar-link",
|
||||||
component: CalendarLink,
|
component: CalendarLink,
|
||||||
|
meta: {
|
||||||
|
label: "createCalendar"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/edit",
|
path: "/edit",
|
||||||
name: "edit",
|
name: "edit",
|
||||||
component: EditCalendarView,
|
component: EditCalendarView,
|
||||||
|
meta: {
|
||||||
|
label: "editCalendar",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/privacy-policy",
|
path: "/privacy-policy",
|
||||||
@@ -86,6 +113,9 @@ const router = createRouter({
|
|||||||
window.location.href =
|
window.location.href =
|
||||||
"https://www.htwk-leipzig.de/hochschule/kontakt/datenschutzerklaerung/";
|
"https://www.htwk-leipzig.de/hochschule/kontakt/datenschutzerklaerung/";
|
||||||
},
|
},
|
||||||
|
meta: {
|
||||||
|
label: "privacy"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/imprint",
|
path: "/imprint",
|
||||||
@@ -95,11 +125,17 @@ const router = createRouter({
|
|||||||
window.location.href =
|
window.location.href =
|
||||||
"https://www.htwk-leipzig.de/hochschule/kontakt/impressum/";
|
"https://www.htwk-leipzig.de/hochschule/kontakt/impressum/";
|
||||||
},
|
},
|
||||||
|
meta: {
|
||||||
|
label: "imprint"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/rename-modules",
|
path: "/rename-modules",
|
||||||
name: "rename-modules",
|
name: "rename-modules",
|
||||||
component: RenameModules,
|
component: RenameModules,
|
||||||
|
meta: {
|
||||||
|
label: "createCalendar"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@@ -42,18 +42,18 @@ const hasContent = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-column align-items-center mt-0">
|
<heading class="flex flex-column align-items-center mt-0">
|
||||||
<div
|
<div
|
||||||
class="flex align-items-center justify-content-center gap-3 mx-2 mb-4 transition-rolldown"
|
class="flex align-items-center justify-content-center gap-3 mx-2 mb-4 transition-rolldown"
|
||||||
:class="{ 'md:mt-8': hideContent }"
|
:class="{ 'md:mt-8': hideContent }"
|
||||||
>
|
>
|
||||||
<h3 class="text-4xl">
|
<h1 class="text-4xl">
|
||||||
{{ headline }}
|
{{ headline }}
|
||||||
</h3>
|
</h1>
|
||||||
<i v-if="icon" :class="icon" style="font-size: 2rem"></i>
|
<i v-if="icon" :class="icon" style="font-size: 2rem"></i>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subTitle" class="flex justify-content-center">
|
<div v-if="subTitle" class="flex justify-content-center">
|
||||||
<h5 class="text-2xl m-2">{{ subTitle }}</h5>
|
<p class="subtitle text-2xl m-2">{{ subTitle }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap mx-0 gap-2 my-4 w-full lg:w-8">
|
<div class="flex flex-wrap mx-0 gap-2 my-4 w-full lg:w-8">
|
||||||
<slot name="selection" flex-specs="flex-1 m-0"></slot>
|
<slot name="selection" flex-specs="flex-1 m-0"></slot>
|
||||||
@@ -87,11 +87,15 @@ const hasContent = computed(() => {
|
|||||||
>
|
>
|
||||||
<slot name="content"></slot>
|
<slot name="content"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</heading>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.transition-rolldown {
|
.transition-rolldown {
|
||||||
transition: margin-top 0.5s ease-in-out;
|
transition: margin-top 0.5s ease-in-out;
|
||||||
}
|
}
|
||||||
|
.subtitle {
|
||||||
|
font-weight: 100;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Reference in New Issue
Block a user