diff --git a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js index c278b43ea..abd647af2 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js @@ -2,6 +2,7 @@ import BsModal from '../../Bootstrap/Modal.js'; import VueDatePicker from '../../vueDatepicker.js.php'; import ApiAbgabe from '../../../api/factory/abgabe.js' import { getDateStyleClass } from "./getDateStyleClass.js"; +import { compareISODateValues, formatISODate, getViennaTodayISO } from "./dateUtils.js"; export const AbgabeMitarbeiterDetail = { name: "AbgabeMitarbeiterDetail", @@ -138,7 +139,7 @@ export const AbgabeMitarbeiterDetail = { termin.dateStyle = getDateStyleClass(termin, this.notenOptions) } - this.projektarbeit.abgabetermine.sort((a, b) =>new Date(a.datum) - new Date(b.datum)) + this.projektarbeit.abgabetermine.sort((a, b) => compareISODateValues(a.datum, b.datum)) const index = this.projektarbeit.abgabetermine.findIndex(t => termin.paabgabe_id == t.paabgabe_id) @@ -159,7 +160,7 @@ export const AbgabeMitarbeiterDetail = { 'fixtermin': false, 'invertedFixtermin': true, 'kurzbz': '', // todo kurzbz textfield value vorschlag für qualgates - 'datum': new Date().toISOString().split('T')[0], + 'datum': getViennaTodayISO(), 'note': this.allowedNotenOptions.find(opt => opt.note == 9), 'beurteilungsnotiz': '', 'upload_allowed': false, @@ -337,16 +338,7 @@ export const AbgabeMitarbeiterDetail = { } }, formatDate(dateParam) { - // unsafe for datepickers, dont use there - const date = new Date(dateParam) - // handle missing leading 0 - const padZero = (num) => String(num).padStart(2, '0'); - - const month = padZero(date.getMonth() + 1); // Months are zero-based - const day = padZero(date.getDate()); - const year = date.getFullYear(); - - return `${day}.${month}.${year}` + return formatISODate(dateParam) }, openCreateNewAbgabeModal() { if(this.projektarbeit?.betreuerart_kurzbz == 'Zweitbegutachter') { @@ -364,7 +356,7 @@ export const AbgabeMitarbeiterDetail = { 'fixtermin': false, 'invertedFixtermin': true, 'kurzbz': '', - 'datum': new Date().toISOString().split('T')[0], + 'datum': getViennaTodayISO(), 'note': this.allowedNotenOptions.find(opt => opt.note == 9), 'beurteilungsnotiz': '', 'upload_allowed': typ.upload_allowed_default, @@ -398,7 +390,7 @@ export const AbgabeMitarbeiterDetail = { 'fixtermin': false, 'invertedFixtermin': true, 'kurzbz': '', - 'datum': new Date().toISOString().split('T')[0], + 'datum': getViennaTodayISO(), 'note': this.allowedNotenOptions.find(opt => opt.note == 9), 'beurteilungsnotiz': '', 'upload_allowed': false, @@ -597,7 +589,7 @@ export const AbgabeMitarbeiterDetail = { 'fixtermin': false, 'invertedFixtermin': true, 'kurzbz': '', - 'datum': new Date().toISOString().split('T')[0], + 'datum': getViennaTodayISO(), 'note': this.allowedNotenOptions.find(opt => opt.note == 9), 'beurteilungsnotiz': '', 'upload_allowed': typ.upload_allowed_default, diff --git a/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js b/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js index 18962f5ae..c9d5280cb 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeStudentDetail.js @@ -3,6 +3,7 @@ import BsModal from '../../Bootstrap/Modal.js'; import VueDatePicker from '../../vueDatepicker.js.php'; import ApiAbgabe from '../../../api/factory/abgabe.js' import FhcOverlay from "../../Overlay/FhcOverlay.js"; +import { formatISODate, getViennaTodayISO } from "./dateUtils.js"; export const AbgabeStudentDetail = { name: "AbgabeStudentDetail", @@ -166,14 +167,7 @@ export const AbgabeStudentDetail = { window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url) }, formatDate(dateParam) { - const date = new Date(dateParam) - const padZero = (num) => String(num).padStart(2, '0'); - - const month = padZero(date.getUTCMonth() + 1); - const day = padZero(date.getUTCDate()); - const year = date.getUTCFullYear(); - - return `${day}.${month}.${year}` + return formatISODate(dateParam) }, async upload(termin) { @@ -210,7 +204,7 @@ export const AbgabeStudentDetail = { if(res.meta.status == "success") { this.$fhcAlert.alertSuccess(this.$capitalize(this.$p.t('abgabetool/c4fileUploadSuccessv3'))) - termin.abgabedatum = new Date().toISOString().split('T')[0]; + termin.abgabedatum = getViennaTodayISO(); if(res?.data?.signatur !== undefined) { termin.signatur = res.data.signatur } @@ -684,4 +678,4 @@ export const AbgabeStudentDetail = { `, }; -export default AbgabeStudentDetail; \ No newline at end of file +export default AbgabeStudentDetail; diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index 108aa33b8..fee321299 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -10,6 +10,7 @@ import FhcOverlay from "../../Overlay/FhcOverlay.js"; import { splitMailsHelper } from "../../../helpers/EmailHelpers.js" import { getDateStyleClass} from "./getDateStyleClass.js"; import { dateFilter } from '../../../tabulator/filters/Dates.js'; +import { compareISODateValues, formatISODate, getViennaTodayISO, toViennaDate } from "./dateUtils.js"; export const AbgabetoolAssistenz = { name: "AbgabetoolAssistenz", @@ -118,7 +119,7 @@ export const AbgabetoolAssistenz = { fixtermin: false, }, serienTermin: Vue.reactive({ - datum: new Date().toISOString().split('T')[0], + datum: getViennaTodayISO(), bezeichnung: { paabgabetyp_kurzbz: 'zwischen', bezeichnung: 'Zwischenabgabe' @@ -404,7 +405,7 @@ export const AbgabetoolAssistenz = { field: 'datum', headerFilter: dateFilter, headerFilterFunc: this.headerFilterTerminColISO, - sorter: (a, b) => new Date(a) - new Date(b), + sorter: compareISODateValues, formatter: (cell) => this.formatDate(cell.getValue()), minWidth: 100 }, @@ -413,7 +414,7 @@ export const AbgabetoolAssistenz = { field: 'abgabedatum', headerFilter: dateFilter, headerFilterFunc: this.headerFilterTerminColISO, - sorter: (a, b) => new Date(a) - new Date(b), + sorter: compareISODateValues, formatter: (cell) => this.formatDate(cell.getValue()), minWidth: 100 }, @@ -633,7 +634,7 @@ export const AbgabetoolAssistenz = { upload_allowed: false, fixtermin: false, } - this.serienEdit.datum = new Date().toISOString().split('T')[0] + this.serienEdit.datum = getViennaTodayISO() this.serienEdit.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === 'zwischen') this.serienEdit.kurzbz = '' this.serienEdit.upload_allowed = false @@ -1087,7 +1088,7 @@ export const AbgabetoolAssistenz = { if (val instanceof Date) { dt = luxon.DateTime.fromJSDate(val); } else if (typeof val === "string") { - dt = luxon.DateTime.fromISO(val); + dt = toViennaDate(val); } else { // fallback dt = luxon.DateTime.fromMillis(Number(val)); } @@ -1124,7 +1125,7 @@ export const AbgabetoolAssistenz = { if (val instanceof Date) { dt = luxon.DateTime.fromJSDate(val); } else if (typeof val === "string") { - dt = luxon.DateTime.fromISO(val); + dt = toViennaDate(val); } else { // fallback dt = luxon.DateTime.fromMillis(Number(val)); } @@ -1372,7 +1373,7 @@ export const AbgabetoolAssistenz = { // while already looping through each termin, calculate datestyle beforehand termin.dateStyle = getDateStyleClass(termin, this.notenOptions) - const date = luxon.DateTime.fromISO(termin.datum).endOf('day') + const date = toViennaDate(termin.datum).endOf('day') termin.luxonDate = date termin.diffMs = date.toMillis() - now.toMillis(); // positive = future, negative = past @@ -1640,16 +1641,7 @@ export const AbgabetoolAssistenz = { return option.bezeichnung }, formatDate(dateParam) { - if(dateParam === null) return '' - const date = new Date(dateParam) - // handle missing leading 0 - const padZero = (num) => String(num).padStart(2, '0'); - - const month = padZero(date.getMonth() + 1); // Months are zero-based - const day = padZero(date.getDate()); - const year = date.getFullYear(); - - return `${day}.${month}.${year}`; + return formatISODate(dateParam); }, formAction(cell) { const actionButtons = document.createElement('div'); @@ -1758,7 +1750,7 @@ export const AbgabetoolAssistenz = { abgabe.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz == abgabe.paabgabetyp_kurzbz) pa.abgabetermine.push(abgabe) - pa.abgabetermine.sort((a, b) => new Date(a.datum) - new Date(b.datum)) + pa.abgabetermine.sort((a, b) => compareISODateValues(a.datum, b.datum)) }) this.projektarbeiten = this.mapProjekteToTableData(this.projektarbeiten) @@ -1822,7 +1814,7 @@ export const AbgabetoolAssistenz = { findLatestTerminWithUpload(projekt) { const withAbgabedatumSorted = projekt?.abgabetermine ?.filter(t => t.abgabedatum != null) - ?.sort((a, b) => new Date(b.abgabedatum) - new Date(a.abgabedatum)); + ?.sort((a, b) => compareISODateValues(b.abgabedatum, a.abgabedatum)); if(withAbgabedatumSorted.length) { return withAbgabedatumSorted[0] diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js index 9d8db2d8f..547218fce 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js @@ -7,6 +7,7 @@ import FhcOverlay from "../../Overlay/FhcOverlay.js"; import { getDateStyleClass } from "./getDateStyleClass.js"; import { dateFilter } from '../../../tabulator/filters/Dates.js'; import {splitMailsHelper} from "../../../helpers/EmailHelpers.js"; +import { formatISODate, getViennaTodayISO, toViennaDate } from "./dateUtils.js"; export const AbgabetoolMitarbeiter = { name: "AbgabetoolMitarbeiter", @@ -63,7 +64,7 @@ export const AbgabetoolMitarbeiter = { allowedNotenOptions: null, notenOptionsNonFinal: null, serienTermin: Vue.reactive({ - datum: new Date().toISOString().split('T')[0], + datum: getViennaTodayISO(), bezeichnung: { paabgabetyp_kurzbz: 'zwischen', bezeichnung: 'Zwischenabgabe' @@ -571,7 +572,7 @@ export const AbgabetoolMitarbeiter = { if (val instanceof Date) { dt = luxon.DateTime.fromJSDate(val); } else if (typeof val === "string") { - dt = luxon.DateTime.fromISO(val); + dt = toViennaDate(val); } else { // fallback dt = luxon.DateTime.fromMillis(Number(val)); } @@ -803,7 +804,7 @@ export const AbgabetoolMitarbeiter = { // while already looping through each termin, calculate datestyle beforehand termin.dateStyle = getDateStyleClass(termin, this.notenOptions) - const date = luxon.DateTime.fromISO(termin.datum).endOf('day') + const date = toViennaDate(termin.datum).endOf('day') termin.luxonDate = date termin.diffMs = date.toMillis() - now.toMillis(); // positive = future, negative = past @@ -914,15 +915,7 @@ export const AbgabetoolMitarbeiter = { return option.bezeichnung }, formatDate(dateParam) { - const date = new Date(dateParam) - // handle missing leading 0 - const padZero = (num) => String(num).padStart(2, '0'); - - const month = padZero(date.getMonth() + 1); // Months are zero-based - const day = padZero(date.getDate()); - const year = date.getFullYear(); - - return `${day}.${month}.${year}`; + return formatISODate(dateParam); }, undoSelection(cell) { // checks if cells row is selected and unselects -> imitates columns which dont trigger row selection diff --git a/public/js/components/Cis/Abgabetool/dateUtils.js b/public/js/components/Cis/Abgabetool/dateUtils.js new file mode 100644 index 000000000..c9cb080a9 --- /dev/null +++ b/public/js/components/Cis/Abgabetool/dateUtils.js @@ -0,0 +1,26 @@ +const zone = 'Europe/Vienna'; + +export function getViennaTodayISO() { + return luxon.DateTime.now().setZone(zone).toISODate(); +} + +export function formatISODate(dateParam) { + if (!dateParam) return ''; + + const date = luxon.DateTime.fromISO(String(dateParam), { zone }); + return date.isValid ? date.toFormat('dd.MM.yyyy') : ''; +} + +export function toViennaDate(dateParam) { + if (!dateParam) return null; + + return luxon.DateTime.fromISO(String(dateParam), { zone }); +} + +export function compareISODateValues(a, b) { + if (!a && !b) return 0; + if (!a) return 1; + if (!b) return -1; + + return String(a).localeCompare(String(b)); +} diff --git a/public/js/components/Cis/Abgabetool/getDateStyleClass.js b/public/js/components/Cis/Abgabetool/getDateStyleClass.js index ba5224647..b6d8e3b78 100644 --- a/public/js/components/Cis/Abgabetool/getDateStyleClass.js +++ b/public/js/components/Cis/Abgabetool/getDateStyleClass.js @@ -1,8 +1,8 @@ const zone = 'Europe/Vienna'; -const today = luxon.DateTime.now().setZone(zone); export function getDateStyleClass(termin, notenOptions) { + const today = luxon.DateTime.now().setZone(zone); const datum = luxon.DateTime.fromISO(termin.datum, { zone }).endOf('day'); const abgabedatum = termin.abgabedatum ? luxon.DateTime.fromISO(termin.abgabedatum, { zone }) : null; termin.diffindays = datum.diff(today, 'days').days; @@ -35,4 +35,4 @@ export function getDateStyleClass(termin, notenOptions) { if (datum < today) return 'verpasst'; if (termin.diffindays <= 12) return 'abzugeben'; return 'standard'; -} \ No newline at end of file +}