mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-08-04 02:39:14 +02:00
Merge branch 'main' into 15-calendar-preview
# Conflicts: # frontend/src/App.vue # frontend/src/view/AdditionalModules.vue
This commit is contained in:
@@ -1,280 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, ref, Ref } from "vue";
|
||||
import { Module } from "../model/module.ts";
|
||||
import { fetchAllModules } from "../api/fetchCourse.ts";
|
||||
import moduleStore from "../store/moduleStore.ts";
|
||||
import { FilterMatchMode } from "primevue/api";
|
||||
|
||||
import { useDialog } from "primevue/usedialog";
|
||||
const dialog = useDialog();
|
||||
|
||||
import router from "../router";
|
||||
import { fetchModule } from "../api/fetchModule.ts";
|
||||
|
||||
const fetchedModules = async () => {
|
||||
return await fetchAllModules();
|
||||
};
|
||||
|
||||
const store = moduleStore();
|
||||
const modules: Ref<Module[]> = ref([]);
|
||||
const filters = ref({
|
||||
course: {
|
||||
value: null,
|
||||
matchMode: FilterMatchMode.CONTAINS,
|
||||
},
|
||||
name: {
|
||||
value: null,
|
||||
matchMode: FilterMatchMode.CONTAINS,
|
||||
},
|
||||
prof: {
|
||||
value: null,
|
||||
matchMode: FilterMatchMode.CONTAINS,
|
||||
}
|
||||
});
|
||||
|
||||
//const selectedModules: Ref<Module[]> = ref([] as Module[]);
|
||||
//const additionalModules: Ref<Map<string, Module>> = ref(new Map());
|
||||
|
||||
fetchedModules().then(
|
||||
(data) =>
|
||||
(modules.value = data.map((module: Module) => {
|
||||
return module;
|
||||
})),
|
||||
);
|
||||
|
||||
async function nextStep() {
|
||||
//selectedModules.value.forEach((module: Module) => {
|
||||
// moduleStore().addModule(module);
|
||||
//});
|
||||
|
||||
await router.push("/rename-modules");
|
||||
}
|
||||
|
||||
const ModuleInformation = defineAsyncComponent(
|
||||
() => import("./ModuleInformation.vue"),
|
||||
);
|
||||
|
||||
async function showInfo(module: Module) {
|
||||
await fetchModule(module).then((data) => {
|
||||
module = data;
|
||||
});
|
||||
dialog.open(ModuleInformation, {
|
||||
props: {
|
||||
style: {
|
||||
width: "50vw",
|
||||
},
|
||||
breakpoints: {
|
||||
"960px": "75vw",
|
||||
"640px": "90vw",
|
||||
},
|
||||
modal: true,
|
||||
},
|
||||
data: {
|
||||
module: module,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
const display = (module: Module) => module.name + " (" + module.course + ")";
|
||||
|
||||
const selectAll = ref(false);
|
||||
|
||||
const onSelectAllChange = (event: MultiSelectAllChangeEvent) => {
|
||||
if (event.checked) {
|
||||
additionalModules.value = new Map(
|
||||
modules.value
|
||||
.filter((module: Module) => !store.hasModule(module))
|
||||
.map((module: Module) => [module.uuid, module]),
|
||||
);
|
||||
store.overwriteModules(modules.value);
|
||||
} else {
|
||||
store.overwriteModules(
|
||||
store.getAllModules().filter(
|
||||
(module: Module) => !additionalModules.value.has(module.uuid)
|
||||
)
|
||||
);
|
||||
additionalModules.value.clear();
|
||||
}
|
||||
|
||||
selectAll.value = event.checked;
|
||||
};
|
||||
|
||||
function selectChange(event : MultiSelectChangeEvent) {
|
||||
let wasSelected: boolean = additionalModules.value.has(event.value.uuid);
|
||||
|
||||
if (event.originalEvent.target.) {
|
||||
additionalModules.value.set(event.value.uuid, event.value);
|
||||
} else {
|
||||
additionalModules.value.delete(event.value.uuid);
|
||||
}
|
||||
selectAll.value = store.countModules() === modules.value.length;
|
||||
}
|
||||
*/
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-column">
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<h3>
|
||||
Select additional Modules that are not listed in the regular semester
|
||||
for your Course
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card flex align-items-center justify-content-center m-2">
|
||||
<DynamicDialog />
|
||||
<DataTable
|
||||
v-model:filters="filters"
|
||||
:value="modules"
|
||||
data-key="uuid"
|
||||
paginator
|
||||
:rows="10"
|
||||
:rows-per-page-options="[5, 10, 20, 50]"
|
||||
paginator-template="FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink RowsPerPageDropdown"
|
||||
current-page-report-template="{first} to {last} of {totalRecords}"
|
||||
filter-display="row"
|
||||
:loading="!modules.length"
|
||||
loading-icon="pi pi-spinner"
|
||||
:show-gridlines="true"
|
||||
:striped-rows="true"
|
||||
class="w-10"
|
||||
>
|
||||
|
||||
<Column
|
||||
selection-mode="multiple"
|
||||
>
|
||||
</Column>
|
||||
|
||||
<Column
|
||||
field="course"
|
||||
header="Course"
|
||||
:show-clear-button="false"
|
||||
:show-filter-menu="false"
|
||||
>
|
||||
<template #filter="{ filterModel, filterCallback }">
|
||||
<InputText
|
||||
v-model="filterModel.value"
|
||||
type="text"
|
||||
class="p-column-filter max-w-10rem"
|
||||
@input="filterCallback()"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
<Column
|
||||
field="name"
|
||||
header="Name"
|
||||
:show-clear-button="false"
|
||||
:show-filter-menu="false"
|
||||
>
|
||||
<template #filter="{ filterModel, filterCallback }">
|
||||
<InputText
|
||||
v-model="filterModel.value"
|
||||
type="text"
|
||||
class="p-column-filter"
|
||||
@input="filterCallback()"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
<Column
|
||||
field="prof"
|
||||
header="Professor"
|
||||
:show-clear-button="false"
|
||||
:show-filter-menu="false"
|
||||
>
|
||||
<template #filter="{ filterModel, filterCallback }">
|
||||
<InputText
|
||||
v-model="filterModel.value"
|
||||
type="text"
|
||||
class="p-column-filter"
|
||||
@input="filterCallback()"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="Info">
|
||||
<template #body="slotProps">
|
||||
<Button
|
||||
icon="pi pi-info"
|
||||
severity="secondary"
|
||||
rounded
|
||||
outlined
|
||||
aria-label="Information"
|
||||
class="small-button"
|
||||
@click.stop="showInfo(slotProps.data)"
|
||||
></Button>
|
||||
</template>
|
||||
|
||||
</Column>
|
||||
<template #footer>
|
||||
<div class="py-2 px-3">
|
||||
<b>{{ store ? store.countModules() : 0 }}</b>
|
||||
item{{
|
||||
(store ? store.countModules() : 0) !== 1 ? "s" : ""
|
||||
}}
|
||||
selected.
|
||||
</div>
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
<!--
|
||||
<MultiSelect
|
||||
:model-value="store.getAllModules()"
|
||||
@update:model-value="store.overwriteModules($event.value)"
|
||||
:max-selected-labels="1"
|
||||
:option-label="display"
|
||||
:options="modules"
|
||||
:select-all="selectAll"
|
||||
:virtual-scroller-options="{ itemSize: 70 }"
|
||||
class="custom-multiselect"
|
||||
filter
|
||||
placeholder="Select additional modules"
|
||||
@change="selectChange($event)"
|
||||
@selectall-change="onSelectAllChange($event)"
|
||||
>
|
||||
<template #option="slotProps">
|
||||
<div class="flex justify-content-between w-full">
|
||||
<div class="flex align-items-center justify-content-center">
|
||||
<p class="text-1xl white-space-normal p-mb-0">
|
||||
{{ display(slotProps.option) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center ml-2">
|
||||
<Button
|
||||
icon="pi pi-info"
|
||||
severity="secondary"
|
||||
rounded
|
||||
outlined
|
||||
aria-label="Information"
|
||||
class="small-button"
|
||||
@click.stop="showInfo(slotProps.option)"
|
||||
></Button>
|
||||
<DynamicDialog />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="py-2 px-3">
|
||||
<b>{{ selectedModules ? selectedModules.length : 0 }}</b>
|
||||
item{{
|
||||
(selectedModules ? selectedModules.length : 0) > 1 ? "s" : ""
|
||||
}}
|
||||
selected.
|
||||
</div>
|
||||
</template>
|
||||
</MultiSelect>
|
||||
-->
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<Button @click="nextStep()">Next Step</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.custom-multiselect) {
|
||||
width: 50rem;
|
||||
}
|
||||
|
||||
:deep(.custom-multiselect li) {
|
||||
height: unset;
|
||||
}
|
||||
</style>
|
@@ -3,15 +3,21 @@ import tokenStore from "../store/tokenStore.ts";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { onMounted } from "vue";
|
||||
import router from "../router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const domain = window.location.hostname;
|
||||
|
||||
const getLink = () =>
|
||||
"https://" + domain + "/api/feed?token=" + tokenStore().token;
|
||||
|
||||
const show = () => {
|
||||
toast.add({
|
||||
severity: "info",
|
||||
summary: "Info",
|
||||
detail: "Link copied to clipboard",
|
||||
summary: t("calendarLink.copyToastSummary"),
|
||||
detail: t("calendarLink.copyToastNotification"),
|
||||
life: 3000,
|
||||
});
|
||||
};
|
||||
@@ -27,11 +33,49 @@ function rerouteIfTokenIsEmpty() {
|
||||
}
|
||||
|
||||
function copyToClipboard() {
|
||||
const text = "https://" + domain + "/api/feed?token=" + tokenStore().token;
|
||||
// Copy the text inside the text field
|
||||
navigator.clipboard.writeText(text);
|
||||
show();
|
||||
navigator.clipboard.writeText(getLink()).then(show, (err) => {
|
||||
console.error("Could not copy text: ", err);
|
||||
toast.add({
|
||||
severity: "error",
|
||||
summary: t("calendarLink.copyToastError"),
|
||||
detail: t("calendarLink.copyToastErrorDetail"),
|
||||
life: 3000,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const forwardToGoogle = () => {
|
||||
window.open(
|
||||
"https://calendar.google.com/calendar/u/0/r?cid=" +
|
||||
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()),
|
||||
);
|
||||
};
|
||||
|
||||
const actions = [
|
||||
{
|
||||
label: t("calendarLink.copyToClipboard"),
|
||||
icon: "pi pi-copy",
|
||||
command: copyToClipboard,
|
||||
},
|
||||
{
|
||||
label: t("calendarLink.toGoogleCalendar"),
|
||||
icon: "pi pi-google",
|
||||
command: forwardToGoogle,
|
||||
},
|
||||
{
|
||||
label: t("calendarLink.toMicrosoftCalendar"),
|
||||
icon: "pi pi-microsoft",
|
||||
command: forwardToMicrosoft,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -39,11 +83,11 @@ function copyToClipboard() {
|
||||
<div class="flex flex-column">
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<h2>
|
||||
{{ "https://" + domain + "/api/feed?token=" + tokenStore().token }}
|
||||
{{ getLink() }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<Button @click="copyToClipboard">Copy iCal Link</Button>
|
||||
<div class="flex align-items-center justify-content-center m-2">
|
||||
<Menu :model="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -1,11 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { Ref, ref } from "vue";
|
||||
import { computed, ComputedRef, Ref, ref, watch } from "vue";
|
||||
import {
|
||||
fetchCourse,
|
||||
fetchModulesByCourseAndSemester,
|
||||
} from "../api/fetchCourse";
|
||||
import ModuleSelection from "./ModuleSelection.vue";
|
||||
import { Module } from "../model/module.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const courses = async () => {
|
||||
return await fetchCourse();
|
||||
@@ -13,15 +15,25 @@ const courses = async () => {
|
||||
|
||||
const selectedCourse: Ref<{ name: string }> = ref({ name: "" });
|
||||
const countries: Ref<{ name: string }[]> = ref([]);
|
||||
const semesters: Ref<{ name: string; value: string }[]> = ref([
|
||||
{ name: "Wintersemester", value: "ws" },
|
||||
{ name: "Sommersemester", value: "ss" },
|
||||
]);
|
||||
const semesters: ComputedRef<{ name: string; value: string }[]> = computed(
|
||||
() => [
|
||||
{ name: t("courseSelection.winterSemester"), value: "ws" },
|
||||
{ name: t("courseSelection.summerSemester"), value: "ss" },
|
||||
],
|
||||
);
|
||||
|
||||
const selectedSemester: Ref<{ name: string; value: string }> = ref(
|
||||
semesters.value[0],
|
||||
);
|
||||
|
||||
//if semesters get changed, refresh the selected semester ref with a watcher
|
||||
watch(
|
||||
semesters,
|
||||
(newValue: { name: string; value: string }[]) =>
|
||||
(selectedSemester.value =
|
||||
newValue[selectedSemester.value.value === "ws" ? 0 : 1]),
|
||||
);
|
||||
|
||||
courses().then(
|
||||
(data) =>
|
||||
(countries.value = data.map((course) => {
|
||||
@@ -43,7 +55,7 @@ async function getModules() {
|
||||
<div class="flex flex-column">
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<h3 class="text-4xl">
|
||||
Welcome to HTWKalender
|
||||
{{ $t("courseSelection.headline") }}
|
||||
<i
|
||||
class="pi pi-calendar vertical-align-baseline"
|
||||
style="font-size: 2rem"
|
||||
@@ -53,7 +65,7 @@ async function getModules() {
|
||||
<div
|
||||
class="flex align-items-center justify-content-center h-4rem border-round m-2"
|
||||
>
|
||||
<h5 class="text-2xl">Please select a course</h5>
|
||||
<h5 class="text-2xl">{{ $t("courseSelection.subTitle") }}</h5>
|
||||
</div>
|
||||
<div
|
||||
class="flex align-items-center justify-content-center border-round m-2"
|
||||
@@ -64,7 +76,9 @@ async function getModules() {
|
||||
class="w-full md:w-25rem mx-2"
|
||||
filter
|
||||
option-label="name"
|
||||
placeholder="Select a Course"
|
||||
:placeholder="$t('courseSelection.courseDropDown')"
|
||||
:empty-message="$t('courseSelection.noCoursesAvailable')"
|
||||
:auto-filter-focus="true"
|
||||
@change="getModules()"
|
||||
></Dropdown>
|
||||
<Dropdown
|
||||
@@ -72,7 +86,7 @@ async function getModules() {
|
||||
:options="semesters"
|
||||
class="w-full md:w-25rem mx-2"
|
||||
option-label="name"
|
||||
placeholder="Select a Semester"
|
||||
:placeholder="$t('courseSelection.semesterDropDown')"
|
||||
@change="getModules()"
|
||||
></Dropdown>
|
||||
</div>
|
||||
|
@@ -1,277 +1,141 @@
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex align-items-center justify-content-center flex-column">
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<h1>FAQ</h1>
|
||||
<h1>{{$t('faqView.headline')}}</h1>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-column col-7">
|
||||
<div class="grid my-2">
|
||||
<div class="col">
|
||||
Wie funktioniert das Kalender erstellen mit dem HTWKalender?
|
||||
{{$t('faqView.firstQuestion')}}
|
||||
</div>
|
||||
<div class="col">
|
||||
Die Webseite ermöglicht es deinen HTWK-Stundenplan in eines deiner
|
||||
bevorzugten Kalender-Verwaltungs-Programme (Outlook, Google Kalender,
|
||||
etc.) einzubinden.
|
||||
{{$t('faqView.firstAnswer')}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid my-2">
|
||||
<div class="col">{{$t('faqView.secondQuestion')}}</div>
|
||||
<div class="col">
|
||||
{{$t('faqView.secondAnswer')}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid my-2">
|
||||
<div class="col">Wie genau funktioniert das alles?</div>
|
||||
<div class="col">
|
||||
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. <br />
|
||||
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.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid my-2">
|
||||
<div class="col">Wie kann ich den Kalender abonnieren?</div>
|
||||
<div class="col">{{$t('faqView.thirdQuestion')}}</div>
|
||||
<div class="col">
|
||||
<Accordion>
|
||||
<AccordionTab header="Google Calendar">
|
||||
<AccordionTab :header="$t('faqView.thirdAnswer.tabTitle')">
|
||||
<ol>
|
||||
<li>Erstelle deinen Kalender und kopiere den Link.</li>
|
||||
<li>{{$t('faqView.thirdAnswer.google.first')}}</li>
|
||||
<li>
|
||||
Bei Google Kalender selbst hast du in der linken Seitenleiste
|
||||
eine Sektion namens <em>"Weitere Kalender"</em>. Dort klickst
|
||||
du das kleine Pfeil-Icon rechts neben dem Schritzug. Im
|
||||
daraufhin erscheinenden Menü gibt es einen Punkt
|
||||
<em>"Über URL hinzufügen"</em>, den du anklicken musst.
|
||||
{{$t('faqView.thirdAnswer.google.second')}}
|
||||
</li>
|
||||
<li>
|
||||
Füge den kopierten Kalenderlink ein, klicke auf
|
||||
<em>"Kalender hinzufügen"</em> und du bist fertig.
|
||||
{{$t('faqView.thirdAnswer.google.third')}}
|
||||
</li>
|
||||
</ol>
|
||||
</AccordionTab>
|
||||
<AccordionTab header="Microsoft Outlook">
|
||||
<p>Unter Outlook 2010:</p>
|
||||
<AccordionTab :header="$t('faqView.thirdAnswer.microsoft_outlook.title')">
|
||||
<p>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2010.title')}}</p>
|
||||
|
||||
<ol>
|
||||
<li>Erstelle deinen Kalender und kopiere den Link.</li>
|
||||
<li>Klicke auf den Reiter <em>“Start”</em>.</li>
|
||||
<li>
|
||||
Dort befindest sich in der Sektion
|
||||
<em>“Kalender verwalten”</em> der Button
|
||||
<em>“Kalender öffnen”</em>, auf den du kicken musst.
|
||||
</li>
|
||||
<li>
|
||||
Es öffnet sich ein Kontextmenü, in dem du den Punkt
|
||||
<em>“Aus dem Internet”</em> anklickst.
|
||||
</li>
|
||||
<li>
|
||||
Im daraufhin erscheinenden Fenster kannst du den Link einfügen
|
||||
und mit <em>“OK”</em> bestätigen. Eventuell musst du das
|
||||
Abonnement in einem weiteren Schritt noch bestätigen. Dann
|
||||
bist du fertig.
|
||||
</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2010.first')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2010.second')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2010.third')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2010.fourth')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2010.fifth')}}</li>
|
||||
</ol>
|
||||
|
||||
<p>Unter Outlook 2007:</p>
|
||||
<p>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2007.title')}}</p>
|
||||
|
||||
<ol>
|
||||
<li>Erstelle deinen Kalender und kopiere den Link.</li>
|
||||
<li>
|
||||
Unter <em>“Extras”</em> findest du die
|
||||
<em>“Kontoeinstellungen”</em>.
|
||||
</li>
|
||||
<li>
|
||||
Dort auf den Reiter <em>“Internetkalender”</em> klicken.
|
||||
</li>
|
||||
<li>Auf <em>“Neu”</em> klicken.</li>
|
||||
<li>
|
||||
Im sich öffnenden Fenster musst du den Link einfügen.
|
||||
Allerdings musst du <em>“http://”</em> durch
|
||||
<em>“webcal://”</em> austauschen.
|
||||
</li>
|
||||
<li>
|
||||
Spätestens beim nächsten Start sollte der Kalender
|
||||
aktualisiert werden und dir fortan zur Verfügung stehen.
|
||||
</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2007.first')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2007.second')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2007.third')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2007.fourth')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2007.fifth')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.microsoft_outlook.outlook_2007.sixth')}}</li>
|
||||
</ol>
|
||||
</AccordionTab>
|
||||
<AccordionTab header="Kalender (OS X)">
|
||||
<AccordionTab :header="$t('faqView.thirdAnswer.apple_osx.title')">
|
||||
<ol>
|
||||
<li>Erstelle deinen Kalender und kopiere den Link.</li>
|
||||
<li>
|
||||
Unter <em>“Ablage”</em> findest du den Punkt
|
||||
<em>“Neues Kalenderabonnement”</em> (oder unter Snow Leopard
|
||||
<em>“Abonnieren”</em>).
|
||||
</li>
|
||||
<li>
|
||||
Im daraufhin erscheinenden Fenster musst du den kopierten Link
|
||||
einfügen und <em>“abonnieren”</em> klicken.
|
||||
</li>
|
||||
<li>
|
||||
Anschließend kannst du dem Kalender noch einen Namen geben und
|
||||
bestimmen, wie oft er aktualisiert werden soll. Falls du
|
||||
iCloud auf deinem iPhone o.ä. verwendest, empfehle ich dir bei
|
||||
<em>“Ort”</em> unbedingt <em>“iCloud”</em> zu wählen. So hast
|
||||
du deinen Stundenplan ohne weiteres zutun auch unterwegs immer
|
||||
parat.
|
||||
</li>
|
||||
<li>{{$t('faqView.thirdAnswer.apple_osx.first')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.apple_osx.second')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.apple_osx.third')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.apple_osx.fourth')}}</li>
|
||||
</ol>
|
||||
</AccordionTab>
|
||||
<AccordionTab header="Thunderbird">
|
||||
<AccordionTab :header="$t('faqView.thirdAnswer.thunderbird.title')">
|
||||
<ol>
|
||||
<li>Erstelle deinen Kalender und kopiere den Link.</li>
|
||||
<li>
|
||||
Im Menü <em>“Termine und Aufgaben”</em> den Punkt
|
||||
<em>“Kalender”</em> wählen.
|
||||
</li>
|
||||
<li>
|
||||
Links siehst du die Kalenderübersicht. In diesem Bereich über
|
||||
die rechte Maustaste klicken und im darauf erscheinenden
|
||||
Kontextmenü <em>“Neuer Kalender”</em> anklicken.
|
||||
</li>
|
||||
<li>
|
||||
Du hast die Wahl zwischen <em>“Auf meinem Computer”</em> und
|
||||
<em>“Im Netzwerk”</em>. Bitte letzteres wählen und
|
||||
<em>“Fortsetzen”</em> klicken.
|
||||
</li>
|
||||
<li>
|
||||
Im folgenden Fenster lässt du das <em>“Format”</em> wie es ist
|
||||
(<em>“iCalender”</em>).
|
||||
</li>
|
||||
<li>
|
||||
Unter <em>"Adresse"</em> den kopierten Kalenderlink einfügen.
|
||||
</li>
|
||||
<li>
|
||||
Anschließend kannst du noch einen Namen vergeben und weitere
|
||||
Einstellungen nach Belieben vornehmen.
|
||||
</li>
|
||||
<li>{{ $t('faqView.thirdAnswer.thunderbird.one') }}</li>
|
||||
<li>{{ $t('faqView.thirdAnswer.thunderbird.two') }}</li>
|
||||
<li>{{ $t('faqView.thirdAnswer.thunderbird.three') }}</li>
|
||||
<li>{{ $t('faqView.thirdAnswer.thunderbird.four') }} </li>
|
||||
<li>{{ $t('faqView.thirdAnswer.thunderbird.five') }}</li>
|
||||
<li>{{ $t('faqView.thirdAnswer.thunderbird.six') }}</li>
|
||||
<li>{{ $t('faqView.thirdAnswer.thunderbird.seven') }}</li>
|
||||
</ol>
|
||||
</AccordionTab>
|
||||
<AccordionTab header="IPhone">
|
||||
<p>
|
||||
Der einfachste Weg unter iOS ist der iCloud-Sync (siehe
|
||||
Anleitung für OS X Kalender). Hier ist der andere Weg:
|
||||
</p>
|
||||
<AccordionTab :header="$t('faqView.thirdAnswer.iphone.title')">
|
||||
<p>{{$t('faqView.thirdAnswer.iphone.description')}}</p>
|
||||
|
||||
<ol>
|
||||
<li>Erstelle deinen Kalender und kopiere den Link.</li>
|
||||
<li>Gehe in die <em>“Systemeinstellungen”</em>.</li>
|
||||
<li>Dort wählst du <em>“Mail, Kontakte, Kalender”</em>.</li>
|
||||
<li><em>“Account hinzufügen”</em> auswählen.</li>
|
||||
<li>Ganz unten tippst du auf <em>“Andere”</em>.</li>
|
||||
<li>
|
||||
Der letzte Punkt ist <em>“Kalenderabo hinzufügen”</em>. Dort
|
||||
drauftippen.
|
||||
</li>
|
||||
<li>
|
||||
Im daraufhin erscheinenden Textfeld fügst du den Kalenderlink
|
||||
ein und drückst oben auf <em>“weiter”</em>.
|
||||
</li>
|
||||
<li>
|
||||
Du kannst noch eine <em>“Beschreibung”</em> vergeben. Den Rest
|
||||
solltest du lassen, wie er ist.
|
||||
</li>
|
||||
<li>
|
||||
Nach kurzer Zeit taucht der abonnierte Kalender in der
|
||||
Kalender-App auf.
|
||||
</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.one')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.two')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.three')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.four')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.five')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.six')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.seven')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.eight')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.iphone.nine')}}</li>
|
||||
</ol>
|
||||
</AccordionTab>
|
||||
<AccordionTab header="Android">
|
||||
<p>
|
||||
Unter Android ist die Synchronisierung mit dem Google Kalender
|
||||
die einfachste Variante. Schaue bitte in die Google Kalender
|
||||
Anleitung um zu erfahren, wie du den Kalender dort abonnierst.
|
||||
</p>
|
||||
<p>{{$t('faqView.thirdAnswer.android.description')}}</p>
|
||||
</AccordionTab>
|
||||
<AccordionTab header="Windows Phone">
|
||||
<p>
|
||||
Am einfachsten ist unter Windows Phone die Synchronisierung über
|
||||
Outlook.com:
|
||||
</p>
|
||||
|
||||
<p>{{$t('faqView.thirdAnswer.windows_phone.description')}}</p>
|
||||
<ol>
|
||||
<li>Erstelle deinen Kalender und kopiere den Link.</li>
|
||||
<li>Bei Outlook.com anmelden und in den Kalender wechseln.</li>
|
||||
<li>Auf <em>“Subscribe”</em> klicken.</li>
|
||||
<li><em>“Subscribe to a public calendar”</em> auswählen.</li>
|
||||
<li>Bei <em>“Calendar URL”</em> den Kalenderlink einfügen.</li>
|
||||
<li>Sonstige Einstellungen nach Belieben vornehmen.</li>
|
||||
<li>Auf <em>“Subscribe to calendar”</em> klicken.</li>
|
||||
<li>
|
||||
Das Windows-Phone-Gerät muss mit dem gleichen
|
||||
Outlook.com-Benutzerkonto angemeldet sein. Fortan sollte die
|
||||
Synchronisierung des Kalenders automatisch erfolgen.
|
||||
</li>
|
||||
<li>{{$t('faqView.thirdAnswer.windows_phone.one')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.windows_phone.two')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.windows_phone.three')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.windows_phone.four')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.windows_phone.five')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.windows_phone.six')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.windows_phone.seven')}}</li>
|
||||
<li>{{$t('faqView.thirdAnswer.windows_phone.eight')}}</li>
|
||||
</ol>
|
||||
</AccordionTab>
|
||||
</Accordion>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid my-2">
|
||||
<div class="col">
|
||||
Kalender abonnieren? Ich will den <em>downloaden</em>!
|
||||
</div>
|
||||
<div class="col">
|
||||
Das kannst du gern tun. Nachdem dein persönlicher Stundenplan erstellt
|
||||
wurde, hast du die Möglichkeit ihn herunterzuladen. Außerdem kannst du
|
||||
ihn jederzeit herunterladen, wenn du den generierten Link einfach in
|
||||
deinem Browser aufrufst. <br />
|
||||
Bedenke hierbei, dass heruntergeladene Kalender bzw. Stundenpläne sich
|
||||
nicht aktualisieren werden. Das ist nur möglich, wenn du den Kalender
|
||||
abonnierst.
|
||||
</div>
|
||||
<div class="col">{{$t('faqView.fourthQuestion')}}</div>
|
||||
<div class="col">{{$t('faqView.fourthAnswer')}}</div>
|
||||
</div>
|
||||
|
||||
<div class="grid my-2">
|
||||
<div class="col">
|
||||
Ich belege zusätzlich Module aus anderen Studiengängen und möchte
|
||||
diese auch in meinem Stundenplan haben.
|
||||
</div>
|
||||
<div class="col">
|
||||
Nachdem du die Möglichkeit hattest, die für deinen Studiengang
|
||||
vorgesehenen Module aus dem Modulhandbuch auszuwählen, wirst du auf
|
||||
eine zweite Seite weitergeleitet. Dort hast du die Möglichkeit,
|
||||
weitere Module aus anderen Studiengängen in deinen Studienplan
|
||||
einzufügen.
|
||||
</div>
|
||||
<div class="col">{{$t('faqView.fifthQuestion')}}</div>
|
||||
<div class="col">{{$t('faqView.fifthAnswer')}}</div>
|
||||
</div>
|
||||
|
||||
<div class="grid my-2">
|
||||
<div class="col my-2">Aktualisierung Probleme mit dem Kalender?</div>
|
||||
<div class="col">
|
||||
Das liegt vermutlich daran, dass du ihn heruntergeladen statt
|
||||
abonniert hast. Automatisch aktualisieren können sich nur Kalender,
|
||||
die du abonniert hast. Eine Aktualisierung des Kalenders auf dem
|
||||
Server wird täglich um 4:00 Uhr durchgeführt. So wird gewährleistet
|
||||
das alle Veränderungen seitens der HTWK übernommen werden.
|
||||
</div>
|
||||
<div class="col my-2">{{$t('faqView.sixthQuestion')}}</div>
|
||||
<div class="col">{{$t('faqView.sixthAnswer')}}</div>
|
||||
</div>
|
||||
|
||||
<div class="grid my-2">
|
||||
<div class="col">
|
||||
Wie lange ist mein Stundenplan bzw. der Link dorthin gültig?
|
||||
</div>
|
||||
<div class="col">
|
||||
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.
|
||||
</div>
|
||||
<div class="col">{{$t('faqView.seventhQuestion')}}</div>
|
||||
<div class="col">{{$t('faqView.seventhAnswer')}}</div>
|
||||
</div>
|
||||
|
||||
<div class="grid my-2">
|
||||
<div class="col">Preis und Entwicklung?</div>
|
||||
<div class="col">
|
||||
Die Kosten können durch das selbständiges Hosting vollständig
|
||||
ausgelagert werden. Die Entwicklung soll als aktives Git Projekt auch
|
||||
durch die Community verwaltet werden.
|
||||
</div>
|
||||
<div class="col">{{$t('faqView.eighthQuestion')}}</div>
|
||||
<div class="col">{{$t('faqView.eighthAnswer')}}</div>
|
||||
</div>
|
||||
<p>
|
||||
Nicht gefunden, wonach du suchst?<br />
|
||||
<a href="/imprint">Kontakt aufnehmen</a>
|
||||
{{$t('faqView.notFound')}}<br />
|
||||
<a href="/imprint">{{$t('faqView.contact')}}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,85 +0,0 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div class="flex align-items-center justify-content-center flex-column">
|
||||
<div class="flex align-items-center justify-content-center h-4rem mt-2">
|
||||
<h3 class="text-4xl">Imprint</h3>
|
||||
</div>
|
||||
<div class="flex flex-column col-7">
|
||||
<p>nach dem Telemediengesetz (TMG) der Bundesrepublik Deutschland.</p>
|
||||
|
||||
<h2>Kontakt</h2>
|
||||
<p>
|
||||
Per Email: <a href="mailto:support@ekresse.de">support@ekresse.de</a>
|
||||
</p>
|
||||
|
||||
<h2>Adresse</h2>
|
||||
<p>
|
||||
Angaben gemäß § 5 TMG und verantwortlich für den Inhalt nach § 55 Abs. 2
|
||||
RStV:
|
||||
</p>
|
||||
<p>
|
||||
Elmar Kresse<br />
|
||||
Philipp-Rosenthal-Straße 33<br />
|
||||
04103 Leipzig
|
||||
</p>
|
||||
|
||||
<h2>Haftungsausschluss</h2>
|
||||
|
||||
<h3>Haftung für Inhalte</h3>
|
||||
<p>
|
||||
Ich bemühe mich die Inhalte der Seite aktuell zu halten. Trotz
|
||||
sorgfältiger Bearbeitung bleibt eine Haftung ausgeschlossen.
|
||||
</p>
|
||||
<p>
|
||||
Als Diensteanbieter bin ich gemäß § 7 Abs.1 TMG für eigene Inhalte auf
|
||||
diesen Seiten nach den allgemeinen Gesetzen verantwortlich.
|
||||
</p>
|
||||
<p>
|
||||
Nach §§ 8 bis 10 TMG bin ich als Diensteanbieter jedoch nicht
|
||||
verpflichtet, übermittelte oder gespeicherte fremde Informationen zu
|
||||
überwachen. Bei bekannt werden von Rechtsverletzungen, werde ich diese
|
||||
Inhalte umgehend entfernen. Eine diesbezügliche Haftung übernehme ich
|
||||
erst ab dem Zeitpunkt der Kenntnis einer möglichen Rechtsverletzung.
|
||||
</p>
|
||||
|
||||
<h3>Haftung für Links</h3>
|
||||
<p>
|
||||
Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren
|
||||
Inhalte wir keinen Einfluss haben. Für die Inhalte der verlinkten Seiten
|
||||
ist stets der jeweilige Anbieter oder Betreiber der Seiten
|
||||
verantwortlich. Für die Inhalte und die Richtigkeit der Informationen
|
||||
verlinkter Websites fremder Informationsanbieter wird keine Gewähr
|
||||
übernommen.
|
||||
</p>
|
||||
<p>
|
||||
Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche
|
||||
Rechtsverstöße <strong>ohne Beanstandung</strong> überprüft. Bei bekannt
|
||||
werden von Rechtsverletzungen werden wir derartige Links umgehend
|
||||
entfernen.
|
||||
</p>
|
||||
|
||||
<h2>Urheberrecht</h2>
|
||||
<p>
|
||||
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen
|
||||
Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung,
|
||||
Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der
|
||||
Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des
|
||||
jeweiligen Autors bzw. Erstellers.
|
||||
</p>
|
||||
|
||||
<h2>Datenschutz</h2>
|
||||
<p>siehe <a href="/privacy-policy">Datenschutzerklärung</a></p>
|
||||
|
||||
<h2>Salvatorische Klausel</h2>
|
||||
<p>
|
||||
Sollte eine Bestimmung des Vertrages unwirksam sein, so bleibt die
|
||||
Wirksamkeit der übrigen unberührt. Die unwirksame Bestimmung ist durch
|
||||
eine Bestimmung zu ersetzen, die dem gewollten Zweck in rechtlich
|
||||
zulässiger Weise am nächsten kommt. Das gleiche gilt für Vertragslücken.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
49
frontend/src/components/LocaleSwitcher.vue
Normal file
49
frontend/src/components/LocaleSwitcher.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import localeStore from "../store/localeStore.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const countries = computed(() => [
|
||||
{ name: t("english"), code: "en", icon: "🇬🇧" },
|
||||
{ name: t("german"), code: "de", icon: "🇩🇪" },
|
||||
]);
|
||||
|
||||
function displayIcon(code: string) {
|
||||
return countries.value.find((country) => country.code === code)?.icon;
|
||||
}
|
||||
|
||||
function displayCountry(code: string) {
|
||||
return countries.value.find((country) => country.code === code)?.name;
|
||||
}
|
||||
|
||||
function updateLocale(locale: string) {
|
||||
localeStore().setLocale(locale);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Dropdown
|
||||
v-model="$i18n.locale"
|
||||
:options="$i18n.availableLocales"
|
||||
option-label="name"
|
||||
placeholder="Select a Language"
|
||||
class="w-full md:w-14rem"
|
||||
@change="updateLocale($event.data)"
|
||||
>
|
||||
<template #value="slotProps">
|
||||
<div v-if="slotProps.value" class="flex align-items-center">
|
||||
<div class="mr-2 flag">{{ displayIcon(slotProps.value) }}</div>
|
||||
<div>{{ displayCountry(slotProps.value) }}</div>
|
||||
</div>
|
||||
<span v-else>
|
||||
{{ slotProps.placeholder }}
|
||||
</span>
|
||||
</template>
|
||||
<template #option="slotProps">
|
||||
<div class="flex align-items-center">
|
||||
<div class="mr-2 flag">{{ displayIcon(slotProps.option) }}</div>
|
||||
<div>{{ displayCountry(slotProps.option) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</template>
|
@@ -1,43 +1,69 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import LocaleSwitcher from "./LocaleSwitcher.vue";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const items = ref([
|
||||
const items = computed(() => [
|
||||
{
|
||||
label: "Create Calendar",
|
||||
label: t("createCalendar"),
|
||||
icon: "pi pi-fw pi-plus",
|
||||
url: "/",
|
||||
route: "/",
|
||||
},
|
||||
{
|
||||
label: "Edit Calendar",
|
||||
label: t("editCalendar"),
|
||||
icon: "pi pi-fw pi-pencil",
|
||||
url: "/edit",
|
||||
route: "/edit",
|
||||
},
|
||||
{
|
||||
label: "Check Room Availability",
|
||||
label: t("roomFinder"),
|
||||
icon: "pi pi-fw pi-calendar",
|
||||
url: "/rooms",
|
||||
route: `rooms`,
|
||||
},
|
||||
{
|
||||
label: "FAQ",
|
||||
label: t("faq"),
|
||||
icon: "pi pi-fw pi-book",
|
||||
url: "/faq",
|
||||
route: `faq`,
|
||||
},
|
||||
{
|
||||
label: "Imprint",
|
||||
label: t("imprint"),
|
||||
icon: "pi pi-fw pi-id-card",
|
||||
url: "/imprint",
|
||||
route: `imprint`,
|
||||
},
|
||||
{
|
||||
label: "Privacy",
|
||||
url: "/privacy-policy",
|
||||
label: t("privacy"),
|
||||
icon: "pi pi-fw pi-exclamation-triangle",
|
||||
route: `privacy-policy`,
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Menubar :model="items" class="menubar justify-content-center">
|
||||
<template #start></template>
|
||||
<Menubar :model="items" class="menubar justify-content-between">
|
||||
<template #start>
|
||||
<img
|
||||
width="35"
|
||||
height="40"
|
||||
src="../../public/htwk.svg"
|
||||
alt="Logo"
|
||||
class="h-10 w-10 mr-6"
|
||||
style="margin-left: 1rem"
|
||||
/>
|
||||
</template>
|
||||
<template #item="{ item }">
|
||||
<router-link v-slot="{ navigate }" :to="item.route" custom>
|
||||
<Button
|
||||
:label="String(item.label)"
|
||||
:icon="item.icon"
|
||||
severity="secondary"
|
||||
text
|
||||
@click="navigate"
|
||||
/>
|
||||
</router-link>
|
||||
</template>
|
||||
<template #end>
|
||||
<LocaleSwitcher></LocaleSwitcher>
|
||||
</template>
|
||||
</Menubar>
|
||||
</template>
|
||||
|
||||
|
@@ -1,10 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import { inject } from "vue";
|
||||
import { Module } from "../model/module.ts";
|
||||
import {inject} from "vue";
|
||||
import {Module} from "../model/module.ts";
|
||||
import {Event} from "../model/event.ts";
|
||||
import moment from "moment-timezone";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dialogRef = inject("dialogRef") as any;
|
||||
const module = dialogRef.value.data.module as Module;
|
||||
|
||||
// formats 2023-10-26 11:45:00.000Z to DD-MM-YYYY HH:MM
|
||||
function formatTimestamp(timestampString: string): string {
|
||||
// Den übergebenen Zeitstempel in ein Moment-Objekt umwandeln
|
||||
const timestamp = moment(timestampString);
|
||||
|
||||
// Die Zeitzone auf "Europe/Berlin" setzen
|
||||
const berlinTime = timestamp.tz('Europe/Berlin');
|
||||
|
||||
// Das gewünschte Format für die Ausgabe festlegen
|
||||
return berlinTime.format('DD.MM.YYYY HH:mm');
|
||||
}
|
||||
|
||||
function sortModuleEventsByStart(events: Event[]) {
|
||||
return events.sort((a, b) => {
|
||||
return a.start.localeCompare(b.start);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -12,24 +32,32 @@ const module = dialogRef.value.data.module as Module;
|
||||
<h2>{{ module.name }}</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Course: {{ module.course }}</td>
|
||||
<td>{{ $t("moduleInformation.course") }}: {{ module.course }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Person: {{ module.prof }}</td>
|
||||
<td>{{ $t("moduleInformation.person") }}: {{ module.prof }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Semester: {{ module.semester }}</td>
|
||||
<td>{{ $t("moduleInformation.semester") }}: {{ module.semester }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="card">
|
||||
<DataTable :value="module.events" table-style="min-width: 50rem">
|
||||
<Column field="day" header="Day"></Column>
|
||||
<Column field="start" header="Start"></Column>
|
||||
<Column field="end" header="End"></Column>
|
||||
<Column field="rooms" header="Room"></Column>
|
||||
<Column field="eventType" header="Type"></Column>
|
||||
<Column field="week" header="Week"></Column>
|
||||
<DataTable :value="sortModuleEventsByStart(module.events)" table-style="min-width: 50rem">
|
||||
<Column field="day" :header="$t('moduleInformation.day')"></Column>
|
||||
<Column field="start" :header="$t('moduleInformation.start')">
|
||||
<template #body="slotProps">
|
||||
{{ formatTimestamp(slotProps.data.start) }}
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="end" :header="$t('moduleInformation.end')">
|
||||
<template #body="slotProps">
|
||||
{{formatTimestamp( slotProps.data.end) }}
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="rooms" :header="$t('moduleInformation.room')"></Column>
|
||||
<Column field="eventType" :header="$t('moduleInformation.type')"></Column>
|
||||
<Column field="week" :header="$t('moduleInformation.week')"></Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</td>
|
||||
|
@@ -64,7 +64,7 @@ function nextStep() {
|
||||
:disabled="selectedModules.length < 1"
|
||||
class="col-4 justify-content-center"
|
||||
@click="nextStep()"
|
||||
>Next Step
|
||||
>{{ $t("moduleSelection.nextStep") }}
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center">
|
||||
@@ -72,16 +72,19 @@ function nextStep() {
|
||||
<template #header>
|
||||
<div class="flex justify-content-between flex-wrap">
|
||||
<div class="flex align-items-center justify-content-center">
|
||||
<h3>Modules - {{ selectedModules.length }}</h3>
|
||||
<h3>
|
||||
{{ $t("moduleSelection.modules") }} -
|
||||
{{ selectedModules.length }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center">
|
||||
<ToggleButton
|
||||
v-model="allSelected"
|
||||
class="w-12rem"
|
||||
off-icon="pi pi-times"
|
||||
off-label="Unselect All"
|
||||
:off-label="$t('moduleSelection.deselectAll')"
|
||||
on-icon="pi pi-check"
|
||||
on-label="Select All"
|
||||
:on-label="$t('moduleSelection.selectAll')"
|
||||
@click="selectAllModules(!allSelected)"
|
||||
/>
|
||||
</div>
|
||||
@@ -89,7 +92,7 @@ function nextStep() {
|
||||
</template>
|
||||
<template #empty>
|
||||
<p class="p-4 text-2xl font-bold text-900 empty-message">
|
||||
No Modules found for this course
|
||||
{{ $t("moduleSelection.noModulesAvailable") }}
|
||||
</p>
|
||||
</template>
|
||||
<template #list="slotProps">
|
||||
@@ -112,9 +115,9 @@ function nextStep() {
|
||||
v-model="modulesWithSelection[slotProps.index].selected"
|
||||
class="w-9rem"
|
||||
off-icon="pi pi-times"
|
||||
off-label="Unselected"
|
||||
:off-label="$t('moduleSelection.unselected')"
|
||||
on-icon="pi pi-check"
|
||||
on-label="Selected"
|
||||
:on-label="$t('moduleSelection.selected')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,18 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { Ref, ref } from "vue";
|
||||
import { computed, Ref, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const helpVisible: Ref<boolean> = ref(false);
|
||||
|
||||
const placeholders = ref([
|
||||
const placeholders = computed(() => [
|
||||
{
|
||||
placeholder: "%t",
|
||||
description: "Event Type",
|
||||
examples: "V = Vorlesung, S = Seminar, P = Praktikum/Prüfung",
|
||||
description: t("moduleTemplateDialog.eventTyp"),
|
||||
examples:
|
||||
"V = " +
|
||||
t("moduleTemplateDialog.lecture") +
|
||||
", S = " +
|
||||
t("moduleTemplateDialog.seminar") +
|
||||
", P = " +
|
||||
t("moduleTemplateDialog.exam"),
|
||||
},
|
||||
{
|
||||
placeholder: "%p",
|
||||
description: "Mandatory",
|
||||
examples: "w = optional, p = mandatory",
|
||||
description: t("moduleTemplateDialog.mandatory"),
|
||||
examples:
|
||||
"w = " +
|
||||
t("moduleTemplateDialog.optional") +
|
||||
", p = " +
|
||||
t("moduleTemplateDialog.mandatory"),
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
@@ -28,20 +40,30 @@ const placeholders = ref([
|
||||
aria-label="Help"
|
||||
@click="helpVisible = true"
|
||||
/>
|
||||
<Dialog v-model:visible="helpVisible" header="Module configuration">
|
||||
<Dialog
|
||||
v-model:visible="helpVisible"
|
||||
:header="$t('moduleTemplateDialog.moduleConfiguration')"
|
||||
>
|
||||
<p>
|
||||
Here you can rename your modules to your liking. This will be the name of
|
||||
the event in your calendar.
|
||||
{{ t("moduleTemplateDialog.explanationOne") }}
|
||||
</p>
|
||||
<p>You can use the following placeholders in your module names:</p>
|
||||
<p>{{ t("moduleTemplateDialog.tableDescription") }}</p>
|
||||
<DataTable :value="placeholders">
|
||||
<Column field="placeholder" header="Placeholder"></Column>
|
||||
<Column field="description" header="Description"></Column>
|
||||
<Column field="examples" header="Examples"></Column>
|
||||
<Column
|
||||
field="placeholder"
|
||||
:header="$t('moduleTemplateDialog.placeholder')"
|
||||
></Column>
|
||||
<Column
|
||||
field="description"
|
||||
:header="$t('moduleTemplateDialog.description')"
|
||||
></Column>
|
||||
<Column
|
||||
field="examples"
|
||||
:header="$t('moduleTemplateDialog.examples')"
|
||||
></Column>
|
||||
</DataTable>
|
||||
<p>
|
||||
Additionally, you can toggle notifications for each module. If you do so,
|
||||
you will be notified 15 minutes before the event starts.
|
||||
{{ t("moduleTemplateDialog.explanationTwo") }}
|
||||
</p>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
@@ -1,646 +0,0 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div class="flex align-items-center justify-content-center flex-column">
|
||||
<div class="flex align-items-center justify-content-center h-4rem mt-2">
|
||||
<h3 class="text-4xl">Privacy policy</h3>
|
||||
</div>
|
||||
<div class="flex flex-column col-7">
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
<p>Stand: 19. September 2023</p>
|
||||
<p>
|
||||
Mit der folgenden Datenschutzerklärung möchten wir Sie darüber
|
||||
aufklären, welche Arten Ihrer personenbezogenen Daten (nachfolgend auch
|
||||
kurz als "Daten“ bezeichnet) wir zu welchen Zwecken und in welchem
|
||||
Umfang im Rahmen der Bereitstellung unserer Applikation verarbeiten. Die
|
||||
verwendeten Begriffe sind nicht geschlechtsspezifisch.
|
||||
</p>
|
||||
|
||||
<h2>Inhaltsübersicht</h2>
|
||||
<ul class="index">
|
||||
<li><a class="index-link" href="#m3">Verantwortlicher</a></li>
|
||||
<li>
|
||||
<a class="index-link" href="#mOverview"
|
||||
>Übersicht der Verarbeitungen</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="index-link" href="#m2427">Maßgebliche Rechtsgrundlagen</a>
|
||||
</li>
|
||||
<li><a class="index-link" href="#m27">Sicherheitsmaßnahmen</a></li>
|
||||
<li>
|
||||
<a class="index-link" href="#m25"
|
||||
>Übermittlung von personenbezogenen Daten</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="index-link" href="#m24">Internationale Datentransfers</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="index-link" href="#m10">Rechte der betroffenen Personen</a>
|
||||
</li>
|
||||
<li><a class="index-link" href="#m134">Einsatz von Cookies</a></li>
|
||||
<li>
|
||||
<a class="index-link" href="#m225"
|
||||
>Bereitstellung des Onlineangebotes und Webhosting</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="index-link" href="#m182">Kontakt- und Anfragenverwaltung</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="m3">Verantwortlicher</h2>
|
||||
<p>
|
||||
Elmar Kresse<br />Philipp-Rosenthal-Straße 33<br />04103, Leipzig,
|
||||
Deutschland
|
||||
</p>
|
||||
E-Mail-Adresse:
|
||||
<p><a href="mailto:support@ekresse.de">support@ekresse.de</a></p>
|
||||
|
||||
<h2 id="mOverview">Übersicht der Verarbeitungen</h2>
|
||||
<p>
|
||||
Die nachfolgende Übersicht fasst die Arten der verarbeiteten Daten und
|
||||
die Zwecke ihrer Verarbeitung zusammen und verweist auf die betroffenen
|
||||
Personen.
|
||||
</p>
|
||||
<h3>Arten der verarbeiteten Daten</h3>
|
||||
<ul>
|
||||
<li>Kontaktdaten.</li>
|
||||
<li>Inhaltsdaten.</li>
|
||||
<li>Nutzungsdaten.</li>
|
||||
<li>Meta-, Kommunikations- und Verfahrensdaten.</li>
|
||||
</ul>
|
||||
<h3>Kategorien betroffener Personen</h3>
|
||||
<ul>
|
||||
<li>Kommunikationspartner.</li>
|
||||
<li>Nutzer.</li>
|
||||
</ul>
|
||||
<h3>Zwecke der Verarbeitung</h3>
|
||||
<ul>
|
||||
<li>Kontaktanfragen und Kommunikation.</li>
|
||||
<li>Sicherheitsmaßnahmen.</li>
|
||||
<li>Verwaltung und Beantwortung von Anfragen.</li>
|
||||
<li>Feedback.</li>
|
||||
<li>
|
||||
Bereitstellung unseres Onlineangebotes und Nutzerfreundlichkeit.
|
||||
</li>
|
||||
<li>Informationstechnische Infrastruktur.</li>
|
||||
</ul>
|
||||
<h2 id="m2427">Maßgebliche Rechtsgrundlagen</h2>
|
||||
<p>
|
||||
<strong>Maßgebliche Rechtsgrundlagen nach der DSGVO: </strong>Im
|
||||
Folgenden erhalten Sie eine Übersicht der Rechtsgrundlagen der DSGVO,
|
||||
auf deren Basis wir personenbezogene Daten verarbeiten. Bitte nehmen Sie
|
||||
zur Kenntnis, dass neben den Regelungen der DSGVO nationale
|
||||
Datenschutzvorgaben in Ihrem bzw. unserem Wohn- oder Sitzland gelten
|
||||
können. Sollten ferner im Einzelfall speziellere Rechtsgrundlagen
|
||||
maßgeblich sein, teilen wir Ihnen diese in der Datenschutzerklärung mit.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Einwilligung (Art. 6 Abs. 1 S. 1 lit. a) DSGVO)</strong> - Die
|
||||
betroffene Person hat ihre Einwilligung in die Verarbeitung der sie
|
||||
betreffenden personenbezogenen Daten für einen spezifischen Zweck oder
|
||||
mehrere bestimmte Zwecke gegeben.
|
||||
</li>
|
||||
<li>
|
||||
<strong
|
||||
>Berechtigte Interessen (Art. 6 Abs. 1 S. 1 lit. f) DSGVO)</strong
|
||||
>
|
||||
- Die Verarbeitung ist zur Wahrung der berechtigten Interessen des
|
||||
Verantwortlichen oder eines Dritten erforderlich, sofern nicht die
|
||||
Interessen oder Grundrechte und Grundfreiheiten der betroffenen
|
||||
Person, die den Schutz personenbezogener Daten erfordern, überwiegen.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong>Nationale Datenschutzregelungen in Deutschland: </strong
|
||||
>Zusätzlich zu den Datenschutzregelungen der DSGVO gelten nationale
|
||||
Regelungen zum Datenschutz in Deutschland. Hierzu gehört insbesondere
|
||||
das Gesetz zum Schutz vor Missbrauch personenbezogener Daten bei der
|
||||
Datenverarbeitung (Bundesdatenschutzgesetz – BDSG). Das BDSG enthält
|
||||
insbesondere Spezialregelungen zum Recht auf Auskunft, zum Recht auf
|
||||
Löschung, zum Widerspruchsrecht, zur Verarbeitung besonderer Kategorien
|
||||
personenbezogener Daten, zur Verarbeitung für andere Zwecke und zur
|
||||
Übermittlung sowie automatisierten Entscheidungsfindung im Einzelfall
|
||||
einschließlich Profiling. Ferner können Landesdatenschutzgesetze der
|
||||
einzelnen Bundesländer zur Anwendung gelangen.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Hinweis auf Geltung DSGVO und Schweizer DSG: </strong>Diese
|
||||
Datenschutzhinweise dienen sowohl der Informationserteilung nach dem
|
||||
schweizerischen Bundesgesetz über den Datenschutz (Schweizer DSG) als
|
||||
auch nach der Datenschutzgrundverordnung (DSGVO). Aus diesem Grund
|
||||
bitten wir Sie zu beachten, dass aufgrund der breiteren räumlichen
|
||||
Anwendung und Verständlichkeit die Begriffe der DSGVO verwendet werden.
|
||||
Insbesondere statt der im Schweizer DSG verwendeten Begriffe
|
||||
„Bearbeitung" von „Personendaten", "überwiegendes Interesse" und
|
||||
"besonders schützenswerte Personendaten" werden die in der DSGVO
|
||||
verwendeten Begriffe „Verarbeitung" von „personenbezogenen Daten" sowie
|
||||
"berechtigtes Interesse" und "besondere Kategorien von Daten" verwendet.
|
||||
Die gesetzliche Bedeutung der Begriffe wird jedoch im Rahmen der Geltung
|
||||
des Schweizer DSG weiterhin nach dem Schweizer DSG bestimmt.
|
||||
</p>
|
||||
|
||||
<h2 id="m27">Sicherheitsmaßnahmen</h2>
|
||||
<p>
|
||||
Wir treffen nach Maßgabe der gesetzlichen Vorgaben unter
|
||||
Berücksichtigung des Stands der Technik, der Implementierungskosten und
|
||||
der Art, des Umfangs, der Umstände und der Zwecke der Verarbeitung sowie
|
||||
der unterschiedlichen Eintrittswahrscheinlichkeiten und des Ausmaßes der
|
||||
Bedrohung der Rechte und Freiheiten natürlicher Personen geeignete
|
||||
technische und organisatorische Maßnahmen, um ein dem Risiko
|
||||
angemessenes Schutzniveau zu gewährleisten.
|
||||
</p>
|
||||
<p>
|
||||
Zu den Maßnahmen gehören insbesondere die Sicherung der Vertraulichkeit,
|
||||
Integrität und Verfügbarkeit von Daten durch Kontrolle des physischen
|
||||
und elektronischen Zugangs zu den Daten als auch des sie betreffenden
|
||||
Zugriffs, der Eingabe, der Weitergabe, der Sicherung der Verfügbarkeit
|
||||
und ihrer Trennung. Des Weiteren haben wir Verfahren eingerichtet, die
|
||||
eine Wahrnehmung von Betroffenenrechten, die Löschung von Daten und
|
||||
Reaktionen auf die Gefährdung der Daten gewährleisten. Ferner
|
||||
berücksichtigen wir den Schutz personenbezogener Daten bereits bei der
|
||||
Entwicklung bzw. Auswahl von Hardware, Software sowie Verfahren
|
||||
entsprechend dem Prinzip des Datenschutzes, durch Technikgestaltung und
|
||||
durch datenschutzfreundliche Voreinstellungen.
|
||||
</p>
|
||||
<p>
|
||||
TLS-Verschlüsselung (https): Um Ihre via unserem Online-Angebot
|
||||
übermittelten Daten zu schützen, nutzen wir eine TLS-Verschlüsselung.
|
||||
Sie erkennen derart verschlüsselte Verbindungen an dem Präfix https://
|
||||
in der Adresszeile Ihres Browsers.
|
||||
</p>
|
||||
|
||||
<h2 id="m25">Übermittlung von personenbezogenen Daten</h2>
|
||||
<p>
|
||||
Im Rahmen unserer Verarbeitung von personenbezogenen Daten kommt es vor,
|
||||
dass die Daten an andere Stellen, Unternehmen, rechtlich selbstständige
|
||||
Organisationseinheiten oder Personen übermittelt oder sie ihnen
|
||||
gegenüber offengelegt werden. Zu den Empfängern dieser Daten können z.B.
|
||||
mit IT-Aufgaben beauftragte Dienstleister oder Anbieter von Diensten und
|
||||
Inhalten, die in eine Webseite eingebunden werden, gehören. In solchen
|
||||
Fällen beachten wir die gesetzlichen Vorgaben und schließen insbesondere
|
||||
entsprechende Verträge bzw. Vereinbarungen, die dem Schutz Ihrer Daten
|
||||
dienen, mit den Empfängern Ihrer Daten ab.
|
||||
</p>
|
||||
|
||||
<h2 id="m24">Internationale Datentransfers</h2>
|
||||
<p>
|
||||
Datenverarbeitung in Drittländern: Sofern wir Daten in einem Drittland
|
||||
(d.h., außerhalb der Europäischen Union (EU), des Europäischen
|
||||
Wirtschaftsraums (EWR)) verarbeiten oder die Verarbeitung im Rahmen der
|
||||
Inanspruchnahme von Diensten Dritter oder der Offenlegung bzw.
|
||||
Übermittlung von Daten an andere Personen, Stellen oder Unternehmen
|
||||
stattfindet, erfolgt dies nur im Einklang mit den gesetzlichen Vorgaben.
|
||||
Sofern das Datenschutzniveau in dem Drittland mittels eines
|
||||
Angemessenheitsbeschlusses anerkannt wurde (Art. 45 DSGVO), dient dieser
|
||||
als Grundlage des Datentransfers. Im Übrigen erfolgen Datentransfers nur
|
||||
dann, wenn das Datenschutzniveau anderweitig gesichert ist, insbesondere
|
||||
durch Standardvertragsklauseln (Art. 46 Abs. 2 lit. c) DSGVO),
|
||||
ausdrückliche Einwilligung oder im Fall vertraglicher oder gesetzlich
|
||||
erforderlicher Übermittlung (Art. 49 Abs. 1 DSGVO). Im Übrigen teilen
|
||||
wir Ihnen die Grundlagen der Drittlandübermittlung bei den einzelnen
|
||||
Anbietern aus dem Drittland mit, wobei die Angemesenheitsbeschlüsse als
|
||||
Grundlagen vorrangig gelten. Informationen zu Drittlandtransfers und
|
||||
vorliegenden Angemessenheitsbeschlüssen können dem Informationsangebot
|
||||
der EU-Kommission entnommen werden:
|
||||
<a
|
||||
href="https://ec.europa.eu/info/law/law-topic/data-protection/international-dimension-data-protection_de"
|
||||
target="_blank"
|
||||
>https://ec.europa.eu/info/law/law-topic/data-protection/international-dimension-data-protection_de.</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
EU-US Trans-Atlantic Data Privacy Framework: Im Rahmen des sogenannten
|
||||
„Data Privacy Framework" (DPF) hat die EU-Kommission das
|
||||
Datenschutzniveau ebenfalls für bestimmte Unternehmen aus den USA im
|
||||
Rahmen der Angemessenheitsbeschlusses vom 10.07.2023 als sicher
|
||||
anerkannt. Die Liste der zertifizierten Unternehmen als auch weitere
|
||||
Informationen zu dem DPF können Sie der Webseite des Handelsministeriums
|
||||
der USA unter
|
||||
<a href="https://www.dataprivacyframework.gov/" target="_blank"
|
||||
>https://www.dataprivacyframework.gov/</a
|
||||
>
|
||||
(in Englisch) entnehmen. Wir informieren Sie im Rahmen der
|
||||
Datenschutzhinweise welche von uns eingesetzten Diensteanbieter unter
|
||||
dem Data Privacy Framework zertifiziert sind.
|
||||
</p>
|
||||
|
||||
<h2 id="m10">Rechte der betroffenen Personen</h2>
|
||||
<p>
|
||||
Rechte der betroffenen Personen aus der DSGVO: Ihnen stehen als
|
||||
Betroffene nach der DSGVO verschiedene Rechte zu, die sich insbesondere
|
||||
aus Art. 15 bis 21 DSGVO ergeben:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong
|
||||
>Widerspruchsrecht: Sie haben das Recht, aus Gründen, die sich aus
|
||||
Ihrer besonderen Situation ergeben, jederzeit gegen die Verarbeitung
|
||||
der Sie betreffenden personenbezogenen Daten, die aufgrund von Art.
|
||||
6 Abs. 1 lit. e oder f DSGVO erfolgt, Widerspruch einzulegen; dies
|
||||
gilt auch für ein auf diese Bestimmungen gestütztes Profiling.
|
||||
Werden die Sie betreffenden personenbezogenen Daten verarbeitet, um
|
||||
Direktwerbung zu betreiben, haben Sie das Recht, jederzeit
|
||||
Widerspruch gegen die Verarbeitung der Sie betreffenden
|
||||
personenbezogenen Daten zum Zwecke derartiger Werbung einzulegen;
|
||||
dies gilt auch für das Profiling, soweit es mit solcher
|
||||
Direktwerbung in Verbindung steht.</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Widerrufsrecht bei Einwilligungen:</strong> Sie haben das
|
||||
Recht, erteilte Einwilligungen jederzeit zu widerrufen.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Auskunftsrecht:</strong> Sie haben das Recht, eine Bestätigung
|
||||
darüber zu verlangen, ob betreffende Daten verarbeitet werden und auf
|
||||
Auskunft über diese Daten sowie auf weitere Informationen und Kopie
|
||||
der Daten entsprechend den gesetzlichen Vorgaben.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Recht auf Berichtigung:</strong> Sie haben entsprechend den
|
||||
gesetzlichen Vorgaben das Recht, die Vervollständigung der Sie
|
||||
betreffenden Daten oder die Berichtigung der Sie betreffenden
|
||||
unrichtigen Daten zu verlangen.
|
||||
</li>
|
||||
<li>
|
||||
<strong
|
||||
>Recht auf Löschung und Einschränkung der Verarbeitung:</strong
|
||||
>
|
||||
Sie haben nach Maßgabe der gesetzlichen Vorgaben das Recht, zu
|
||||
verlangen, dass Sie betreffende Daten unverzüglich gelöscht werden,
|
||||
bzw. alternativ nach Maßgabe der gesetzlichen Vorgaben eine
|
||||
Einschränkung der Verarbeitung der Daten zu verlangen.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Recht auf Datenübertragbarkeit:</strong> Sie haben das Recht,
|
||||
Sie betreffende Daten, die Sie uns bereitgestellt haben, nach Maßgabe
|
||||
der gesetzlichen Vorgaben in einem strukturierten, gängigen und
|
||||
maschinenlesbaren Format zu erhalten oder deren Übermittlung an einen
|
||||
anderen Verantwortlichen zu fordern.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Beschwerde bei Aufsichtsbehörde:</strong> Sie haben
|
||||
unbeschadet eines anderweitigen verwaltungsrechtlichen oder
|
||||
gerichtlichen Rechtsbehelfs das Recht auf Beschwerde bei einer
|
||||
Aufsichtsbehörde, insbesondere in dem Mitgliedstaat ihres gewöhnlichen
|
||||
Aufenthaltsorts, ihres Arbeitsplatzes oder des Orts des mutmaßlichen
|
||||
Verstoßes, wenn Sie der Ansicht sind, dass die Verarbeitung der Sie
|
||||
betreffenden personenbezogenen Daten gegen die Vorgaben der DSGVO
|
||||
verstößt.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="m134">Einsatz von Cookies</h2>
|
||||
<p>
|
||||
Cookies sind kleine Textdateien, bzw. sonstige Speichervermerke, die
|
||||
Informationen auf Endgeräten speichern und Informationen aus den
|
||||
Endgeräten auslesen. Z.B. um den Login-Status in einem Nutzerkonto,
|
||||
einen Warenkorbinhalt in einem E-Shop, die aufgerufenen Inhalte oder
|
||||
verwendete Funktionen eines Onlineangebotes speichern. Cookies können
|
||||
ferner zu unterschiedlichen Zwecken eingesetzt werden, z.B. zu Zwecken
|
||||
der Funktionsfähigkeit, Sicherheit und Komfort von Onlineangeboten sowie
|
||||
der Erstellung von Analysen der Besucherströme.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Hinweise zur Einwilligung: </strong>Wir setzen Cookies im
|
||||
Einklang mit den gesetzlichen Vorschriften ein. Daher holen wir von den
|
||||
Nutzern eine vorhergehende Einwilligung ein, außer wenn diese gesetzlich
|
||||
nicht gefordert ist. Eine Einwilligung ist insbesondere nicht notwendig,
|
||||
wenn das Speichern und das Auslesen der Informationen, also auch von
|
||||
Cookies, unbedingt erforderlich sind, um dem den Nutzern einen von ihnen
|
||||
ausdrücklich gewünschten Telemediendienst (also unser Onlineangebot) zur
|
||||
Verfügung zu stellen. Zu den unbedingt erforderlichen Cookies gehören in
|
||||
der Regel Cookies mit Funktionen, die der Anzeige und Lauffähigkeit des
|
||||
Onlineangebotes , dem Lastausgleich, der Sicherheit, der Speicherung der
|
||||
Präferenzen und Auswahlmöglichkeiten der Nutzer oder ähnlichen mit der
|
||||
Bereitstellung der Haupt- und Nebenfunktionen des von den Nutzern
|
||||
angeforderten Onlineangebotes zusammenhängenden Zwecken dienen. Die
|
||||
widerrufliche Einwilligung wird gegenüber den Nutzern deutlich
|
||||
kommuniziert und enthält die Informationen zu der jeweiligen
|
||||
Cookie-Nutzung.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Hinweise zu datenschutzrechtlichen Rechtsgrundlagen: </strong
|
||||
>Auf welcher datenschutzrechtlichen Rechtsgrundlage wir die
|
||||
personenbezogenen Daten der Nutzer mit Hilfe von Cookies verarbeiten,
|
||||
hängt davon ab, ob wir Nutzer um eine Einwilligung bitten. Falls die
|
||||
Nutzer einwilligen, ist die Rechtsgrundlage der Verarbeitung Ihrer Daten
|
||||
die erklärte Einwilligung. Andernfalls werden die mithilfe von Cookies
|
||||
verarbeiteten Daten auf Grundlage unserer berechtigten Interessen (z.B.
|
||||
an einem betriebswirtschaftlichen Betrieb unseres Onlineangebotes und
|
||||
Verbesserung seiner Nutzbarkeit) verarbeitet oder, wenn dies im Rahmen
|
||||
der Erfüllung unserer vertraglichen Pflichten erfolgt, wenn der Einsatz
|
||||
von Cookies erforderlich ist, um unsere vertraglichen Verpflichtungen zu
|
||||
erfüllen. Zu welchen Zwecken die Cookies von uns verarbeitet werden,
|
||||
darüber klären wir im Laufe dieser Datenschutzerklärung oder im Rahmen
|
||||
von unseren Einwilligungs- und Verarbeitungsprozessen auf.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Speicherdauer:</strong>Im Hinblick auf die Speicherdauer werden
|
||||
die folgenden Arten von Cookies unterschieden:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong
|
||||
>Temporäre Cookies (auch: Session- oder Sitzungs-Cookies):</strong
|
||||
>
|
||||
Temporäre Cookies werden spätestens gelöscht, nachdem ein Nutzer ein
|
||||
Online-Angebot verlassen und sein Endgerät (z.B. Browser oder mobile
|
||||
Applikation) geschlossen hat.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Permanente Cookies:</strong> Permanente Cookies bleiben auch
|
||||
nach dem Schließen des Endgerätes gespeichert. So können
|
||||
beispielsweise der Login-Status gespeichert oder bevorzugte Inhalte
|
||||
direkt angezeigt werden, wenn der Nutzer eine Website erneut besucht.
|
||||
Ebenso können die mit Hilfe von Cookies erhobenen Daten der Nutzer zur
|
||||
Reichweitenmessung verwendet werden. Sofern wir Nutzern keine
|
||||
expliziten Angaben zur Art und Speicherdauer von Cookies mitteilen
|
||||
(z.B. im Rahmen der Einholung der Einwilligung), sollten Nutzer davon
|
||||
ausgehen, dass Cookies permanent sind und die Speicherdauer bis zu
|
||||
zwei Jahre betragen kann.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong
|
||||
>Allgemeine Hinweise zum Widerruf und Widerspruch (sog. "Opt-Out"): </strong
|
||||
>Nutzer können die von ihnen abgegebenen Einwilligungen jederzeit
|
||||
widerrufen und der Verarbeitung entsprechend den gesetzlichen Vorgaben
|
||||
widersprechen. Hierzu können Nutzer unter anderem die Verwendung von
|
||||
Cookies in den Einstellungen ihres Browsers einschränken (wobei dadurch
|
||||
auch die Funktionalität unseres Onlineangebotes eingeschränkt sein
|
||||
kann). Ein Widerspruch gegen die Verwendung von Cookies zu
|
||||
Online-Marketing-Zwecken kann auch über die Websites
|
||||
<a href="https://optout.aboutads.info/" target="_new"
|
||||
>https://optout.aboutads.info</a
|
||||
>
|
||||
und
|
||||
<a href="https://www.youronlinechoices.com/" target="_new"
|
||||
>https://www.youronlinechoices.com/</a
|
||||
>
|
||||
erklärt werden.
|
||||
</p>
|
||||
<ul class="m-elements">
|
||||
<li class="">
|
||||
<strong>Rechtsgrundlagen:</strong> Berechtigte Interessen (Art. 6 Abs.
|
||||
1 S. 1 lit. f) DSGVO). Einwilligung (Art. 6 Abs. 1 S. 1 lit. a)
|
||||
DSGVO).
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong
|
||||
>Weitere Hinweise zu Verarbeitungsprozessen, Verfahren und
|
||||
Diensten:</strong
|
||||
>
|
||||
</p>
|
||||
<ul class="m-elements">
|
||||
<li>
|
||||
<strong
|
||||
>Verarbeitung von Cookie-Daten auf Grundlage einer Einwilligung: </strong
|
||||
>Wir setzen ein Verfahren zum Cookie-Einwilligungs-Management ein, in
|
||||
dessen Rahmen die Einwilligungen der Nutzer in den Einsatz von
|
||||
Cookies, bzw. der im Rahmen des
|
||||
Cookie-Einwilligungs-Management-Verfahrens genannten Verarbeitungen
|
||||
und Anbieter eingeholt sowie von den Nutzern verwaltet und widerrufen
|
||||
werden können. Hierbei wird die Einwilligungserklärung gespeichert, um
|
||||
deren Abfrage nicht erneut wiederholen zu müssen und die Einwilligung
|
||||
entsprechend der gesetzlichen Verpflichtung nachweisen zu können. Die
|
||||
Speicherung kann serverseitig und/oder in einem Cookie (sogenanntes
|
||||
Opt-In-Cookie, bzw. mithilfe vergleichbarer Technologien) erfolgen, um
|
||||
die Einwilligung einem Nutzer, bzw. dessen Gerät zuordnen zu können.
|
||||
Vorbehaltlich individueller Angaben zu den Anbietern von
|
||||
Cookie-Management-Diensten, gelten die folgenden Hinweise: Die Dauer
|
||||
der Speicherung der Einwilligung kann bis zu zwei Jahren betragen.
|
||||
Hierbei wird ein pseudonymer Nutzer-Identifikator gebildet und mit dem
|
||||
Zeitpunkt der Einwilligung, Angaben zur Reichweite der Einwilligung
|
||||
(z.B. welche Kategorien von Cookies und/oder Diensteanbieter) sowie
|
||||
dem Browser, System und verwendeten Endgerät gespeichert;
|
||||
<span class=""
|
||||
><strong>Rechtsgrundlagen:</strong> Einwilligung (Art. 6 Abs. 1 S. 1
|
||||
lit. a) DSGVO).</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="m225">Bereitstellung des Onlineangebotes und Webhosting</h2>
|
||||
<p>
|
||||
Wir verarbeiten die Daten der Nutzer, um ihnen unsere Online-Dienste zur
|
||||
Verfügung stellen zu können. Zu diesem Zweck verarbeiten wir die
|
||||
IP-Adresse des Nutzers, die notwendig ist, um die Inhalte und Funktionen
|
||||
unserer Online-Dienste an den Browser oder das Endgerät der Nutzer zu
|
||||
übermitteln.
|
||||
</p>
|
||||
<ul class="m-elements">
|
||||
<li>
|
||||
<strong>Verarbeitete Datenarten:</strong> Nutzungsdaten (z.B. besuchte
|
||||
Webseiten, Interesse an Inhalten, Zugriffszeiten); Meta-,
|
||||
Kommunikations- und Verfahrensdaten (z.B. IP-Adressen, Zeitangaben,
|
||||
Identifikationsnummern, Einwilligungsstatus).
|
||||
</li>
|
||||
<li>
|
||||
<strong>Betroffene Personen:</strong> Nutzer (z..B. Webseitenbesucher,
|
||||
Nutzer von Onlinediensten).
|
||||
</li>
|
||||
<li>
|
||||
<strong>Zwecke der Verarbeitung:</strong> Bereitstellung unseres
|
||||
Onlineangebotes und Nutzerfreundlichkeit; Informationstechnische
|
||||
Infrastruktur (Betrieb und Bereitstellung von Informationssystemen und
|
||||
technischen Geräten (Computer, Server etc.).). Sicherheitsmaßnahmen.
|
||||
</li>
|
||||
<li class="">
|
||||
<strong>Rechtsgrundlagen:</strong> Berechtigte Interessen (Art. 6 Abs.
|
||||
1 S. 1 lit. f) DSGVO).
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong
|
||||
>Weitere Hinweise zu Verarbeitungsprozessen, Verfahren und
|
||||
Diensten:</strong
|
||||
>
|
||||
</p>
|
||||
<ul class="m-elements">
|
||||
<li>
|
||||
<strong
|
||||
>Bereitstellung Onlineangebot auf eigener/ dedizierter
|
||||
Serverhardware: </strong
|
||||
>Für die Bereitstellung unseres Onlineangebotes nutzen wir von uns
|
||||
betriebene Serverhardware sowie den damit verbundenen Speicherplatz,
|
||||
die Rechenkapazität und die Software;
|
||||
<span class=""
|
||||
><strong>Rechtsgrundlagen:</strong> Berechtigte Interessen (Art. 6
|
||||
Abs. 1 S. 1 lit. f) DSGVO).</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Erhebung von Zugriffsdaten und Logfiles: </strong>Der Zugriff
|
||||
auf unser Onlineangebot wird in Form von so genannten
|
||||
"Server-Logfiles" protokolliert. Zu den Serverlogfiles können die
|
||||
Adresse und Name der abgerufenen Webseiten und Dateien, Datum und
|
||||
Uhrzeit des Abrufs, übertragene Datenmengen, Meldung über
|
||||
erfolgreichen Abruf, Browsertyp nebst Version, das Betriebssystem des
|
||||
Nutzers, Referrer URL (die zuvor besuchte Seite) und im Regelfall
|
||||
IP-Adressen und der anfragende Provider gehören. Die Serverlogfiles
|
||||
können zum einen zu Zwecken der Sicherheit eingesetzt werden, z.B., um
|
||||
eine Überlastung der Server zu vermeiden (insbesondere im Fall von
|
||||
missbräuchlichen Angriffen, sogenannten DDoS-Attacken) und zum
|
||||
anderen, um die Auslastung der Server und ihre Stabilität
|
||||
sicherzustellen;
|
||||
<span class=""
|
||||
><strong>Rechtsgrundlagen:</strong> Berechtigte Interessen (Art. 6
|
||||
Abs. 1 S. 1 lit. f) DSGVO). </span
|
||||
><strong>Löschung von Daten:</strong> Logfile-Informationen werden für
|
||||
die Dauer von maximal 30 Tagen gespeichert und danach gelöscht oder
|
||||
anonymisiert. Daten, deren weitere Aufbewahrung zu Beweiszwecken
|
||||
erforderlich ist, sind bis zur endgültigen Klärung des jeweiligen
|
||||
Vorfalls von der Löschung ausgenommen.
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="m182">Kontakt- und Anfragenverwaltung</h2>
|
||||
<p>
|
||||
Bei der Kontaktaufnahme mit uns (z.B. per Post, Kontaktformular, E-Mail,
|
||||
Telefon oder via soziale Medien) sowie im Rahmen bestehender Nutzer- und
|
||||
Geschäftsbeziehungen werden die Angaben der anfragenden Personen
|
||||
verarbeitet soweit dies zur Beantwortung der Kontaktanfragen und
|
||||
etwaiger angefragter Maßnahmen erforderlich ist.
|
||||
</p>
|
||||
<ul class="m-elements">
|
||||
<li>
|
||||
<strong>Verarbeitete Datenarten:</strong> Kontaktdaten (z.B. E-Mail,
|
||||
Telefonnummern); Inhaltsdaten (z.B. Eingaben in Onlineformularen);
|
||||
Nutzungsdaten (z.B. besuchte Webseiten, Interesse an Inhalten,
|
||||
Zugriffszeiten); Meta-, Kommunikations- und Verfahrensdaten (z.B.
|
||||
IP-Adressen, Zeitangaben, Identifikationsnummern,
|
||||
Einwilligungsstatus).
|
||||
</li>
|
||||
<li><strong>Betroffene Personen:</strong> Kommunikationspartner.</li>
|
||||
<li>
|
||||
<strong>Zwecke der Verarbeitung:</strong> Kontaktanfragen und
|
||||
Kommunikation; Verwaltung und Beantwortung von Anfragen; Feedback
|
||||
(z.B. Sammeln von Feedback via Online-Formular). Bereitstellung
|
||||
unseres Onlineangebotes und Nutzerfreundlichkeit.
|
||||
</li>
|
||||
<li class="">
|
||||
<strong>Rechtsgrundlagen:</strong> Berechtigte Interessen (Art. 6 Abs.
|
||||
1 S. 1 lit. f) DSGVO).
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="m10">Rechte der betroffenen Personen</h2>
|
||||
<p>
|
||||
Ihnen stehen als Betroffene nach der DSGVO verschiedene Rechte zu, die
|
||||
sich insbesondere aus Art. 15 bis 21 DSGVO ergeben:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong
|
||||
>Widerspruchsrecht: Sie haben das Recht, aus Gründen, die sich aus
|
||||
Ihrer besonderen Situation ergeben, jederzeit gegen die Verarbeitung
|
||||
der Sie betreffenden personenbezogenen Daten, die aufgrund von Art.
|
||||
6 Abs. 1 lit. e oder f DSGVO erfolgt, Widerspruch einzulegen; dies
|
||||
gilt auch für ein auf diese Bestimmungen gestütztes Profiling.
|
||||
Werden die Sie betreffenden personenbezogenen Daten verarbeitet, um
|
||||
Direktwerbung zu betreiben, haben Sie das Recht, jederzeit
|
||||
Widerspruch gegen die Verarbeitung der Sie betreffenden
|
||||
personenbezogenen Daten zum Zwecke derartiger Werbung einzulegen;
|
||||
dies gilt auch für das Profiling, soweit es mit solcher
|
||||
Direktwerbung in Verbindung steht.</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Widerrufsrecht bei Einwilligungen:</strong> Sie haben das
|
||||
Recht, erteilte Einwilligungen jederzeit zu widerrufen.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Auskunftsrecht:</strong> Sie haben das Recht, eine Bestätigung
|
||||
darüber zu verlangen, ob betreffende Daten verarbeitet werden und auf
|
||||
Auskunft über diese Daten sowie auf weitere Informationen und Kopie
|
||||
der Daten entsprechend den gesetzlichen Vorgaben.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Recht auf Berichtigung:</strong> Sie haben entsprechend den
|
||||
gesetzlichen Vorgaben das Recht, die Vervollständigung der Sie
|
||||
betreffenden Daten oder die Berichtigung der Sie betreffenden
|
||||
unrichtigen Daten zu verlangen.
|
||||
</li>
|
||||
<li>
|
||||
<strong
|
||||
>Recht auf Löschung und Einschränkung der Verarbeitung:</strong
|
||||
>
|
||||
Sie haben nach Maßgabe der gesetzlichen Vorgaben das Recht, zu
|
||||
verlangen, dass Sie betreffende Daten unverzüglich gelöscht werden,
|
||||
bzw. alternativ nach Maßgabe der gesetzlichen Vorgaben eine
|
||||
Einschränkung der Verarbeitung der Daten zu verlangen.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Recht auf Datenübertragbarkeit:</strong> Sie haben das Recht,
|
||||
Sie betreffende Daten, die Sie uns bereitgestellt haben, nach Maßgabe
|
||||
der gesetzlichen Vorgaben in einem strukturierten, gängigen und
|
||||
maschinenlesbaren Format zu erhalten oder deren Übermittlung an einen
|
||||
anderen Verantwortlichen zu fordern.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Beschwerde bei Aufsichtsbehörde:</strong> Sie haben
|
||||
unbeschadet eines anderweitigen verwaltungsrechtlichen oder
|
||||
gerichtlichen Rechtsbehelfs das Recht auf Beschwerde bei einer
|
||||
Aufsichtsbehörde, insbesondere in dem Mitgliedstaat ihres gewöhnlichen
|
||||
Aufenthaltsorts, ihres Arbeitsplatzes oder des Orts des mutmaßlichen
|
||||
Verstoßes, wenn Sie der Ansicht sind, dass die Verarbeitung der Sie
|
||||
betreffenden personenbezogenen Daten gegen die Vorgaben der DSGVO
|
||||
verstößt.
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="m42">Begriffsdefinitionen</h2>
|
||||
<p>
|
||||
In diesem Abschnitt erhalten Sie eine Übersicht über die in dieser
|
||||
Datenschutzerklärung verwendeten Begrifflichkeiten. Viele der Begriffe
|
||||
sind dem Gesetz entnommen und vor allem im Art. 4 DSGVO definiert. Die
|
||||
gesetzlichen Definitionen sind verbindlich. Die nachfolgenden
|
||||
Erläuterungen sollen dagegen vor allem dem Verständnis dienen. Die
|
||||
Begriffe sind alphabetisch sortiert.
|
||||
</p>
|
||||
<ul class="glossary">
|
||||
<li>
|
||||
<strong>Personenbezogene Daten:</strong> "Personenbezogene Daten“ sind
|
||||
alle Informationen, die sich auf eine identifizierte oder
|
||||
identifizierbare natürliche Person (im Folgenden "betroffene Person“)
|
||||
beziehen; als identifizierbar wird eine natürliche Person angesehen,
|
||||
die direkt oder indirekt, insbesondere mittels Zuordnung zu einer
|
||||
Kennung wie einem Namen, zu einer Kennnummer, zu Standortdaten, zu
|
||||
einer Online-Kennung (z.B. Cookie) oder zu einem oder mehreren
|
||||
besonderen Merkmalen identifiziert werden kann, die Ausdruck der
|
||||
physischen, physiologischen, genetischen, psychischen,
|
||||
wirtschaftlichen, kulturellen oder sozialen Identität dieser
|
||||
natürlichen Person sind.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Verantwortlicher:</strong> Als "Verantwortlicher“ wird die
|
||||
natürliche oder juristische Person, Behörde, Einrichtung oder andere
|
||||
Stelle, die allein oder gemeinsam mit anderen über die Zwecke und
|
||||
Mittel der Verarbeitung von personenbezogenen Daten entscheidet,
|
||||
bezeichnet.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Verarbeitung:</strong> "Verarbeitung" ist jeder mit oder ohne
|
||||
Hilfe automatisierter Verfahren ausgeführte Vorgang oder jede solche
|
||||
Vorgangsreihe im Zusammenhang mit personenbezogenen Daten. Der Begriff
|
||||
reicht weit und umfasst praktisch jeden Umgang mit Daten, sei es das
|
||||
Erheben, das Auswerten, das Speichern, das Übermitteln oder das
|
||||
Löschen.
|
||||
</li>
|
||||
</ul>
|
||||
<p class="seal">
|
||||
<a
|
||||
href="https://datenschutz-generator.de/"
|
||||
rel="noopener noreferrer nofollow"
|
||||
target="_blank"
|
||||
title="Rechtstext von Dr. Schwenke - für weitere Informationen bitte anklicken."
|
||||
>Erstellt mit kostenlosem Datenschutz-Generator.de von Dr. Thomas
|
||||
Schwenke</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
@@ -3,8 +3,11 @@ import moduleStore from "../store/moduleStore.ts";
|
||||
import { createIndividualFeed } from "../api/createFeed.ts";
|
||||
import router from "../router";
|
||||
import tokenStore from "../store/tokenStore.ts";
|
||||
import { ref } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import ModuleTemplateDialog from "./ModuleTemplateDialog.vue";
|
||||
import { onlyWhitespace } from "../helpers/strings.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const store = moduleStore();
|
||||
const tableData = ref(
|
||||
@@ -16,10 +19,10 @@ const tableData = ref(
|
||||
}),
|
||||
);
|
||||
|
||||
const columns = ref([
|
||||
{ field: "Course", header: "Course" },
|
||||
{ field: "Module", header: "Module" },
|
||||
{ field: "Reminder", header: "Reminder" },
|
||||
const columns = computed(() => [
|
||||
{ field: "Course", header: t("moduleInformation.course") },
|
||||
{ field: "Module", header: t("moduleInformation.module") },
|
||||
{ field: "Reminder", header: t("renameModules.reminder") },
|
||||
]);
|
||||
|
||||
async function finalStep() {
|
||||
@@ -32,7 +35,7 @@ async function finalStep() {
|
||||
<template>
|
||||
<div class="flex flex-column">
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<h3>Configure your selected Modules to your liking.</h3>
|
||||
<h3>{{ $t("renameModules.subTitle") }}</h3>
|
||||
<ModuleTemplateDialog />
|
||||
</div>
|
||||
<div class="card flex align-items-center justify-content-center m-2">
|
||||
@@ -44,7 +47,7 @@ async function finalStep() {
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex align-items-center justify-content-end">
|
||||
Enable all notifications:
|
||||
{{ $t("renameModules.enableAllNotifications") }}
|
||||
<InputSwitch
|
||||
class="mx-4"
|
||||
:model-value="
|
||||
@@ -69,7 +72,11 @@ async function finalStep() {
|
||||
<!-- Text Body -->
|
||||
<template #body="{ data, field }">
|
||||
<template v-if="field === 'Module'">
|
||||
{{ data[field].userDefinedName }}
|
||||
{{
|
||||
onlyWhitespace(data[field].userDefinedName)
|
||||
? data[field].name
|
||||
: data[field].userDefinedName
|
||||
}}
|
||||
</template>
|
||||
<template v-else-if="field === 'Reminder'">
|
||||
<Button
|
||||
@@ -95,10 +102,6 @@ async function finalStep() {
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="field === 'Reminder'">
|
||||
<!--<InputSwitch
|
||||
v-model="data.Module.reminder"
|
||||
class="align-self-center"
|
||||
/>-->
|
||||
<Button
|
||||
:icon="data.Module.reminder ? 'pi pi-bell' : 'pi pi-times'"
|
||||
:severity="data.Module.reminder ? 'warning' : 'secondary'"
|
||||
@@ -117,7 +120,7 @@ async function finalStep() {
|
||||
</div>
|
||||
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<Button @click="finalStep()">Next Step</Button>
|
||||
<Button @click="finalStep()">{{ $t("renameModules.nextStep") }}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -1,47 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { Ref, ref } from "vue";
|
||||
import { fetchRoom } from "../api/fetchRoom.ts";
|
||||
import RoomOccupation from "./RoomOccupation.vue";
|
||||
|
||||
const rooms = async () => {
|
||||
return await fetchRoom();
|
||||
};
|
||||
|
||||
const roomsList: Ref<{ name: string }[]> = ref([]);
|
||||
const selectedRoom: Ref<{ name: string }> = ref({ name: "" });
|
||||
|
||||
rooms().then(
|
||||
(data) =>
|
||||
(roomsList.value = data.map((room) => {
|
||||
return { name: room };
|
||||
})),
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-column">
|
||||
<div class="flex align-items-center justify-content-center h-4rem m-2">
|
||||
<h3 class="text-4xl">Raumfinder</h3>
|
||||
</div>
|
||||
<div
|
||||
class="flex align-items-center justify-content-center h-4rem border-round m-2"
|
||||
>
|
||||
<h5 class="text-2xl">Please select a room</h5>
|
||||
</div>
|
||||
<div
|
||||
class="flex align-items-center justify-content-center border-round m-2"
|
||||
>
|
||||
<Dropdown
|
||||
v-model="selectedRoom"
|
||||
:options="roomsList"
|
||||
class="w-full md:w-25rem mx-2"
|
||||
filter
|
||||
option-label="name"
|
||||
placeholder="Select a Room"
|
||||
/>
|
||||
</div>
|
||||
<div class="m-6">
|
||||
<RoomOccupation :room="selectedRoom.name" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@@ -3,9 +3,11 @@ import FullCalendar from "@fullcalendar/vue3";
|
||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||
import interactionPlugin from "@fullcalendar/interaction";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
import { computed, ref, Ref, watch } from "vue";
|
||||
import { computed, ComputedRef, ref, Ref, watch } from "vue";
|
||||
import { CalendarOptions, EventInput } from "@fullcalendar/core";
|
||||
import { fetchEventsByRoomAndDuration } from "../api/fetchRoom.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const props = defineProps({
|
||||
room: {
|
||||
@@ -53,84 +55,92 @@ async function getOccupation() {
|
||||
calendar?.refetchEvents();
|
||||
}
|
||||
|
||||
const calendarOptions: CalendarOptions = {
|
||||
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin],
|
||||
initialView: "week",
|
||||
dayHeaderFormat: { weekday: "short", omitCommas: true },
|
||||
slotDuration: "00:15:00",
|
||||
eventTimeFormat: {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
},
|
||||
views: {
|
||||
week: {
|
||||
type: "timeGrid",
|
||||
slotLabelFormat: {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
omitZeroMinute: false,
|
||||
meridiem: false,
|
||||
hour12: false,
|
||||
},
|
||||
dateAlignment: "week",
|
||||
titleFormat: { month: "short", day: "numeric" },
|
||||
slotMinTime: "06:00:00",
|
||||
slotMaxTime: "22:00:00",
|
||||
import allLocales from "@fullcalendar/core/locales-all";
|
||||
|
||||
duration: { days: 7 },
|
||||
firstDay: 1,
|
||||
allDaySlot: false,
|
||||
hiddenDays: [0],
|
||||
const calendarOptions: ComputedRef<CalendarOptions> = computed(() => {
|
||||
return {
|
||||
locales: allLocales,
|
||||
locale: t("languageCode"),
|
||||
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin],
|
||||
initialView: "week",
|
||||
dayHeaderFormat: { weekday: "short", omitCommas: true },
|
||||
slotDuration: "00:15:00",
|
||||
eventTimeFormat: {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
},
|
||||
Day: {
|
||||
type: "timeGrid",
|
||||
slotLabelFormat: {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
omitZeroMinute: false,
|
||||
meridiem: false,
|
||||
hour12: false,
|
||||
views: {
|
||||
week: {
|
||||
description: "Wochenansicht",
|
||||
type: "timeGrid",
|
||||
slotLabelFormat: {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
omitZeroMinute: false,
|
||||
meridiem: false,
|
||||
hour12: false,
|
||||
},
|
||||
dateAlignment: "week",
|
||||
titleFormat: { month: "short", day: "numeric" },
|
||||
slotMinTime: "06:00:00",
|
||||
slotMaxTime: "22:00:00",
|
||||
duration: { days: 7 },
|
||||
firstDay: 1,
|
||||
allDaySlot: false,
|
||||
hiddenDays: [0],
|
||||
},
|
||||
Day: {
|
||||
type: "timeGrid",
|
||||
slotLabelFormat: {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
omitZeroMinute: false,
|
||||
meridiem: false,
|
||||
hour12: false,
|
||||
},
|
||||
titleFormat: { month: "short", day: "numeric" },
|
||||
slotMinTime: "06:00:00",
|
||||
slotMaxTime: "22:00:00",
|
||||
duration: { days: 1 },
|
||||
allDaySlot: false,
|
||||
hiddenDays: [0],
|
||||
},
|
||||
titleFormat: { month: "short", day: "numeric" },
|
||||
slotMinTime: "06:00:00",
|
||||
slotMaxTime: "22:00:00",
|
||||
duration: { days: 1 },
|
||||
allDaySlot: false,
|
||||
hiddenDays: [0],
|
||||
},
|
||||
},
|
||||
headerToolbar: {
|
||||
end: "prev,next today",
|
||||
center: "title",
|
||||
start: "week,Day",
|
||||
},
|
||||
headerToolbar: {
|
||||
end: "prev,next today",
|
||||
center: "title",
|
||||
start: "week,Day",
|
||||
},
|
||||
|
||||
datesSet: function (dateInfo) {
|
||||
const view = dateInfo.view;
|
||||
const offset = new Date().getTimezoneOffset();
|
||||
const startDate = new Date(view.activeStart.getTime() - offset * 60 * 1000);
|
||||
const endDate = new Date(view.activeEnd.getTime() - offset * 60 * 1000);
|
||||
currentDateFrom.value = startDate.toISOString().split("T")[0];
|
||||
currentDateTo.value = endDate.toISOString().split("T")[0];
|
||||
getOccupation();
|
||||
},
|
||||
events: function (_info, successCallback, failureCallback) {
|
||||
if (occupations.value.length === 0) {
|
||||
failureCallback(new Error("no events"));
|
||||
} else {
|
||||
successCallback(
|
||||
occupations.value.map((event) => {
|
||||
return {
|
||||
id: event.id.toString(),
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
} as EventInput;
|
||||
}),
|
||||
datesSet: function (dateInfo: any) {
|
||||
const view = dateInfo.view;
|
||||
const offset = new Date().getTimezoneOffset();
|
||||
const startDate = new Date(
|
||||
view.activeStart.getTime() - offset * 60 * 1000,
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
const endDate = new Date(view.activeEnd.getTime() - offset * 60 * 1000);
|
||||
currentDateFrom.value = startDate.toISOString().split("T")[0];
|
||||
currentDateTo.value = endDate.toISOString().split("T")[0];
|
||||
getOccupation();
|
||||
},
|
||||
events: function (_info: any, successCallback: any, failureCallback: any) {
|
||||
if (occupations.value.length === 0) {
|
||||
failureCallback(new Error("no events"));
|
||||
} else {
|
||||
successCallback(
|
||||
occupations.value.map((event) => {
|
||||
return {
|
||||
id: event.id.toString(),
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
} as EventInput;
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<FullCalendar ref="fullCalendar" :options="calendarOptions" />
|
||||
|
@@ -91,6 +91,7 @@ function selectChange() {
|
||||
class="custom-multiselect"
|
||||
filter
|
||||
placeholder="Select additional modules"
|
||||
:auto-filter-focus="true"
|
||||
@change="selectChange()"
|
||||
@selectall-change="onSelectAllChange($event)"
|
||||
>
|
||||
|
@@ -7,6 +7,7 @@ import { saveIndividualFeed } from "../../api/createFeed";
|
||||
import tokenStore from "../../store/tokenStore";
|
||||
import router from "../../router";
|
||||
import ModuleTemplateDialog from "../ModuleTemplateDialog.vue";
|
||||
import { onlyWhitespace } from "../../helpers/strings.ts";
|
||||
|
||||
const store = moduleStore();
|
||||
const tableData = computed(() =>
|
||||
@@ -90,7 +91,11 @@ async function finalStep() {
|
||||
<!-- Text Body -->
|
||||
<template #body="{ data, field }">
|
||||
<template v-if="field === 'Module'">
|
||||
{{ data[field].userDefinedName }}
|
||||
{{
|
||||
onlyWhitespace(data[field].userDefinedName)
|
||||
? data[field].name
|
||||
: data[field].userDefinedName
|
||||
}}
|
||||
</template>
|
||||
<template v-else-if="field === 'Reminder'">
|
||||
<Button
|
||||
@@ -125,6 +130,9 @@ async function finalStep() {
|
||||
@click="data.Module.reminder = !data.Module.reminder"
|
||||
></Button>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ data[field] }}
|
||||
</template>
|
||||
</template>
|
||||
</Column>
|
||||
<Column>
|
||||
|
Reference in New Issue
Block a user