mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender-pwa.git
synced 2025-08-09 21:27:47 +02:00
Merge remote-tracking branch 'htwk-org/development'
# Conflicts: # backend/go.mod # frontend/index.html # frontend/package-lock.json # frontend/package.json # frontend/public/themes/lara-dark-blue/theme.css # frontend/public/themes/lara-dark-blue/theme.css.map # frontend/public/themes/lara-light-blue/theme.css # frontend/public/themes/lara-light-blue/theme.css.map # frontend/src/App.vue # frontend/src/components/DarkModeSwitcher.vue # frontend/src/i18n/index.ts # frontend/src/main.ts # frontend/src/router/index.ts # frontend/src/view/CalendarLink.vue # frontend/src/view/edit/EditCalendar.vue # frontend/vite.config.ts # reverseproxy.conf # reverseproxy.local.conf # services/data-manager/main.go # services/data-manager/model/roomOccupancyModel.go # services/data-manager/service/addRoute.go # services/data-manager/service/addSchedule.go # services/data-manager/service/db/dbGroups.go # services/data-manager/service/feed/feedFunctions.go # services/data-manager/service/fetch/sport/sportFetcher.go # services/data-manager/service/fetch/v1/fetchSeminarEventService.go # services/data-manager/service/fetch/v1/fetchSeminarGroupService.go # services/data-manager/service/fetch/v2/fetcher.go # services/data-manager/service/functions/filter.go # services/data-manager/service/functions/filter_test.go # services/data-manager/service/functions/time/parse.go # services/data-manager/service/room/roomService.go # services/data-manager/service/room/roomService_test.go # services/go.sum # services/ical/service/connector/grpc/client.go
This commit is contained in:
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
@@ -10,6 +10,7 @@ lerna-debug.log*
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
.vite-ssg-temp
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@@ -22,9 +22,10 @@
|
||||
rel="stylesheet"
|
||||
href="/themes/lara-light-blue/theme.css"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -50,7 +50,7 @@ http {
|
||||
index index.html index.htm;
|
||||
|
||||
#necessary to display vue subpage
|
||||
try_files $uri $uri/ /index.html;
|
||||
try_files $uri $uri.html $uri/ /index.html;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
|
6098
frontend/package-lock.json
generated
6098
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"build": "vue-tsc && vite-ssg build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
|
||||
"lint-no-fix": "eslint --ext .js,.vue --ignore-path .gitignore src",
|
||||
@@ -20,7 +20,6 @@
|
||||
"@fullcalendar/timegrid": "^6.1.13",
|
||||
"@fullcalendar/vue3": "^6.1.13",
|
||||
"@tanstack/vue-query": "^5.37.1",
|
||||
"@tanstack/vue-query-devtools": "^5.37.1",
|
||||
"@types/ical": "^0.8.3",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"bson": "^5.5.1",
|
||||
@@ -39,20 +38,29 @@
|
||||
"vue-router": "^4.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@types/node": "^20.12.12",
|
||||
"@vitejs/plugin-basic-ssl": "^1.1.0",
|
||||
"@unhead/vue": "^1.9.15",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@tanstack/vue-query-devtools": "^5.28.10",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-vue": "^9.26.0",
|
||||
"prettier": "3.2.1",
|
||||
"sass": "^1.77.2",
|
||||
"sass-loader": "^13.3.3",
|
||||
"terser": "^5.31.0",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.11",
|
||||
"vite-plugin-pwa": "^0.20.0",
|
||||
"vite-plugin-vue-devtools": "^7.3.1",
|
||||
"vite-ssg": "^0.23.7",
|
||||
"vite-ssg-sitemap": "^0.7.1",
|
||||
"vitest": "^1.6.0",
|
||||
"vue-router": "^4.4.0",
|
||||
"vue-tsc": "^1.8.27"
|
||||
}
|
||||
}
|
||||
|
BIN
frontend/public/img/banner-image.png
Executable file
BIN
frontend/public/img/banner-image.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
BIN
frontend/public/promo/htwkarte.png
Normal file
BIN
frontend/public/promo/htwkarte.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
BIN
frontend/public/promo/mensa.png
Normal file
BIN
frontend/public/promo/mensa.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -18,15 +18,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MenuBar from "./components/MenuBar.vue";
|
||||
import { RouteRecordName, RouterView } from "vue-router";
|
||||
import { RouteRecordName, RouterView, useRoute, useRouter } from "vue-router";
|
||||
import { useHead, useServerHead, useServerSeoMeta } from "@unhead/vue";
|
||||
import CalendarPreview from "./components/CalendarPreview.vue";
|
||||
import moduleStore from "./store/moduleStore.ts";
|
||||
import { onMounted, provide, ref } from "vue";
|
||||
import { computed, provide, ref } from "vue";
|
||||
import { VueQueryDevtools } from "@tanstack/vue-query-devtools";
|
||||
import settingsStore from "@/store/settingsStore.ts";
|
||||
import { setTheme } from "@/helpers/theme.ts";
|
||||
import { usePrimeVue } from "primevue/config";
|
||||
const primeVue = usePrimeVue();
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const disabledPages = [
|
||||
"room-finder",
|
||||
@@ -40,6 +44,56 @@ const disabledPages = [
|
||||
"room-schedule"
|
||||
];
|
||||
|
||||
// Provide canonical link for SEO
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const domain = "cal.htwk-leipzig.de"
|
||||
provide('domain', domain);
|
||||
const baseUri = "https://" + domain;
|
||||
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"
|
||||
);
|
||||
const description = computed(() => route.meta.description?
|
||||
t(String(route.meta.description)):
|
||||
t("description")
|
||||
);
|
||||
|
||||
useHead({
|
||||
title: title,
|
||||
link: [
|
||||
{ rel: "canonical", href: canonical},
|
||||
],
|
||||
meta: [
|
||||
{ name: "description", content: description},
|
||||
{ property: "og:description", content: description},
|
||||
]
|
||||
});
|
||||
|
||||
// SEO optimization
|
||||
useServerHead({
|
||||
title: title
|
||||
});
|
||||
useServerSeoMeta(
|
||||
{
|
||||
title: title,
|
||||
description: description,
|
||||
keywords: "HTWK Leipzig, Stundenplan, iCal, freie Räume, Lerngruppen, Sport, Prüfungen",
|
||||
// openGraph
|
||||
ogTitle: title,
|
||||
ogDescription: description,
|
||||
ogImage: `${baseUri}/img/banner-image.png`,
|
||||
ogImageType: "image/png",
|
||||
ogLocale: "de_DE",
|
||||
ogUrl: canonical,
|
||||
// twitter
|
||||
twitterCard: "summary_large_image",
|
||||
twitterSite: "@HTWKLeipzig",
|
||||
}
|
||||
);
|
||||
|
||||
const store = moduleStore();
|
||||
const mobilePage = ref(true);
|
||||
provide("mobilePage", mobilePage);
|
||||
@@ -49,11 +103,12 @@ const isDisabled = (routeName: RouteRecordName | null | undefined) => {
|
||||
};
|
||||
|
||||
const updateMobile = () => {
|
||||
if (import.meta.env.SSR) return;
|
||||
mobilePage.value = window.innerWidth <= 992;
|
||||
};
|
||||
|
||||
updateMobile();
|
||||
window.addEventListener("resize", updateMobile);
|
||||
if (!import.meta.env.SSR)window.addEventListener("resize", updateMobile);
|
||||
|
||||
const settings = settingsStore;
|
||||
const emit = defineEmits(["dark-mode-toggled"]);
|
||||
@@ -71,7 +126,7 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<MenuBar />
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<RouterView v-slot="{ Component, route }" class="mb-8">
|
||||
<transition mode="out-in" name="scale">
|
||||
<div :key="route.name ?? ''" class="origin-near-top">
|
||||
<component :is="Component" />
|
||||
|
@@ -17,8 +17,11 @@
|
||||
import { Module } from "../model/module.ts";
|
||||
|
||||
export async function createIndividualFeed(modules: Module[]): Promise<string> {
|
||||
if (import.meta.env.SSR) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
const response = await fetch("/api/createFeed", {
|
||||
const response = await fetch("/api/feed", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -62,6 +65,9 @@ export async function saveIndividualFeed(
|
||||
token: string,
|
||||
modules: Module[],
|
||||
): Promise<string> {
|
||||
if (import.meta.env.SSR) {
|
||||
return "";
|
||||
}
|
||||
await fetch("/api/collections/feeds/records/" + token, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
@@ -81,6 +87,9 @@ export async function saveIndividualFeed(
|
||||
}
|
||||
|
||||
export async function deleteIndividualFeed(token: string): Promise<void> {
|
||||
if (import.meta.env.SSR) {
|
||||
return;
|
||||
}
|
||||
await fetch("/api/feed?token=" + token, {
|
||||
method: "DELETE",
|
||||
})
|
||||
|
@@ -19,6 +19,9 @@
|
||||
import { Module } from "../model/module.ts";
|
||||
|
||||
export async function fetchCourse(): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const courses: string[] = [];
|
||||
await fetch("/api/courses")
|
||||
.then((response) => {
|
||||
@@ -39,6 +42,9 @@ export async function fetchCourse(): Promise<string[]> {
|
||||
export async function fetchCourseBySemester(
|
||||
semester: string,
|
||||
): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const courses: string[] = [];
|
||||
await fetch("/api/courses/events?semester=" + semester)
|
||||
.then((response) => {
|
||||
@@ -60,6 +66,9 @@ export async function fetchModulesByCourseAndSemester(
|
||||
course: string,
|
||||
semester: string,
|
||||
): Promise<Module[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const modules: Module[] = [];
|
||||
await fetch("/api/course/modules?course=" + course + "&semester=" + semester)
|
||||
.then((response) => {
|
||||
@@ -86,6 +95,9 @@ export async function fetchModulesByCourseAndSemester(
|
||||
}
|
||||
|
||||
export async function fetchAllModules(): Promise<Module[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const modules: Module[] = [];
|
||||
await fetch("/api/modules")
|
||||
.then((response) => {
|
||||
|
36
frontend/src/api/fetchEvents.ts
Normal file
36
frontend/src/api/fetchEvents.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// function to fetch course data from the API
|
||||
|
||||
export async function fetchEventTypes(): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const eventTypes: string[] = [];
|
||||
await fetch("/api/events/types")
|
||||
.then((response) => {
|
||||
return response.json() as Promise<string[]>;
|
||||
})
|
||||
.then((responseModules: string[]) => {
|
||||
responseModules.forEach((eventType: string) => {
|
||||
eventTypes.push(
|
||||
eventType,
|
||||
);
|
||||
});
|
||||
});
|
||||
return eventTypes;
|
||||
}
|
@@ -17,7 +17,10 @@
|
||||
import { Module } from "../model/module";
|
||||
|
||||
export async function fetchModule(module: Module): Promise<Module> {
|
||||
// request to the backend on /api/module with query parameters name as the module name
|
||||
if (import.meta.env.SSR) {
|
||||
return new Module("", "", "", "", "", "", "", false, []);
|
||||
}
|
||||
// request to the data-manager on /api/module with query parameters name as the module name
|
||||
const request = new Request("/api/module?uuid=" + module.uuid);
|
||||
|
||||
return await fetch(request)
|
||||
|
@@ -17,6 +17,9 @@
|
||||
import { AnonymizedEventDTO } from "../model/event.ts";
|
||||
|
||||
export async function fetchRoom(): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const rooms: string[] = [];
|
||||
await fetch("/api/rooms")
|
||||
.then((response) => {
|
||||
@@ -33,6 +36,9 @@ export async function fetchEventsByRoomAndDuration(
|
||||
from_date: string,
|
||||
to_date: string,
|
||||
): Promise<AnonymizedEventDTO[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const events: AnonymizedEventDTO[] = [];
|
||||
await fetch(
|
||||
"/api/schedule?room=" + room + "&from=" + from_date + "&to=" + to_date,
|
||||
|
@@ -18,7 +18,10 @@ import { Module } from "../model/module";
|
||||
import { Calendar } from "../model/calendar";
|
||||
|
||||
export async function getCalender(token: string): Promise<Module[]> {
|
||||
const request = new Request("/api/collections/feeds/records/" + token, {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const request = new Request("/api/feeds/records/" + token, {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
|
@@ -14,11 +14,14 @@
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// load free rooms as a list of strings form the backend
|
||||
// load free rooms as a list of strings form the data-manager
|
||||
export async function requestFreeRooms(
|
||||
from: string,
|
||||
to: string,
|
||||
): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const rooms: string[] = [];
|
||||
await fetch("/api/rooms/free?from=" + from + "&to=" + to)
|
||||
.then((response) => {
|
||||
|
@@ -27,9 +27,10 @@ import {
|
||||
DataTableRowUnselectEvent,
|
||||
} from "primevue/datatable";
|
||||
import { useDialog } from "primevue/usedialog";
|
||||
import router from "../router";
|
||||
import { router } from "@/main";
|
||||
import { fetchModule } from "../api/fetchModule.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { fetchEventTypes } from "../api/fetchEvents.ts";
|
||||
|
||||
const dialog = useDialog();
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
@@ -39,6 +40,9 @@ if (store.isEmpty()) {
|
||||
router.replace("/");
|
||||
}
|
||||
|
||||
const eventTypes: Ref<string[]> = ref([]);
|
||||
|
||||
|
||||
const mobilePage = inject("mobilePage") as Ref<boolean>;
|
||||
const filters = ref({
|
||||
course: {
|
||||
@@ -51,7 +55,7 @@ const filters = ref({
|
||||
},
|
||||
eventType: {
|
||||
value: null,
|
||||
matchMode: FilterMatchMode.CONTAINS,
|
||||
matchMode: FilterMatchMode.IN,
|
||||
},
|
||||
prof: {
|
||||
value: null,
|
||||
@@ -63,7 +67,7 @@ const loadedModules: Ref<Module[]> = ref(new Array(10));
|
||||
|
||||
const loadingData = ref(true);
|
||||
|
||||
onMounted(() => {
|
||||
onMounted( () => {
|
||||
fetchAllModules()
|
||||
.then(
|
||||
(data) =>
|
||||
@@ -74,6 +78,10 @@ onMounted(() => {
|
||||
.finally(() => {
|
||||
loadingData.value = false;
|
||||
});
|
||||
|
||||
fetchEventTypes().then((data) => {
|
||||
eventTypes.value = data;
|
||||
});
|
||||
});
|
||||
|
||||
const ModuleInformation = defineAsyncComponent(
|
||||
@@ -184,16 +192,20 @@ function unselectModule(event: DataTableRowUnselectEvent) {
|
||||
</Column>
|
||||
<Column
|
||||
field="eventType"
|
||||
filter-field="eventType"
|
||||
:filter-menu-style="{ width: '10rem' }"
|
||||
style="min-width: 10rem"
|
||||
:header="$t('additionalModules.eventType')"
|
||||
:show-clear-button="false"
|
||||
:show-filter-menu="false"
|
||||
>
|
||||
<template #filter="{ filterModel, filterCallback }">
|
||||
<InputText
|
||||
<MultiSelect
|
||||
v-model="filterModel.value"
|
||||
type="text"
|
||||
:options="eventTypes"
|
||||
class="p-column-filter max-w-10rem"
|
||||
@input="filterCallback()"
|
||||
style="min-width: 10rem"
|
||||
@change="filterCallback()"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="loadingData" #body>
|
||||
|
@@ -110,13 +110,14 @@ const items = computed(() => [
|
||||
"
|
||||
v-bind="props.action"
|
||||
@click="navigate"
|
||||
:href="item.route"
|
||||
>
|
||||
<span :class="item.icon" />
|
||||
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
||||
</a>
|
||||
</router-link>
|
||||
<a
|
||||
v-else
|
||||
v-else-if="item.url"
|
||||
:class="
|
||||
$route.path.includes(item.info)
|
||||
? 'flex align-items-center active'
|
||||
@@ -128,6 +129,18 @@ const items = computed(() => [
|
||||
<span :class="item.icon" />
|
||||
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
||||
</a>
|
||||
<span
|
||||
v-else
|
||||
:class="
|
||||
$route.path.includes(item.info)
|
||||
? 'flex align-items-center active'
|
||||
: 'flex align-items-center'
|
||||
"
|
||||
v-bind="props.action"
|
||||
>
|
||||
<span :class="item.icon" />
|
||||
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<template #end>
|
||||
<div class="flex align-items-stretch justify-content-center">
|
||||
|
@@ -26,7 +26,7 @@ import { CalendarOptions, DatesSetArg, EventInput } from "@fullcalendar/core";
|
||||
import { fetchEventsByRoomAndDuration } from "../api/fetchRoom.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import allLocales from "@fullcalendar/core/locales-all";
|
||||
import router from "@/router";
|
||||
import { router } from "@/main";
|
||||
import { formatYearMonthDay } from "@/helpers/dates";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { watch } from "vue";
|
||||
|
@@ -28,7 +28,7 @@ function setup() {
|
||||
_i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: settingsStore().locale,
|
||||
fallbackLocale: "en",
|
||||
fallbackLocale: "de",
|
||||
messages: {
|
||||
en,
|
||||
de,
|
||||
|
@@ -8,6 +8,7 @@
|
||||
"faq": "FAQ",
|
||||
"imprint": "Impressum",
|
||||
"privacy": "Datenschutz",
|
||||
"description": "Dein individueller Stundenplan mit Sportevents und Prüfungen. Finde kommende Veranstaltungen oder freie Räume zum Lernen und Arbeiten.",
|
||||
"english": "Englisch",
|
||||
"german": "Deutsch",
|
||||
"japanese": "Japanisch",
|
||||
@@ -29,6 +30,7 @@
|
||||
"roomSchedule": "Raumbelegung",
|
||||
"headline": "Raumbelegung",
|
||||
"detail": "Bitte wähle einen Raum aus, um die Belegung einzusehen",
|
||||
"description": "Möchtest du schnell checken, ob ein Raum frei ist? Hier kannst du die Belegung der Räume an der HTWK Leipzig einsehen.",
|
||||
"dropDownSelect": "Bitte wähle einen Raum aus",
|
||||
"noRoomsAvailable": "Keine Räume verfügbar",
|
||||
"available": "verfügbar",
|
||||
@@ -38,6 +40,7 @@
|
||||
"freeRooms": {
|
||||
"freeRooms": "Freie Räume",
|
||||
"detail": "Bitte wähle einen Zeitraum aus, um alle Räume ohne Belegung anzuzeigen.",
|
||||
"description": "Freier Lerngruppenraum gesucht? Hier kannst du alle freien Räume in einem bestimmten Zeitraum an der HTWK Leipzig einsehen.",
|
||||
"searchByRoom": "Suche nach Räumen",
|
||||
"pleaseSelectDate": "Bitte wähle ein Datum aus",
|
||||
"room": "Raum",
|
||||
@@ -54,7 +57,7 @@
|
||||
},
|
||||
"moduleInformation": {
|
||||
"course": "Kurs",
|
||||
"person": "Dozent",
|
||||
"person": "Dozent*in",
|
||||
"semester": "Semester",
|
||||
"module": "Modul",
|
||||
"notes": "Hinweis",
|
||||
@@ -76,6 +79,7 @@
|
||||
}
|
||||
},
|
||||
"editCalendarView": {
|
||||
"description": "Mit deinem Token kannst du deinen HTWKalender jederzeit bearbeiten und sogar ins nächste Semester mitnehmen",
|
||||
"error": "Fehler",
|
||||
"invalidToken": "Ungültiger Token",
|
||||
"headline": "Bearbeite deinen HTWKalender",
|
||||
@@ -114,7 +118,8 @@
|
||||
"to": " bis ",
|
||||
"of": " von insgesamt "
|
||||
},
|
||||
"eventType": "Ereignistyp"
|
||||
"eventType": "Ereignistyp",
|
||||
"scrollToTop": "Nach oben scrollen"
|
||||
},
|
||||
"renameModules": {
|
||||
"reminder": "Erinnerung",
|
||||
@@ -157,17 +162,19 @@
|
||||
},
|
||||
"faqView": {
|
||||
"headline": "Fragen und Antworten",
|
||||
"description": "Falls du Fragen zum HTWKalender hast, findest du hier Antworten auf häufig gestellte Fragen und unsere Kontaktdaten.",
|
||||
"firstQuestion": "Wie funktioniert das Kalender erstellen mit dem HTWKalender?",
|
||||
"firstAnswer": "Die Webseite ermöglicht es deinen HTWK-Stundenplan in eines deiner bevorzugten Kalender-Verwaltungs-Programme (Outlook, Google Kalender, etc.) einzubinden. ",
|
||||
"secondQuestion": "Wie genau funktioniert das alles?",
|
||||
"secondAnswer": "Du wählst deinen Studiengang und das gewünschte Semester aus, danach kannst du aus den dazugehörigen Modulen wählen. Dabei kannst du einzelne Module an/abwählen wie \"Feiertage\" oder Wahlpflichtmodule, die du nicht belegst. Im letzten Schritt wird dir der Link mit dem entsprechenden Token für den von dir erstellenten Kalenders angezeigt. Mit diesem Link kannst du den Kalender abonnieren oder herunterladen. ",
|
||||
"secondAnswer": "Du wählst deinen Studiengang und das gewünschte Semester aus, danach kannst du aus den dazugehörigen Modulen wählen. Dabei kannst du einzelne Module an/abwählen wie \"Feiertage\" oder Wahlpflichtmodule, die du nicht belegst. Im letzten Schritt wird dir der Link mit dem entsprechenden Token für den von dir erstellten Kalenders angezeigt. Mit diesem Link kannst du den Kalender abonnieren oder herunterladen. ",
|
||||
"thirdQuestion": "Wie kann ich den Kalender abonnieren?",
|
||||
"thirdAnswer": {
|
||||
"tabTitle": "Google Calendar",
|
||||
"google": {
|
||||
"first": "Erstelle deinen Kalender und kopiere den Link.",
|
||||
"second": "Bei Google Kalender selbst hast du in der linken Seitenleiste eine Sektion namens \"Weitere Kalender\". Dort klickst du das kleine Pfeil-Icon rechts neben dem Schritzug. Im daraufhin erscheinenden Menü gibt es einen Punkt \"Über URL hinzufügen\", den du anklicken musst. ",
|
||||
"third": "Füge den kopierten Kalenderlink ein, klicke auf \"Kalender hinzufügen\" und du bist fertig. "
|
||||
"third": "Füge den kopierten Kalenderlink ein, klicke auf \"Kalender hinzufügen\" und du bist fertig. ",
|
||||
"fourth": "Der Kalender sollte sich nun in deiner Kalenderübersicht befinden und sich automatisch aktualisieren. Falls nicht, lade die Seite neu um den Kalender zu aktualisieren, oder überprüfe die Einstellungen des Kalenders. "
|
||||
},
|
||||
"microsoft_outlook": {
|
||||
"title": "Mircosoft Outlook",
|
||||
@@ -243,9 +250,23 @@
|
||||
"seventhQuestion": "Wie lange ist mein Stundenplan bzw. der Link dorthin gültig?",
|
||||
"seventhAnswer": "Studenpläne sind erstmal nur für die ausgewählten Semester gültig, da durch Wahlpflichtmodule oder deine Planung sich Veränderungen ergeben können. Der Link ist jedoch unbegrenzt gültig und du kannst zu jedem Zeitpunkt deinen Stundenplan aktualisieren.",
|
||||
"eighthQuestion": "Preis und Entwicklung?",
|
||||
"eighthAnswer": "Die Entwicklung soll als aktives Git Projekt auch durch die Community verwaltet werden. Eine freie Version des HTWKalenders wird intern auf den Servern des FSR IMN gehostet und ist für alle HTWK-Studenten kostenlos.",
|
||||
"eighthAnswer": "Die Entwicklung soll als aktives Git Projekt durch die Community verwaltet werden. Eine freie Version des HTWKalenders wird intern auf den Servern des FSR IM gehostet und ist für alle HTWK-Studierenden kostenlos.",
|
||||
"ninthQuestion": "Wo kann ich den Quellcode einsehen und mitwirken?",
|
||||
"ninthAnswer": "Wenn du dich für die Entwicklung und den Quelltext interessierst, kannst du jederzeit als HTWK-Student daran mitarbeiten. Quelltext und weitere Informationen findest du im ",
|
||||
"ninthAnswer": "Wenn du dich für die Entwicklung und den Quelltext interessierst, kannst du jederzeit als HTWK-Student*in daran mitarbeiten. Quelltext und weitere Informationen findest du im ",
|
||||
"crossPromoQuestion": "Weitere studentische Projekte.",
|
||||
"crossPromo": {
|
||||
"teaser": "Der HTWKalender arbeitet mit anderen studentischen Projekten zusammen, um dir das Studium zu erleichtern. Vermutlich gibt es noch mehr, als die hier gelisteten. Schau doch mal dort vorbei!",
|
||||
"mensa": {
|
||||
"title": "HTWK Mensa Mate",
|
||||
"description": "Finde den Sitzplatz deiner Kommilitonen in der HTWK Mensa.",
|
||||
"link": "https://mensa.heylinus.de/"
|
||||
},
|
||||
"htwkarte": {
|
||||
"title": "HTWKarte",
|
||||
"description": "Finde dich auf dem Campus zurecht und suche nach Räumen. (in Entwicklung)",
|
||||
"link": "https://htwkarte.de/"
|
||||
}
|
||||
},
|
||||
"notFound": "Nicht gefunden, wonach du suchst?",
|
||||
"contact": "Kontakt aufnehmen"
|
||||
},
|
||||
|
@@ -8,6 +8,7 @@
|
||||
"faq": "faq",
|
||||
"imprint": "imprint",
|
||||
"privacy": "privacy",
|
||||
"description": "Your individual timetable with sports events and exams. Find upcoming events or free rooms for studying and working.",
|
||||
"english": "English",
|
||||
"german": "German",
|
||||
"japanese": "Japanese",
|
||||
@@ -29,6 +30,7 @@
|
||||
"roomSchedule": "room occupancy",
|
||||
"headline": "room occupancy plan",
|
||||
"detail": "Please select a room to view the occupancy.",
|
||||
"description": "Would you like to quickly check whether a room is available? Here you can see the occupancy of the rooms at HTWK Leipzig.",
|
||||
"dropDownSelect": "please select a room",
|
||||
"noRoomsAvailable": "no rooms listed",
|
||||
"available": "available",
|
||||
@@ -38,6 +40,7 @@
|
||||
"freeRooms": {
|
||||
"freeRooms": "free rooms",
|
||||
"detail": "Please select a time period to display rooms that have no occupancy.",
|
||||
"description": "Looking for a free study group room? Here you can see all available rooms at HTWK Leipzig in a specific period.",
|
||||
"searchByRoom": "search by room",
|
||||
"pleaseSelectDate": "please select a date",
|
||||
"room": "room",
|
||||
@@ -76,6 +79,7 @@
|
||||
}
|
||||
},
|
||||
"editCalendarView": {
|
||||
"description": "With your token, you can edit your HTW calendar at any time and even take it with you into the next semester",
|
||||
"error": "error",
|
||||
"invalidToken": "invalid token",
|
||||
"headline": "edit your HTWKalender",
|
||||
@@ -114,7 +118,8 @@
|
||||
"to": " to ",
|
||||
"of": " of "
|
||||
},
|
||||
"eventType": "event type"
|
||||
"eventType": "event type",
|
||||
"scrollToTop": "scroll to top"
|
||||
},
|
||||
"renameModules": {
|
||||
"reminder": "reminder",
|
||||
@@ -157,6 +162,7 @@
|
||||
},
|
||||
"faqView": {
|
||||
"headline": "faq",
|
||||
"description": "If you have any questions about the HTWKalender, you will find answers to frequently asked questions and our contact details here.",
|
||||
"firstQuestion": "How does calendar creation work with HTWKalender?",
|
||||
"firstAnswer": "The website allows you to integrate your HTWK timetable into one of your preferred calendar management programs (Outlook, Google Calendar, etc.).",
|
||||
"secondQuestion": "How does it all work exactly?",
|
||||
@@ -167,7 +173,8 @@
|
||||
"google": {
|
||||
"first": "Create your calendar and copy the link.",
|
||||
"second": "In Google Calendar itself, in the left sidebar, there is a section called 'Other calendars.' There, click the small arrow icon to the right of the text. In the menu that appears, there is an option 'Add by URL' that you need to click.",
|
||||
"third": "Paste the copied calendar link, click 'Add Calendar,' and you're done."
|
||||
"third": "Paste the copied calendar link, click 'Add Calendar,' and you're done.",
|
||||
"fourth": "The calendar should now be in your calendar overview and update automatically. If not, reload the page to refresh the calendar or check the calendar settings."
|
||||
},
|
||||
"microsoft_outlook": {
|
||||
"title": "Microsoft Outlook",
|
||||
@@ -246,6 +253,20 @@
|
||||
"eighthAnswer": "The development should also be managed by the community as an active Git project. A free version of the HTW calendar is hosted internally on the servers of the FSR IMN and is free of charge for all HTWK students.",
|
||||
"ninthQuestion": "Where could i find the source code?",
|
||||
"ninthAnswer": "If you want to contribute, you can do so at any time if you are a HTWK student. The source code is available on ",
|
||||
"crossPromoQuestion": "More student projects.",
|
||||
"crossPromo": {
|
||||
"teaser": "The HTWKalender collaborates with other student projects to make your studies easier. There are probably more than those listed here. Check them out!",
|
||||
"mensa": {
|
||||
"title": "HTWK Mensa Mate",
|
||||
"description": "Find the seating location of your fellow students in the HTWK Mensa Academica.",
|
||||
"link": "https://mensa.heylinus.de/"
|
||||
},
|
||||
"htwkarte": {
|
||||
"title": "HTWKarte",
|
||||
"description": "Find your way around the campus and search for rooms. (in development)",
|
||||
"link": "https://htwkarte.de/"
|
||||
}
|
||||
},
|
||||
"notFound": "Not finding what you're looking for?",
|
||||
"contact": "Get in touch"
|
||||
},
|
||||
|
@@ -16,10 +16,11 @@
|
||||
|
||||
import "source-sans/source-sans-3.css";
|
||||
|
||||
import { createApp } from "vue";
|
||||
import { ViteSSG } from "vite-ssg";
|
||||
import "./style.css";
|
||||
import App from "./App.vue";
|
||||
import PrimeVue from "primevue/config";
|
||||
import Avatar from "primevue/avatar";
|
||||
import Badge from "primevue/badge";
|
||||
import Button from "primevue/button";
|
||||
import Dropdown from "primevue/dropdown";
|
||||
@@ -35,7 +36,7 @@ import OverlayPanel from "primevue/overlaypanel";
|
||||
import ToggleButton from "primevue/togglebutton";
|
||||
import "primeicons/primeicons.css";
|
||||
import "primeflex/primeflex.css";
|
||||
import router from "./router";
|
||||
import routes from "./router";
|
||||
import SpeedDial from "primevue/speeddial";
|
||||
import TabView from "primevue/tabview";
|
||||
import TabPanel from "primevue/tabpanel";
|
||||
@@ -56,13 +57,46 @@ import Skeleton from "primevue/skeleton";
|
||||
import Calendar from "primevue/calendar";
|
||||
import i18n from "./i18n";
|
||||
import { VueQueryPlugin } from "@tanstack/vue-query";
|
||||
import { Router } from "vue-router";
|
||||
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
||||
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||
|
||||
polyfillCountryFlagEmojis();
|
||||
|
||||
const app = createApp(App);
|
||||
const pinia = createPinia();
|
||||
var router : Router;
|
||||
|
||||
export const createApp = ViteSSG(
|
||||
App,
|
||||
{
|
||||
base: import.meta.env.BASE_URL,
|
||||
routes: routes.routes,
|
||||
},
|
||||
(ctx) => {
|
||||
const { app } = ctx;
|
||||
const pinia = createPinia();
|
||||
app.use(pinia);
|
||||
|
||||
router = ctx.router;
|
||||
|
||||
router.beforeEach(async (to, from) => {
|
||||
if (import.meta.env.SSR) {
|
||||
return;
|
||||
}
|
||||
|
||||
// External redirect
|
||||
if (to.matched.some((record) => record.meta.redirect)) {
|
||||
window.location.replace(to.meta.redirect as string);
|
||||
return;
|
||||
}
|
||||
|
||||
const newLocale = to.params.locale;
|
||||
const prevLocale = from.params.locale;
|
||||
// If the locale hasn't changed, do nothing
|
||||
if (newLocale === prevLocale) {
|
||||
return;
|
||||
}
|
||||
i18n.setLocale(newLocale);
|
||||
});
|
||||
|
||||
pinia.use(piniaPluginPersistedstate);
|
||||
|
||||
@@ -83,6 +117,7 @@ app.use(pinia);
|
||||
app.use(DialogService);
|
||||
i18n.setup();
|
||||
app.use(i18n.vueI18n);
|
||||
app.component("Avatar", Avatar);
|
||||
app.component("Badge", Badge);
|
||||
app.component("Button", Button);
|
||||
app.component("Menu", Menu);
|
||||
@@ -111,5 +146,7 @@ app.component("Checkbox", Checkbox);
|
||||
app.component("Skeleton", Skeleton);
|
||||
app.component("Calendar", Calendar);
|
||||
app.component("OverlayPanel", OverlayPanel);
|
||||
},
|
||||
)
|
||||
|
||||
app.mount("#app");
|
||||
export {router}
|
@@ -14,7 +14,7 @@
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { createMemoryHistory, RouterOptions, createWebHistory } from "vue-router";
|
||||
|
||||
const Faq = () => import("../components/FaqPage.vue");
|
||||
const AdditionalModules = () => import("../view/AdditionalModules.vue");
|
||||
@@ -31,13 +31,22 @@ const FreeRooms = () => import("../view/FreeRooms.vue");
|
||||
const CalenderViewer = () => import("../view/UserCalendar.vue");
|
||||
const SettingsView = () => import("../view/SettingsView.vue");
|
||||
const NotFound = () => import("../view/NotFound.vue");
|
||||
const AdditionalModules = () => import("../view/create/AdditionalModules.vue");
|
||||
const CalendarLink = () => import("../view/CalendarLink.vue");
|
||||
const RenameModules = () => import("../view/create/RenameModules.vue");
|
||||
const RoomFinder = () => import("../view/rooms/RoomFinder.vue");
|
||||
const FreeRooms = () => import("../view/rooms/FreeRooms.vue");
|
||||
const EditCalendarView = () => import("../view/edit/EditCalendar.vue");
|
||||
const EditAdditionalModules = () =>
|
||||
import("../view/edit/EditAdditionalModules.vue");
|
||||
const EditModules = () => import("../view/edit/EditModules.vue");
|
||||
const FaqView = () => import("../view/FaqView.vue");
|
||||
|
||||
import i18n from "../i18n";
|
||||
import settingsStore from "@/store/settingsStore.ts";
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
const routes : RouterOptions = {
|
||||
history: import.meta.env.SSR ? createMemoryHistory(import.meta.env.BASE_URL) : createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
@@ -53,11 +62,18 @@ const router = createRouter({
|
||||
path: "/calendar/create",
|
||||
name: "calendar-create",
|
||||
component: CourseSelection,
|
||||
meta: {
|
||||
label: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/rooms/occupancy",
|
||||
name: "room-schedule",
|
||||
component: RoomFinder,
|
||||
meta: {
|
||||
label: "roomFinderPage.roomSchedule",
|
||||
description: "roomFinderPage.description",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/rooms/occupancy/offline",
|
||||
@@ -68,6 +84,10 @@ const router = createRouter({
|
||||
path: "/rooms/free",
|
||||
name: "free-rooms",
|
||||
component: FreeRooms,
|
||||
meta: {
|
||||
label: "freeRooms.freeRooms",
|
||||
description: "freeRooms.description",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/calendar/view",
|
||||
@@ -77,55 +97,80 @@ const router = createRouter({
|
||||
{
|
||||
path: "/faq",
|
||||
name: "faq",
|
||||
component: Faq,
|
||||
component: FaqView,
|
||||
meta: {
|
||||
label: "faq",
|
||||
description: "faqView.description",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/additional-modules",
|
||||
name: "additional-modules",
|
||||
component: AdditionalModules,
|
||||
meta: {
|
||||
label: "createCalendar",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/edit-additional-modules",
|
||||
name: "edit-additional-modules",
|
||||
component: EditAdditionalModules,
|
||||
meta: {
|
||||
label: "editCalendar",
|
||||
description: "editCalendarView.description"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/edit-calendar",
|
||||
name: "edit-calendar",
|
||||
component: EditModules,
|
||||
meta: {
|
||||
label: "editCalendar",
|
||||
description: "editCalendarView.description"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/calendar-link",
|
||||
name: "calendar-link",
|
||||
component: CalendarLink,
|
||||
meta: {
|
||||
label: "createCalendar"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/calendar/edit",
|
||||
name: "edit",
|
||||
component: EditCalendarView,
|
||||
meta: {
|
||||
label: "editCalendar",
|
||||
description: "editCalendarView.description"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/privacy-policy",
|
||||
name: "privacy-policy",
|
||||
component: Faq,
|
||||
beforeEnter() {
|
||||
window.location.href =
|
||||
"https://www.htwk-leipzig.de/hochschule/kontakt/datenschutzerklaerung/";
|
||||
},
|
||||
component: {},
|
||||
meta: {
|
||||
label: "privacy",
|
||||
redirect: "https://www.htwk-leipzig.de/hochschule/kontakt/datenschutzerklaerung/",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/imprint",
|
||||
name: "imprint",
|
||||
component: Faq,
|
||||
beforeEnter() {
|
||||
window.location.href =
|
||||
"https://www.htwk-leipzig.de/hochschule/kontakt/impressum/";
|
||||
},
|
||||
component: {},
|
||||
meta: {
|
||||
label: "imprint",
|
||||
redirect: "https://www.htwk-leipzig.de/hochschule/kontakt/impressum/",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/rename-modules",
|
||||
name: "rename-modules",
|
||||
component: RenameModules,
|
||||
meta: {
|
||||
label: "createCalendar"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
@@ -138,24 +183,6 @@ const router = createRouter({
|
||||
component: NotFound, // Replace with your NotFound component
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const newLocale = to.params.locale;
|
||||
const prevLocale = from.params.locale;
|
||||
// If the locale hasn't changed, do nothing
|
||||
if (!(newLocale === prevLocale)) {
|
||||
i18n.setLocale(newLocale);
|
||||
}
|
||||
|
||||
const userSettings = settingsStore();
|
||||
const defaultPath = userSettings.defaultPage || "/home";
|
||||
|
||||
if (to.path === "/") {
|
||||
next(defaultPath.value);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default routes;
|
||||
|
@@ -17,16 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script lang="ts" setup>
|
||||
import tokenStore from "../store/tokenStore.ts";
|
||||
import tokenStore from "@/store/tokenStore.ts";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { computed, onMounted } from "vue";
|
||||
import router from "../router";
|
||||
import { computed, inject, onMounted } from "vue";
|
||||
import { router } from "@/main";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const domain = window.location.hostname;
|
||||
const domain = import.meta.env.SSR ? inject<string>("domain")! : window.location.hostname;
|
||||
|
||||
const getLink = () =>
|
||||
"https://" + domain + "/api/feed?token=" + tokenStore().token;
|
||||
@@ -36,7 +36,7 @@ const show = () => {
|
||||
severity: "info",
|
||||
summary: t("calendarLink.copyToastSummary"),
|
||||
detail: t("calendarLink.copyToastNotification"),
|
||||
life: 3000,
|
||||
life: 3000
|
||||
});
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ function copyToClipboard() {
|
||||
severity: "error",
|
||||
summary: t("calendarLink.copyToastError"),
|
||||
detail: t("calendarLink.copyToastErrorDetail"),
|
||||
life: 3000,
|
||||
life: 3000
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -65,14 +65,14 @@ function copyToClipboard() {
|
||||
const forwardToGoogle = () => {
|
||||
window.open(
|
||||
"https://calendar.google.com/calendar/u/0/r?cid=" +
|
||||
encodeURI(getLink().replace("https://", "http://")),
|
||||
encodeURI(getLink().replace("https://", "http://"))
|
||||
);
|
||||
};
|
||||
|
||||
const forwardToMicrosoft = () => {
|
||||
window.open(
|
||||
"https://outlook.live.com/owa?path=/calendar/action/compose&rru=addsubscription&name=HTWK%20Kalender&url=" +
|
||||
encodeURI(getLink()),
|
||||
encodeURI(getLink())
|
||||
);
|
||||
};
|
||||
|
||||
@@ -88,22 +88,22 @@ const actions = computed(() => [
|
||||
{
|
||||
label: t("calendarLink.copyToClipboard"),
|
||||
icon: "pi pi-copy",
|
||||
command: copyToClipboard,
|
||||
command: copyToClipboard
|
||||
},
|
||||
{
|
||||
label: t("calendarLink.toGoogleCalendar"),
|
||||
icon: "pi pi-google",
|
||||
command: forwardToGoogle,
|
||||
command: forwardToGoogle
|
||||
},
|
||||
{
|
||||
label: t("calendarLink.toMicrosoftCalendar"),
|
||||
icon: "pi pi-microsoft",
|
||||
command: forwardToMicrosoft,
|
||||
command: forwardToMicrosoft
|
||||
},
|
||||
{
|
||||
label: t("calendarLink.toHTWKalendar"),
|
||||
icon: "pi pi-home",
|
||||
command: forwardToHTWKalendar,
|
||||
command: forwardToHTWKalendar
|
||||
},
|
||||
]);
|
||||
</script>
|
@@ -21,13 +21,13 @@ import { computed, ComputedRef, Ref, ref, watch } from "vue";
|
||||
import {
|
||||
fetchCourseBySemester,
|
||||
fetchModulesByCourseAndSemester,
|
||||
} from "../api/fetchCourse";
|
||||
import DynamicPage from "./DynamicPage.vue";
|
||||
import ModuleSelection from "../components/ModuleSelection.vue";
|
||||
import { Module } from "../model/module.ts";
|
||||
} from "@/api/fetchCourse";
|
||||
import DynamicPage from "@/view/DynamicPage.vue";
|
||||
import ModuleSelection from "@/components/ModuleSelection.vue";
|
||||
import { Module } from "@/model/module.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import moduleStore from "../store/moduleStore";
|
||||
import router from "../router";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import { router } from "@/main";
|
||||
|
||||
async function getModules() {
|
||||
modules.value = await fetchModulesByCourseAndSemester(
|
||||
|
@@ -53,7 +53,7 @@ const hasContent = computed(() => {
|
||||
<i v-if="icon" :class="icon" style="font-size: 2rem"></i>
|
||||
</div>
|
||||
<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 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>
|
||||
@@ -86,6 +86,18 @@ const hasContent = computed(() => {
|
||||
]"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
<div
|
||||
v-if="button"
|
||||
class="flex flex-wrap my-3 gap-2 align-items-center justify-content-end"
|
||||
>
|
||||
<Button
|
||||
:disabled="button.disabled"
|
||||
class="col-12 md:col-4"
|
||||
:icon="button.icon"
|
||||
:label="button.label"
|
||||
@click="button.onClick()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -94,4 +106,8 @@ const hasContent = computed(() => {
|
||||
.transition-rolldown {
|
||||
transition: margin-top 0.5s ease-in-out;
|
||||
}
|
||||
.subtitle {
|
||||
font-weight: 100;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
|
@@ -16,7 +16,8 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex align-items-center justify-content-center flex-column">
|
||||
@@ -53,6 +54,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<li>
|
||||
{{ $t("faqView.thirdAnswer.google.third") }}
|
||||
</li>
|
||||
<li>
|
||||
{{ $t("faqView.thirdAnswer.google.fourth") }}
|
||||
</li>
|
||||
</ol>
|
||||
</AccordionTab>
|
||||
<AccordionTab
|
||||
@@ -230,10 +234,39 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<div class="col">{{ $t("faqView.ninthQuestion") }}</div>
|
||||
<div class="col">
|
||||
{{ $t("faqView.ninthAnswer") }}
|
||||
<a href="https://git.imn.htwk-leipzig.de/ekresse/htwkalender"
|
||||
<a href="https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender"
|
||||
>Gitlab</a
|
||||
>
|
||||
.
|
||||
>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid my-2">
|
||||
<div class="col">{{ $t("faqView.crossPromoQuestion") }}</div>
|
||||
<div class="col">
|
||||
{{ $t("faqView.crossPromo.teaser") }}
|
||||
<div class="flex flex-column gap-3 my-3">
|
||||
<Card v-for="promoPage in new Array('mensa', 'htwkarte')" :key="promoPage">
|
||||
<template #title>
|
||||
<div class="flex flex-row align-items-start">
|
||||
<Avatar :image="'/promo/' + promoPage + '.png'" class="mr-2 flex-shrink-0" size="xlarge" />
|
||||
<div class="flex flex-column gap-1">
|
||||
<div>
|
||||
{{$t("faqView.crossPromo." + promoPage + ".title") }}
|
||||
</div>
|
||||
<div class="p-card-subtitle text-base">
|
||||
<a :href="$t('faqView.crossPromo.' + promoPage + '.link')">
|
||||
{{$t("faqView.crossPromo." + promoPage + ".link") }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
{{ $t("faqView.crossPromo." + promoPage + ".description") }}
|
||||
</p>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
@@ -17,35 +17,50 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script lang="ts" setup>
|
||||
import moduleStore from "../store/moduleStore";
|
||||
import router from "../router";
|
||||
import AdditionalModuleTable from "../components/AdditionalModuleTable.vue";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import {router} from "@/main";
|
||||
import AdditionalModuleTable from "@/components/AdditionalModuleTable.vue";
|
||||
|
||||
const store = moduleStore();
|
||||
|
||||
function topFunction() {
|
||||
window.scrollTo({top: 0, behavior: 'smooth'});
|
||||
}
|
||||
|
||||
async function nextStep() {
|
||||
await router.push("/rename-modules");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-column align-items-center w-full mb-7">
|
||||
<div class="flex align-items-center justify-content-center m-2">
|
||||
<div class="flex flex-column align-items-center w-full mb-8">
|
||||
<div class="flex align-items-center justify-content-center">
|
||||
<h3>
|
||||
{{ $t("additionalModules.subTitle") }}
|
||||
</h3>
|
||||
</div>
|
||||
<AdditionalModuleTable />
|
||||
<div
|
||||
class="flex align-items-center justify-content-end h-4rem m-2 w-full lg:w-10"
|
||||
class="flex align-items-center justify-content-end h-4rem mb-2 w-full lg:w-10"
|
||||
>
|
||||
<Button
|
||||
:disabled="store.isEmpty()"
|
||||
class="col-12 md:col-4 mb-3 align-self-end"
|
||||
class="col-12 md:col-4 align-self-end"
|
||||
icon="pi pi-arrow-right"
|
||||
:label="$t('additionalModules.nextStep')"
|
||||
@click="nextStep()"
|
||||
/>
|
||||
</div>
|
||||
<AdditionalModuleTable />
|
||||
<div
|
||||
class="flex align-items-center justify-content-end mt-2 w-full lg:w-10"
|
||||
>
|
||||
<Button
|
||||
class="col-12 md:col-4 mb-3 align-self-end"
|
||||
severity="secondary"
|
||||
icon="pi pi-arrow-up"
|
||||
:label="$t('additionalModules.scrollToTop')"
|
||||
@click="topFunction()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@@ -17,13 +17,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import moduleStore from "../store/moduleStore.ts";
|
||||
import { createIndividualFeed } from "../api/createFeed.ts";
|
||||
import router from "../router";
|
||||
import tokenStore from "../store/tokenStore.ts";
|
||||
import moduleStore from "@/store/moduleStore.ts";
|
||||
import { createIndividualFeed } from "@/api/createFeed.ts";
|
||||
import { router } from "@/main";
|
||||
import tokenStore from "@/store/tokenStore.ts";
|
||||
import { Ref, computed, inject, ref, onMounted } from "vue";
|
||||
import ModuleTemplateDialog from "./ModuleTemplateDialog.vue";
|
||||
import { onlyWhitespace } from "../helpers/strings.ts";
|
||||
import ModuleTemplateDialog from "@/components/ModuleTemplateDialog.vue";
|
||||
import { onlyWhitespace } from "@/helpers/strings.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Module } from "@/model/module.ts";
|
||||
import { useToast } from "primevue/usetoast";
|
@@ -18,12 +18,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import moduleStore from "../../store/moduleStore";
|
||||
import router from "../../router";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import { router } from "@/main";
|
||||
|
||||
const store = moduleStore();
|
||||
const AdditionalModuleTable = defineAsyncComponent(
|
||||
() => import("../../components/AdditionalModuleTable.vue"),
|
||||
() => import("@/components/AdditionalModuleTable.vue"),
|
||||
);
|
||||
|
||||
async function nextStep() {
|
@@ -18,14 +18,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Ref, ref } from "vue";
|
||||
import { Module } from "../model/module";
|
||||
import moduleStore from "../store/moduleStore";
|
||||
import { getCalender } from "../api/loadCalendar";
|
||||
import router from "../router";
|
||||
import tokenStore from "../store/tokenStore";
|
||||
import { Module } from "@/model/module";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import { getCalender } from "@/api/loadCalendar";
|
||||
import { router } from "@/main";
|
||||
import tokenStore from "@/store/tokenStore";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import DynamicPage from "./DynamicPage.vue";
|
||||
import DynamicPage from "@/view/DynamicPage.vue";
|
||||
import { extractToken, isToken } from "@/helpers/token.ts";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
@@ -19,12 +19,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, ref } from "vue";
|
||||
import { Module } from "@/model/module.ts";
|
||||
import moduleStore from "../../store/moduleStore";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import { fetchAllModules } from "@/api/fetchCourse.ts";
|
||||
import { deleteIndividualFeed, saveIndividualFeed } from "@/api/createFeed.ts";
|
||||
import tokenStore from "../../store/tokenStore";
|
||||
import router from "@/router";
|
||||
import ModuleTemplateDialog from "../../components/ModuleTemplateDialog.vue";
|
||||
import tokenStore from "@/store/tokenStore";
|
||||
import { router } from "@/main";
|
||||
import ModuleTemplateDialog from "@/components/ModuleTemplateDialog.vue";
|
||||
import { onlyWhitespace } from "@/helpers/strings.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useToast } from "primevue/usetoast";
|
@@ -155,7 +155,7 @@ import DynamicPage from "@/view/DynamicPage.vue";
|
||||
import { requestFreeRooms } from "@/api/requestFreeRooms.ts";
|
||||
import { FilterMatchMode } from "primevue/api";
|
||||
import { padStart } from "@fullcalendar/core/internal";
|
||||
import router from "@/router";
|
||||
import { router } from "@/main";
|
||||
import { formatYearMonthDay } from "@/helpers/dates";
|
||||
|
||||
const mobilePage = inject("mobilePage") as Ref<boolean>;
|
@@ -18,11 +18,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Ref, computed, ref, watch } from "vue";
|
||||
import { fetchRoom } from "../api/fetchRoom.ts";
|
||||
import DynamicPage from "./DynamicPage.vue";
|
||||
import RoomOccupation from "../components/RoomOccupation.vue";
|
||||
import { fetchRoom } from "@/api/fetchRoom.ts";
|
||||
import DynamicPage from "@/view/DynamicPage.vue";
|
||||
import RoomOccupation from "@/components/RoomOccupation.vue";
|
||||
import { computedAsync } from "@vueuse/core";
|
||||
import router from "@/router";
|
||||
import { router } from "@/main";
|
||||
|
||||
type Room = {
|
||||
name: string;
|
@@ -20,7 +20,8 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": ["./src/*"],
|
||||
"primevue/*": ["./node_modules/primevue/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
|
@@ -19,10 +19,21 @@ import vue from "@vitejs/plugin-vue";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
import basicSsl from "@vitejs/plugin-basic-ssl";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import {resolve as pathResolver} from "path";
|
||||
import terser from "@rollup/plugin-terser";
|
||||
import ViteSSGOptions from "vite-ssg";
|
||||
import generateSitemap from 'vite-ssg-sitemap'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
const hostname = "https://cal.htwk-leipzig.de";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
resolve(),
|
||||
terser(),
|
||||
vueDevTools(),
|
||||
basicSsl(),
|
||||
VitePWA({
|
||||
mode: "development",
|
||||
@@ -120,8 +131,27 @@ export default defineConfig({
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
alias:
|
||||
{
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
'primevue' : pathResolver(__dirname, 'node_modules/primevue'),
|
||||
},
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.css', '.scss'],
|
||||
},
|
||||
ssgOptions: {
|
||||
script: "async",
|
||||
formatting: "minify",
|
||||
format: "esm",
|
||||
onFinished: () => {
|
||||
generateSitemap({
|
||||
hostname: hostname,
|
||||
exclude: [
|
||||
'/additional-modules',
|
||||
'/edit-additional-modules',
|
||||
'/edit-calendar',
|
||||
'/rename-modules',
|
||||
]
|
||||
})
|
||||
},
|
||||
},
|
||||
server: {
|
||||
@@ -150,4 +180,24 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
sourcemap: true,
|
||||
minify: "terser",
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: true,
|
||||
},
|
||||
},
|
||||
cssMinify: "esbuild",
|
||||
cssCodeSplit: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (id.includes("node_modules")) {
|
||||
return id.toString().split("node_modules/")[1].split("/")[0].toString()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
Reference in New Issue
Block a user