diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index 39ed72072..6427df0ac 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -108,6 +108,7 @@ class Students extends FHCAPI_Controller 'studiensemester_kurzbz' => $studiensemester_kurzbz ]); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); $this->studentlistlib->addJoin( "( @@ -153,7 +154,8 @@ class Students extends FHCAPI_Controller $this->addMeta('ci_params', [ 'studiensemester_kurzbz' => $studiensemester_kurzbz ]); - + + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); $this->studentlistlib->addJoin( "( @@ -213,6 +215,7 @@ class Students extends FHCAPI_Controller 'studiensemester_kurzbz' => $studiensemester_kurzbz ]); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); $this->studentlistlib->addJoin( "( diff --git a/application/helpers/hlp_header_helper.php b/application/helpers/hlp_header_helper.php index eb2cd9f85..41f361bf1 100644 --- a/application/helpers/hlp_header_helper.php +++ b/application/helpers/hlp_header_helper.php @@ -82,6 +82,21 @@ function generateCSSsInclude($CSSs) } } +/** + * Generates tags for the style sheets you want to include, the parameter could by a string or an array of strings + */ +function generatePrintCSS($path) +{ + $cssLink = ''; + + $ci =& get_instance(); + $cachetoken = '?'.$ci->config->item('fhcomplete_build_version'); + + if (isset($path)) { + echo sprintf($cssLink, base_url($path).$cachetoken) . PHP_EOL; + } +} + /** * Generates global JS-Object to pass parms to other javascripts */ diff --git a/application/views/templates/CISVUE-Header.php b/application/views/templates/CISVUE-Header.php index d98cbc9cd..1746f3704 100644 --- a/application/views/templates/CISVUE-Header.php +++ b/application/views/templates/CISVUE-Header.php @@ -7,6 +7,7 @@ $includesArray = array( 'fontawesome6' => true, 'axios027' => true, 'primevue3' => true, + 'printCssPath' => 'public/css/Cis4/print.css', 'customJSModules' => array_merge([ 'public/js/apps/Cis/Menu.js' ], $customJSModules ?? []), diff --git a/application/views/templates/FHC-Header.php b/application/views/templates/FHC-Header.php index 4203aa571..f9e2620bf 100644 --- a/application/views/templates/FHC-Header.php +++ b/application/views/templates/FHC-Header.php @@ -12,6 +12,8 @@ $customJSs = isset($customJSs) ? $customJSs : null; $customJSModules = isset($customJSModules) ? $customJSModules : null; $skipID = isset($skipID) ? $skipID : null; + $printCssPath = isset($printCssPath) ? $printCssPath : null; + ?> @@ -138,6 +140,9 @@ generateCSSsIncludeIfExtensionCssExists('tags.css'); } + // Print CSS + if ($printCssPath) generatePrintCSS($printCssPath); + $extapphelper = ExtendableAppsHelper::getInstance(); $extapphelper->init($customCSSs, $customJSs, $customJSModules); diff --git a/public/css/Cis4/CoreCalendarEvents.css b/public/css/Cis4/CoreCalendarEvents.css index 0e684b790..c2420cdf7 100644 --- a/public/css/Cis4/CoreCalendarEvents.css +++ b/public/css/Cis4/CoreCalendarEvents.css @@ -59,12 +59,21 @@ padding: .25em .5em .25em .25em; margin-inline-end: .25rem; } +.rangePageContainer .calendar-event-default .event-time { + display: none !important; +} .weekPageContainer .calendar-event-default .event-text { display: flex; flex-direction: column; justify-content: space-evenly; align-items: center; } +.rangePageContainer .calendar-event-default .event-text { + display: flex; + flex-direction: column; + justify-content: space-evenly; + align-items: start; +} .dayPageContainer .calendar-event-default, .listPageContainer .calendar-event-default { display: flex; diff --git a/public/css/Cis4/print.css b/public/css/Cis4/print.css new file mode 100644 index 000000000..c80391ebc --- /dev/null +++ b/public/css/Cis4/print.css @@ -0,0 +1,51 @@ +@page { + size: landscape; +} + +* { + background-color: white !important; + color: black !important; + font-size: x-small !important; + overflow: visible !important; +} + +.fhc-calendar-base-slider > div { + position: relative !important; +} + +header, +#skiplink, +#cis-lvplan-personal-heading, +.cis-lvplan-personal hr, +.fhc-calendar-base-header, +.fhc-calendar-mode-range .grid-allday, +.fhc-calendar-mode-range #rangePresetSelector, +.fhc-calendar-base-grid-line-background { + display: none !important; +} + +.fhc-calendar-mode-range .main-header-sunday, +.fhc-calendar-mode-range .fhc-calendar-base-grid-line-sunday { + border-bottom-width: 0 !important; +} + +.fhc-calendar-base-grid-line-sunday, +.fhc-calendar-mode-range .main-header-sunday { + break-after: page !important; +} + +.fhc-calendar-mode-range .fhc-calendar-base-grid { + grid-template-rows: auto repeat(var(--fhc-grid-displayed-axis-main-count), auto) !important; + gap: 0px !important; +} + +.fhc-calendar-mode-range .fhc-calendar-base-grid * { + gap: 0px !important; +} + +.fhc-calendar-mode-range .main-header:not(.main-header-empty), +.fhc-calendar-mode-range .fhc-calendar-base-grid-line { + border-top: solid !important; + border-top-color: black !important; + border-top-width: 1px !important; +} diff --git a/public/css/components/calendar/frame.css b/public/css/components/calendar/frame.css index 68b907179..617e029f5 100644 --- a/public/css/components/calendar/frame.css +++ b/public/css/components/calendar/frame.css @@ -111,11 +111,17 @@ .fhc-calendar-base-grid-line { grid-gap: var(--fhc-calendar-gap-events, var(--fhc-calendar-gap, 1px)); } +.fhc-calendar-mode-range .main-header-sunday, +.fhc-calendar-mode-range .all-day-events-sunday, +.fhc-calendar-mode-range .fhc-calendar-base-grid-line-sunday { + border: solid; + border-width: 0 0 8px 0; + border-color: var(--fhc-calendar-gap); +} .fhc-calendar-base-label-time { display: flex; flex-direction: column; justify-content: space-between; - align-items: end; } /* Header */ .fhc-calendar-base-header .header-picker { diff --git a/public/js/apps/Cis/Cis.js b/public/js/apps/Cis/Cis.js index d4f846f25..183298c83 100644 --- a/public/js/apps/Cis/Cis.js +++ b/public/js/apps/Cis/Cis.js @@ -190,7 +190,7 @@ const router = VueRouter.createRouter({ } }, { - path: `/Cis/MyLvPlan/:mode?/:focus_date?`, + path: `/Cis/MyLvPlan/:mode?/:focus_date?/:range_length?`, name: 'MyLvPlan', component: MyLvPlan, props(route) { diff --git a/public/js/components/Calendar/Base.js b/public/js/components/Calendar/Base.js index 360c9afa2..7e634ada1 100644 --- a/public/js/components/Calendar/Base.js +++ b/public/js/components/Calendar/Base.js @@ -114,7 +114,8 @@ export default { type: Boolean, default: undefined }, - btnTableList: { + btnTableList: {}, + btnRange: { type: Boolean, default: undefined }, @@ -193,8 +194,16 @@ export default { return date.setLocale(this.locale); }, set(value) { - this.internalDate = value; - this.$emit('update:date', value, this.cMode); + let date; + let rangeLength; + if (value instanceof luxon.DateTime) { + date = value; + } else { + date = value.date; + rangeLength = value.rangeLength; + } + this.internalDate = date; + this.$emit('update:date', date, this.cMode, rangeLength); } }, sMode() { @@ -315,6 +324,7 @@ export default { :btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))" :btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))" :btn-table-list="!!modes['tableList'] && (btnTableList || (showBtns && btnTableList !== false))" + :btn-range="!!modes['range'] && (btnRange || (showBtns && btnList !== false))" :mode-options="modeOptions ? modeOptions[cMode] : undefined" > @@ -328,6 +338,7 @@ export default { @request-modal-open="showEventModal" @request-modal-close="hideEventModal" @drop="$emit('drop', $event)" + @update:date="cDate = $event" v-bind="modeOptions ? modeOptions[cMode] : null || {}" > diff --git a/public/js/components/Calendar/Base/Grid.js b/public/js/components/Calendar/Base/Grid.js index df322c151..160e0a61c 100644 --- a/public/js/components/Calendar/Base/Grid.js +++ b/public/js/components/Calendar/Base/Grid.js @@ -65,7 +65,11 @@ export default { dayVisibility: { type: Array, default: null - } + }, + arePartHeadersRepeated: { + type: Boolean, + default: false, + }, }, data() { return { @@ -122,6 +126,19 @@ export default { return this.axisParts.slice(0, -1); return this.axisParts; }, + displayedAxisMain() { + if (!this.$props.arePartHeadersRepeated) + return this.$props.axisMain; + + let displayedAxisMain = []; + this.$props.axisMain.forEach((part, index) => { + if (part.weekday === 1 && index > 0) { + displayedAxisMain.push(null); + } + displayedAxisMain.push(part); + }); + return displayedAxisMain; + }, start() { return this.axisPartsWithBreaks[0].start; }, @@ -140,15 +157,17 @@ export default { return ends; }, axisMainBorders() { - return this.axisMain.reduce( - (res, curr) => res.concat([curr.plus(this.start), curr.plus(this.end)]), + return this.displayedAxisMain.reduce( + (res, curr) => res.concat(curr ? [curr.plus(this.start), curr.plus(this.end)] : [null, null]), [] ); }, eventsAllDay() { if (!this.allDayEvents) return []; - return this.mapIntoMainAxis(this.originalEvents.filter(event => event.orig.allDayEvent)); + + let eventsAllDay = this.mapIntoMainAxis(this.originalEvents.filter(event => event.orig.allDayEvent)); + return eventsAllDay; }, eventsNormal() { if (!this.allDayEvents) @@ -164,18 +183,8 @@ export default { hasValidEvents() { return this.events.find(e => e.length); }, - styleGridCols() - { - if (this.dayVisibility) - { - let anyVisible = this.dayVisibility.some(day => day); - if (anyVisible) - { - return this.axisMain.map((day, i) => this.isDayCollapsed(i) ? 'var(--fhc-calendar-axis-collapsible-manual, 0.1fr)' : '1fr').join(' '); - } - } - - let cols = 'repeat(' + this.axisMain.length + ', 1fr)'; + styleGridCols() { + let cols = 'repeat(' + this.displayedAxisMain.length + ', 1fr)'; if (this.axisMainCollapsible) { if (this.hasValidEvents) cols = this.events @@ -203,7 +212,7 @@ export default { }); this.eventsNormal.forEach((events, mainIndex) => { - let day = this.axisMain[mainIndex]; + let day = this.displayedAxisMain[mainIndex]; events.forEach(event => { if (!event.startsHere && !event.endsHere) return; @@ -233,15 +242,17 @@ export default { }, methods: { mapIntoMainAxis(target) { - const result = Array.from({length: this.axisMain.length}, () => Array()); + const result = Array.from({length: this.displayedAxisMain.length}, () => Array()); target.forEach(event => { const start = event.start || this.axisMainBorders[0].plus(-1); const end = event.end || this.axisMainBorders[this.axisMainBorders.length - 1].plus(1); - for (var i = 0; i < this.axisMain.length; i++) { - let laneStart = this.axisMainBorders[i * 2]; - let laneEnd = this.axisMainBorders[i * 2 + 1]; + this.displayedAxisMain.forEach((part, index) => { + if (!part) return; + + let laneStart = this.axisMainBorders[index * 2]; + let laneEnd = this.axisMainBorders[index * 2 + 1]; if (event.orig?.allDayEvent) { laneStart = laneStart.startOf('day'); laneEnd = laneEnd.endOf('day'); @@ -249,13 +260,13 @@ export default { if (start < laneEnd && end > laneStart) { const startsHere = start >= laneStart; const endsHere = end <= laneEnd; - result[i].push({ + result[index].push({ ...event, startsHere, endsHere }); } - } + }); }); return result; @@ -516,23 +527,32 @@ export default {
-
- -
+
-
- +
- - -
+ + + +
+
+
@@ -571,9 +599,9 @@ export default { >
@@ -589,50 +617,56 @@ export default { :style="'grid-' + axisCol + ':2/-1;grid-' + axisRow + ':1/-1'" >
diff --git a/public/js/components/Calendar/Base/Grid/Line.js b/public/js/components/Calendar/Base/Grid/Line.js index 4b0f6525e..be23c856e 100644 --- a/public/js/components/Calendar/Base/Grid/Line.js +++ b/public/js/components/Calendar/Base/Grid/Line.js @@ -133,6 +133,9 @@ export default { template: /* html */ `
diff --git a/public/js/components/Calendar/Base/Header.js b/public/js/components/Calendar/Base/Header.js index 7c3fcafcb..3d77e9eff 100644 --- a/public/js/components/Calendar/Base/Header.js +++ b/public/js/components/Calendar/Base/Header.js @@ -25,7 +25,8 @@ export default { btnWeek: Boolean, btnDay: Boolean, btnList: Boolean, - btnTableList: Boolean + btnTableList: Boolean, + btnRange: Boolean, }, emits: [ "next", @@ -37,7 +38,8 @@ export default { ], data() { return { - open: false + open: false, + semesterOptions: [], }; }, methods: { @@ -55,6 +57,15 @@ export default {
+ @@ -124,10 +135,10 @@ export default { :list-length="modeOptions.length" /> diff --git a/public/js/components/Calendar/Base/Header/Datepicker.js b/public/js/components/Calendar/Base/Header/Datepicker.js index 3e2ec8327..7c6132886 100644 --- a/public/js/components/Calendar/Base/Header/Datepicker.js +++ b/public/js/components/Calendar/Base/Header/Datepicker.js @@ -5,10 +5,11 @@ export default { components: { VueDatePicker }, - inject: [ - "locale", - "timezone" - ], + inject: { + locale: "locale", + timezone: "timezone", + rangeLength: {default: 30} + }, props: { date: { type: luxon.DateTime, @@ -21,7 +22,7 @@ export default { listLength: { type: Number, default: 7 - } + }, }, emits: [ "update:date", @@ -44,6 +45,8 @@ export default { return {month: this.convertedDate.month-1, year: this.convertedDate.year}; case "list": return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1]; + case "range": + return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.rangeLength }).ts - 1]; case "week": return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts]; case "tableList": @@ -67,6 +70,8 @@ export default { return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + end.toLocaleString(luxon.DateTime.DATE_FULL); case "list": return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL); + case "range": + return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.rangeLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL); case "day": return this.date.toLocaleString(luxon.DateTime.DATE_FULL); default: @@ -75,11 +80,21 @@ export default { }, weekStart() { return luxon.Info.getStartOfWeek(this.date)%7; - } + }, + rangeConfig() { + if (this.$props.mode === "list") { + return { autoRange: listLength - 1 }; + } else if (this.$props.mode === "range") { + return true; + } else { + return false; + } + }, }, methods: { update(value) { let date; + let rangeLength; switch (this.mode) { case "month": value.month++; @@ -98,13 +113,18 @@ export default { this.$emit('update:date', start); this.$emit('update:date-range', { start, end }); return; + case "range": + date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); + let endDate = luxon.DateTime.fromJSDate(value[1]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); + rangeLength = Math.floor(endDate.diff(date, "days").toObject().days) + 1; + break; case "day": date = luxon.DateTime.fromJSDate(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); break; default: return; // Don't update if the value is invalid! } - this.$emit("update:date", date); + this.$emit("update:date", { date, rangeLength }); }, weekNumbers(date) { return luxon.DateTime.fromJSDate(date, { locale: this.locale }).localWeekNumber; @@ -117,7 +137,7 @@ export default { :format="() => title" :month-picker="mode == 'month'" :week-picker="mode == 'week'" - :range="mode == 'list' ? { autoRange: listLength - 1 } : (mode == 'tableList' ? { partialRange: false } : false)" + :range="rangeConfig" :text-input="mode == 'day'" :week-start="weekStart" :week-numbers="{ type: weekNumbers }" diff --git a/public/js/components/Calendar/Base/Label/Time.js b/public/js/components/Calendar/Base/Label/Time.js index a7fc21ae7..1e8e04577 100644 --- a/public/js/components/Calendar/Base/Label/Time.js +++ b/public/js/components/Calendar/Base/Label/Time.js @@ -26,7 +26,11 @@ export default { } return true; } - } + }, + alignItemsClassSuffix: { + type: String, + default: "end", + }, }, computed: { sanitizedTimestamps() { @@ -49,7 +53,7 @@ export default { } }, template: ` -
+
{{ start }} - {{ end }} diff --git a/public/js/components/Calendar/LvPlan.js b/public/js/components/Calendar/LvPlan.js index 198415714..6600c23d4 100644 --- a/public/js/components/Calendar/LvPlan.js +++ b/public/js/components/Calendar/LvPlan.js @@ -9,13 +9,21 @@ import ModeDay from './Mode/Day.js'; import ModeWeek from './Mode/Week.js'; import ModeMonth from './Mode/Month.js'; import ModeList from './Mode/List.js'; +import ModeRange from './Mode/Range.js'; export default { name: "CalendarLvPlan", components: { FhcCalendar }, - inject: ["isMobile"], + inject: { + isMobile: { + default: false, + }, + rangeLength: { + default: 30, + } + }, props: { date: { type: [Date, String, Number, luxon.DateTime], @@ -28,7 +36,11 @@ export default { getPromiseFunc: { type: Function, required: true - } + }, + shouldIncludeRangeMode: { + type: Boolean, + default: false, + }, }, provide() { return { @@ -68,31 +80,37 @@ export default { backgrounds() { let now = luxon.DateTime.now().setZone(this.timezone); - if (this.mode == 'Month') + if (this.mode == 'Month') { return [ { class: 'background-past', end: now.startOf('day') } ]; - - return [ - { - class: 'background-past', - end: now, - label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false }) - } - ]; + } else if (this.mode == 'Range') { + return []; + } else { + return [ + { + class: 'background-past', + end: now, + label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false }) + } + ]; + } }, modes() { let modes = { day: Vue.markRaw(ModeDay), month: Vue.markRaw(ModeMonth), }; - if (this.isMobile) { - modes.list = Vue.markRaw(ModeList); - } else { + if (!this.isMobile) { modes.week = Vue.markRaw(ModeWeek); + if (this.$props.shouldIncludeRangeMode) { + modes.range = Vue.markRaw(ModeRange); + } + } else { + modes.list = Vue.markRaw(ModeList); } return modes; @@ -163,7 +181,7 @@ export default { :backgrounds="backgrounds" :time-grid="teachingunits" show-btns - @update:date="(newDate, newMode) => $emit('update:date', newDate, newMode)" + @update:date="(newDate, newMode, newRangeLength) => $emit('update:date', newDate, newMode, newRangeLength)" @update:mode="(newMode, newDate) => $emit('update:mode', newMode, newDate)" @update:range="updateRange" > diff --git a/public/js/components/Calendar/Mode/Range.js b/public/js/components/Calendar/Mode/Range.js new file mode 100644 index 000000000..1ac88d4cf --- /dev/null +++ b/public/js/components/Calendar/Mode/Range.js @@ -0,0 +1,122 @@ +import BaseSlider from "../Base/Slider.js"; +import RangeView from "./Range/View.js"; +import FormInput from "../../Form/Input.js"; + +export default { + name: "ModeRange", + components: { + BaseSlider, + RangeView, + FormInput, + }, + inject: { + rangeLength: { + default: 30, + }, + rangeViewPresets: { + default: {}, + }, + }, + props: { + currentDate: { + type: luxon.DateTime, + required: true, + }, + }, + emits: ["update:currentDate", "update:range", "click", "requestModalOpen"], + data() { + return { + selectedRangePreset: null, + }; + }, + computed: { + range() { + let first = this.$props.currentDate; + let last = first.plus({ days: this.rangeLength }); + + return luxon.Interval.fromDateTimes(first, last); + }, + }, + watch: { + currentDate() { + this.$emit("update:range", this.range); + }, + rangeLength() { + this.$emit("update:range", this.range); + }, + selectedRangePreset() { + if (!this.selectedRangePreset) return; + + const preset = this.rangeViewPresets.presets.find( + (preset) => preset.name === this.selectedRangePreset, + ); + if (!preset) return; + + this.$emit("update:date", { + date: preset.startDate, + rangeLength: + preset.endDate.diff(preset.startDate, "days").days + 1, + }); + + this.selectedRangePreset = null; + }, + }, + methods: { + viewAttrs() { + const day = this.$props.currentDate.startOf("day"); + const rangeLength = this.rangeLength; + return { ...this.$attrs, day, rangeLength }; + }, + handleClickDefaults(evt) { + switch (evt.detail.source) { + case "day": + // default: Set current-date + this.$emit("update:currentDate", { + date: evt.detail.value, + }); + break; + case "event": + // default: Request Modal + this.$emit("requestModalOpen", { event: evt.detail.value }); + break; + } + }, + }, + mounted() { + this.$emit("update:range", this.range); + }, + template: /*html*/ ` +
+
+ {{ rangeViewPresets.label }} + + + +
+
+ + + + + +
+
+ `, +}; diff --git a/public/js/components/Calendar/Mode/Range/View.js b/public/js/components/Calendar/Mode/Range/View.js new file mode 100644 index 000000000..66f52a148 --- /dev/null +++ b/public/js/components/Calendar/Mode/Range/View.js @@ -0,0 +1,88 @@ +import CalendarGrid from '../../Base/Grid.js'; +import LabelDay from '../../Base/Label/Day.js'; +import LabelDow from '../../Base/Label/Dow.js'; +import LabelTime from '../../Base/Label/Time.js'; + +export default { + name: "RangeView", + components: { + CalendarGrid, + LabelDay, + LabelDow, + LabelTime + }, + inject: { + timeGrid: "timeGrid", + timezone: "timezone" + }, + props: { + day: { + type: luxon.DateTime, + required: true + }, + collapseEmptyDays: Boolean, + rangeLength: Number, + }, + computed: { + axisMain() { + return Array.from({ length: this.$props.rangeLength }, (e, i) => this.$props.day.plus({ days: i })); + }, + axisParts() { + if (this.timeGrid) { + // create {start, end} array + return this.timeGrid.map(tu => { + return { + start: luxon.Duration.fromISOTime(tu.start), + end: luxon.Duration.fromISOTime(tu.end) + }; + }); + } else { + // create 07:00-23:00 + return Array.from({ length: 17 }, (e, i) => luxon.Duration.fromObject({ hours: i + 7 })); + } + } + }, + methods: { + isToday(date) { + return date.hasSame(luxon.DateTime.now().setZone(this.timezone), 'day'); + } + }, + template: /* html */` +
+ + + + + + +
+ ` +} diff --git a/public/js/components/Cis/LvPlan/MyLvPlan.js b/public/js/components/Cis/LvPlan/MyLvPlan.js index cb43a06d9..5bcbfbeb7 100644 --- a/public/js/components/Cis/LvPlan/MyLvPlan.js +++ b/public/js/components/Cis/LvPlan/MyLvPlan.js @@ -2,6 +2,7 @@ import FhcCalendar from "../../Calendar/LvPlan.js"; import ApiLvPlan from '../../../api/factory/lvPlan.js'; import ApiAuthinfo from '../../../api/factory/authinfo.js'; +import ApiStudiensemester from '../../../api/factory/studiensemester.js'; export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week"; export const DEFAULT_MODE_LVPLAN_MOBILE = "List"; @@ -23,9 +24,23 @@ export default { isMitarbeiter: false, isStudent: false, timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone, + semesterRangePresets: { + label: null, + presets: [], + }, }; }, inject: ["isMobile"], + provide() { + return { + rangeLength: Vue.computed(() => { + if (!this.$route.params.range_length) return 30; + else if (this.$route.params.range_length > 365) return 365; + else return this.$route.params.range_length; + }), + rangeViewPresets: Vue.computed(() => this.semesterRangePresets) + }; + }, computed:{ currentDay() { if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date))) @@ -34,7 +49,11 @@ export default { }, currentMode() { let validModes = ["day", "month"]; - validModes.push(this.isMobile ? "list" : "week"); + if (!this.isMobile) { + validModes.push("week", "range"); + } else { + validModes.push("list"); + } const defaultMode = this.isMobile ? DEFAULT_MODE_LVPLAN_MOBILE @@ -94,10 +113,10 @@ export default { }, }, methods: { - handleChangeDate(day, newMode) { - return this.handleChangeMode(newMode, day); + handleChangeDate(day, newMode, rangeLength) { + return this.handleChangeMode(newMode, day, rangeLength); }, - handleChangeMode(newMode, day) { + handleChangeMode(newMode, day, range_length = null) { const mode = newMode[0].toUpperCase() + newMode.slice(1) const focus_date = day.toISODate(); @@ -105,7 +124,8 @@ export default { name: "MyLvPlan", params: { mode, - focus_date + focus_date, + range_length, } }); }, @@ -134,13 +154,32 @@ export default { this.isMitarbeiter = authInfo.isMitarbeiter; this.isStudent = authInfo.isStudent; }, + async fetchSemesters() { + const semestersResponse = await this.$api.call(ApiStudiensemester.getAll()); + if (semestersResponse.meta.status === "success") { + this.semesterRangePresets = { + label: "View specific semester", + presets: semestersResponse.data.map((semester) => { + let startDate = luxon.DateTime.fromISO(semester.start); + let endDate = luxon.DateTime.fromISO(semester.ende); + return { + startDate, + endDate, + name: semester.studiensemester_kurzbz, + description: semester.bezeichnung, + }; + }) + }; + } + }, }, async created() { await this.fetchAuthInfo(); + await this.fetchSemesters(); }, template: /*html*/`
-

+

{{ $p.t('lehre/stundenplan') }} {{ studiensemester_kurzbz }} @@ -154,6 +193,7 @@ export default { :get-promise-func="getPromiseFunc" :date="currentDay" :mode="currentMode" + :shouldIncludeRangeMode="true" @update:date="handleChangeDate" @update:mode="handleChangeMode" @update:range="updateRange"