Improved UX on date selector. Cannot select invalid dates anymore

This commit is contained in:
PMKuipers 2023-09-03 16:54:09 +02:00
parent db8b9c9a02
commit 7431943747
15 changed files with 141 additions and 76 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,3 @@
define("local_treestudyplan/treestudyplan-components",["exports","./util/string-helper"],(function(_exports,_stringHelper){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var _default={studyplanTiming:function(a){var now=(new Date).getTime(),timing="future";return new Date(a.startdate).getTime()<now&&(timing=a.enddate&&now>new Date(a.enddate).getTime()?"past":"present"),timing},install:function(Vue){var strings=(0,_stringHelper.load_strings)({studyplancard:{open:"open",noenddate:"noenddate",idnumber:"studyplan_idnumber"}}),ItemEventBus=new Vue;Vue.component("s-studyplan-card",{props:{value:{type:Object},open:{type:Boolean}},data:function(){return{text:strings.studyplancard}},computed:{timing:function(){var now=(new Date).getTime(),startdate=new Date(this.value.pages[0].startdate).getTime(),enddate=new Date(this.value.pages[0].enddate).getTime(),timing="future";return startdate<now&&(timing=this.value.pages[0].enddate&&now>enddate?"past":"present"),timing},startdate:function(){return(0,_stringHelper.format_date)(this.value.pages[0].startdate)},enddate:function(){return this.value.pages[0].enddate?(0,_stringHelper.format_date)(this.value.pages[0].enddate):this.text.noenddate}},methods:{onOpenClick:function(e){this.$emit("open",e)}},template:"\n <b-card \n :class=\"'s-studyplan-card timing-' + timing\"\n >\n <template #header></template>\n <b-card-title>\n <a v-if='open' href='#' @click.prevent='onOpenClick($event)'>{{value.name}}</a>\n <template v-else>{{value.name}}</template>\n <slot name='title'></slot>\n </b-card-title>\n <div class='s-studyplan-card-idnumber' v-if='value.idnumber'><i>{{ text.idnumber}}:</i> {{ value.idnumber }}</div>\n <div class='s-studyplan-card-description' v-if='value.description'>{{ value.description }}</div>\n <slot></slot>\n <template #footer>\n <span :class=\"'t-timing-'+timing\" v-html=\"startdate + ' - '+ enddate\"></span>\n <span class=\"s-studyplan-card-buttons\">\n <slot name='footer'></slot>\n <b-button style=\"float:right;\" v-if='open' variant='primary' \n @click.prevent='onOpenClick($event)'>{{ text.open }}</b-button>\n </span>\n </template>\n </b-card>\n "}),Vue.component("s-studyline-header-heading",{props:{},data:function(){return{layerHeights:{}}},created:function(){ItemEventBus.$on("headerHeightChange",this.onHeaderHeightChange)},computed:{},methods:{onHeaderHeightChange:function(newheight){this.$refs.main&&(this.$refs.main.style.height="".concat(newheight,"px"))}},template:'\n <div class="s-studyline-header-heading" ref="main"></div>\n '}),Vue.component("s-studyline-header-period",{props:{value:{type:Object}},mounted:function(){var self=this;1==self.value.period&&(self.resizeListener=new ResizeObserver((function(){if(self.$refs.main){var size=self.$refs.main.getBoundingClientRect();ItemEventBus.$emit("headerHeightChange",size.height)}})).observe(self.$refs.main))},unmounted:function(){this.resizeListener&&this.resizeListener.disconnect()},computed:{startdate:function(){return(0,_stringHelper.format_date)(this.value.startdate)},enddate:function(){return(0,_stringHelper.format_date)(this.value.enddate)},current:function(){if(this.value&&this.value.startdate&&this.value.enddate){var now=new Date,pstart=new Date(this.value.startdate),pend=new Date(this.value.enddate);return now>=pstart&&now<pend}return!1}},data:function(){return{}},template:'\n <div :class="\'s-studyline-header-period \' + (current?\'current \':\' \')" ref="main"\n ><p><abbr :id="\'s-period-\'+value.id" :title="value.fullname">{{ value.shortname }}</abbr>\n <b-tooltip\n :target="\'s-period-\'+value.id" triggers="hover"\n >{{ value.fullname }}<br>\n <span class="s-studyline-header-period-datespan">\n <span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>\n </span>\n </b-tooltip>\n <slot></slot\n ><p class="s-studyline-header-period-datespan small">\n <span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>\n </p>\n </div>\n '})}};return _exports.default=_default,_exports.default})); define("local_treestudyplan/treestudyplan-components",["exports","./util/string-helper","./util/date-helper"],(function(_exports,_stringHelper,_dateHelper){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var _default={studyplanTiming:function(a){var now=(new Date).getTime(),timing="future";return new Date(a.startdate).getTime()<now&&(timing=a.enddate&&now>new Date(a.enddate).getTime()?"past":"present"),timing},install:function(Vue){var strings=(0,_stringHelper.load_strings)({studyplancard:{open:"open",noenddate:"noenddate",idnumber:"studyplan_idnumber"}}),ItemEventBus=new Vue;Vue.component("s-studyplan-card",{props:{value:{type:Object},open:{type:Boolean}},data:function(){return{text:strings.studyplancard}},computed:{timing:function(){var now=(new Date).getTime(),startdate=new Date(this.value.pages[0].startdate).getTime(),enddate=new Date(this.value.pages[0].enddate).getTime(),timing="future";return startdate<now&&(timing=this.value.pages[0].enddate&&now>enddate?"past":"present"),timing},startdate:function(){return(0,_dateHelper.format_date)(this.value.pages[0].startdate)},enddate:function(){return this.value.pages[0].enddate?(0,_dateHelper.format_date)(this.value.pages[0].enddate):this.text.noenddate}},methods:{onOpenClick:function(e){this.$emit("open",e)}},template:"\n <b-card \n :class=\"'s-studyplan-card timing-' + timing\"\n >\n <template #header></template>\n <b-card-title>\n <a v-if='open' href='#' @click.prevent='onOpenClick($event)'>{{value.name}}</a>\n <template v-else>{{value.name}}</template>\n <slot name='title'></slot>\n </b-card-title>\n <div class='s-studyplan-card-idnumber' v-if='value.idnumber'><i>{{ text.idnumber}}:</i> {{ value.idnumber }}</div>\n <div class='s-studyplan-card-description' v-if='value.description'>{{ value.description }}</div>\n <slot></slot>\n <template #footer>\n <span :class=\"'t-timing-'+timing\" v-html=\"startdate + ' - '+ enddate\"></span>\n <span class=\"s-studyplan-card-buttons\">\n <slot name='footer'></slot>\n <b-button style=\"float:right;\" v-if='open' variant='primary' \n @click.prevent='onOpenClick($event)'>{{ text.open }}</b-button>\n </span>\n </template>\n </b-card>\n "}),Vue.component("s-studyline-header-heading",{props:{},data:function(){return{layerHeights:{}}},created:function(){ItemEventBus.$on("headerHeightChange",this.onHeaderHeightChange)},computed:{},methods:{onHeaderHeightChange:function(newheight){this.$refs.main&&(this.$refs.main.style.height="".concat(newheight,"px"))}},template:'\n <div class="s-studyline-header-heading" ref="main"></div>\n '}),Vue.component("s-studyline-header-period",{props:{value:{type:Object}},mounted:function(){var self=this;1==self.value.period&&(self.resizeListener=new ResizeObserver((function(){if(self.$refs.main){var size=self.$refs.main.getBoundingClientRect();ItemEventBus.$emit("headerHeightChange",size.height)}})).observe(self.$refs.main))},unmounted:function(){this.resizeListener&&this.resizeListener.disconnect()},computed:{startdate:function(){return(0,_dateHelper.format_date)(this.value.startdate)},enddate:function(){return(0,_dateHelper.format_date)(this.value.enddate)},current:function(){if(this.value&&this.value.startdate&&this.value.enddate){var now=new Date,pstart=new Date(this.value.startdate),pend=new Date(this.value.enddate);return now>=pstart&&now<pend}return!1}},data:function(){return{}},template:'\n <div :class="\'s-studyline-header-period \' + (current?\'current \':\' \')" ref="main"\n ><p><abbr :id="\'s-period-\'+value.id" :title="value.fullname">{{ value.shortname }}</abbr>\n <b-tooltip\n :target="\'s-period-\'+value.id" triggers="hover"\n >{{ value.fullname }}<br>\n <span class="s-studyline-header-period-datespan">\n <span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>\n </span>\n </b-tooltip>\n <slot></slot\n ><p class="s-studyline-header-period-datespan small">\n <span class="date">{{ startdate }}</span> - <span class="date">{{ enddate }}</span>\n </p>\n </div>\n '})}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=treestudyplan-components.min.js.map //# sourceMappingURL=treestudyplan-components.min.js.map

File diff suppressed because one or more lines are too long

3
amd/build/util/date-helper.min.js vendored Normal file
View file

@ -0,0 +1,3 @@
define("local_treestudyplan/util/date-helper",["exports"],(function(_exports){function format_date(d,short){d instanceof Date||(d=new Date(d));var monthformat="short";return short&&(monthformat="numeric"),d.toLocaleDateString(document.documentElement.lang,{year:"numeric",month:monthformat,day:"numeric"})}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.add_days=function(datestr,days){var date=new Date(datestr);return function(date){var d=new Date(date),month=""+(d.getMonth()+1),day=""+d.getDate(),year=d.getFullYear();month.length<2&&(month="0"+month);day.length<2&&(day="0"+day);return[year,month,day].join("-")}(new Date(date.getTime()+864e5*days))},_exports.datespaninfo=function(first,last){first instanceof Date||(first=new Date(first));last instanceof Date||(last=new Date(last));first.setHours(0),first.setMinutes(0),first.setSeconds(0),first.setMilliseconds(0),last.setHours(23),last.setMinutes(59),last.setSeconds(59),last.setMilliseconds(999);var dayspan=Math.round((last-first+1)/864e5),years=Math.floor(dayspan/365),ydaysleft=dayspan%365,weeks=Math.floor(ydaysleft/7);return{first:first,last:last,totaldays:dayspan,years:years,weeks:weeks,days:ydaysleft%7,formatted:{first:format_date(first),last:format_date(last)}}},_exports.format_date=format_date}));
//# sourceMappingURL=date-helper.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"date-helper.min.js","sources":["../../src/util/date-helper.js"],"sourcesContent":["/**\n * Format a date according to localized custom\n * @param {Date|string} d The date to convert\n * @param {boolean} short Short format (default false)\n * @returns {string}\n */\nexport function format_date(d,short){\n if(!(d instanceof Date)){\n d = new Date(d);\n }\n\n let monthformat = \"short\";\n if(short){\n monthformat = \"numeric\";\n }\n return d.toLocaleDateString(document.documentElement.lang,{\n year: 'numeric', month: monthformat, day: 'numeric'\n });\n}\n\n/**\n * Provides standardized information about the period between two dates\n * As\n * @param {Date|string} first First day of the span\n * @param {Date|string} last Last day of the span\n * @returns {Object} Object containing formatted start and end dates and span information\n */\nexport function datespaninfo(first,last){\n if(!(first instanceof Date)){ first = new Date(first);}\n if(!(last instanceof Date)){ last = new Date(last);}\n\n // Make sure the end date is at the very end of the day and the start date at the very beginning\n first.setHours(0);\n first.setMinutes(0);\n first.setSeconds(0);\n first.setMilliseconds(0);\n last.setHours(23);\n last.setMinutes(59);\n last.setSeconds(59);\n last.setMilliseconds(999);\n\n const dayspan = Math.round(((last - first)+1)/(24*60*60*1000)); // Add one millisecond to offset the 999 ms\n const years = Math.floor(dayspan/365); // Yes, we ignore leap years/leap days\n const ydaysleft = dayspan % 365;\n\n const weeks = Math.floor(ydaysleft/7);\n const wdaysleft = ydaysleft % 7;\n\n return {\n first: first,\n last: last,\n totaldays: dayspan,\n years: years,\n weeks: weeks,\n days: wdaysleft,\n formatted: {\n first: format_date(first),\n last: format_date(last),\n }\n };\n\n}\n\n/**\n * Format a Date object to YYYY-MM-DD format\n * @param {Date} date Date object\n * @returns Date string in YYYY-MM-DD format\n */\nfunction dateYYYYMMDD(date) {\n const d = new Date(date);\n let month = '' + (d.getMonth() + 1);\n let day = '' + d.getDate();\n const year = d.getFullYear();\n\n if (month.length < 2) {\n month = '0' + month;\n }\n if (day.length < 2) {\n day = '0' + day;\n }\n return [year, month, day].join('-');\n}\n\n/**\n * String formatting function - replaces {name} in string by value of same key in values parameter\n * @param {string} datestr String containing date format in YYYY-MM-DD\n * @param {int} days Object containing keys to replace {key} strings with. Make negative to subtract\n * @returns Date string in YYYY-MM-DD format\n */\nexport function add_days(datestr,days) {\n const date = new Date(datestr);\n const newdate = new Date(date.getTime() + (days * 86400000) ); // Add n days in ms.\n return dateYYYYMMDD(newdate);\n}"],"names":["format_date","d","short","Date","monthformat","toLocaleDateString","document","documentElement","lang","year","month","day","datestr","days","date","getMonth","getDate","getFullYear","length","join","dateYYYYMMDD","getTime","first","last","setHours","setMinutes","setSeconds","setMilliseconds","dayspan","Math","round","years","floor","ydaysleft","weeks","totaldays","formatted"],"mappings":"uFAMgBA,YAAYC,EAAEC,OACrBD,aAAaE,OACdF,EAAI,IAAIE,KAAKF,QAGbG,YAAc,eACfF,QACCE,YAAc,WAEXH,EAAEI,mBAAmBC,SAASC,gBAAgBC,KAAK,CACtDC,KAAM,UAAWC,MAAON,YAAaO,IAAK,+FAyEzBC,QAAQC,UACvBC,KAAO,IAAIX,KAAKS,yBAtBJE,UACZb,EAAI,IAAIE,KAAKW,MACfJ,MAAQ,IAAMT,EAAEc,WAAa,GAC7BJ,IAAM,GAAKV,EAAEe,UACXP,KAAOR,EAAEgB,cAEXP,MAAMQ,OAAS,IACfR,MAAQ,IAAMA,OAEdC,IAAIO,OAAS,IACbP,IAAM,IAAMA,WAET,CAACF,KAAMC,MAAOC,KAAKQ,KAAK,KAYxBC,CADS,IAAIjB,KAAKW,KAAKO,UAAoB,MAAPR,uCAhElBS,MAAMC,MAC1BD,iBAAiBnB,OAAQmB,MAAQ,IAAInB,KAAKmB,QAC1CC,gBAAgBpB,OAAQoB,KAAO,IAAIpB,KAAKoB,OAG7CD,MAAME,SAAS,GACfF,MAAMG,WAAW,GACjBH,MAAMI,WAAW,GACjBJ,MAAMK,gBAAgB,GACtBJ,KAAKC,SAAS,IACdD,KAAKE,WAAW,IAChBF,KAAKG,WAAW,IAChBH,KAAKI,gBAAgB,SAEfC,QAAUC,KAAKC,OAAQP,KAAOD,MAAO,UACrCS,MAAQF,KAAKG,MAAMJ,QAAQ,KAC3BK,UAAYL,QAAU,IAEtBM,MAAQL,KAAKG,MAAMC,UAAU,SAG5B,CACHX,MAAOA,MACPC,KAAMA,KACNY,UAAWP,QACXG,MAAOA,MACPG,MAAOA,MACPrB,KARcoB,UAAY,EAS1BG,UAAW,CACPd,MAAOtB,YAAYsB,OACnBC,KAAMvB,YAAYuB"}

View file

@ -0,0 +1,3 @@
define("local_treestudyplan/util/string-helper copy",["exports","core/str"],(function(_exports,_str){function format_date(d,short){d instanceof Date||(d=new Date(d));var monthformat="short";return short&&(monthformat="numeric"),d.toLocaleDateString(document.documentElement.lang,{year:"numeric",month:monthformat,day:"numeric"})}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.datespaninfo=function(first,last){first instanceof Date||(first=new Date(first));last instanceof Date||(last=new Date(last));first.setHours(0),first.setMinutes(0),first.setSeconds(0),first.setMilliseconds(0),last.setHours(23),last.setMinutes(59),last.setSeconds(59),last.setMilliseconds(999);var dayspan=Math.round((last-first+1)/864e5),years=Math.floor(dayspan/365),ydaysleft=dayspan%365,weeks=Math.floor(ydaysleft/7);return{first:first,last:last,totaldays:dayspan,years:years,weeks:weeks,days:ydaysleft%7,formatted:{first:format_date(first),last:format_date(last)}}},_exports.format_date=format_date,_exports.load_stringkeys=function(string_keys){var _loop2=function(idx){var stringkeys=[];for(var i in string_keys[idx]){var parts=string_keys[idx][i].textkey.split("$"),identifier=parts[0],component=parts.length>1?parts[1]:"local_treestudyplan";stringkeys.push({key:identifier,component:component})}(0,_str.get_strings)(stringkeys).then((function(strings){for(var _i in strings){var s=strings[_i];string_keys[idx][_i].text=s}}))};for(var idx in string_keys)_loop2(idx);return string_keys},_exports.load_strings=function(strings){var _loop=function(idx){var stringkeys=[];for(var handle in strings[idx]){var parts=strings[idx][handle].split(/[$@]/),identifier=parts[0],component=parts.length>1?parts[1]:"local_treestudyplan";stringkeys.push({key:identifier,component:component})}(0,_str.get_strings)(stringkeys).then((function(str){var i=0;for(var _key in strings[idx])strings[idx][_key]=str[i],i++}))};for(var idx in strings)_loop(idx);return strings},_exports.strformat=function(str,values){return str.replace(/\{(\w+)\}/g,(function(m,m1){return m1&&values.hasOwnProperty(m1)?values[m1]:m}))}}));
//# sourceMappingURL=string-helper copy.min.js.map

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,3 @@
define("local_treestudyplan/util/string-helper",["exports","core/str"],(function(_exports,_str){function format_date(d,short){d instanceof Date||(d=new Date(d));var monthformat="short";return short&&(monthformat="numeric"),d.toLocaleDateString(document.documentElement.lang,{year:"numeric",month:monthformat,day:"numeric"})}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.datespaninfo=function(first,last){first instanceof Date||(first=new Date(first));last instanceof Date||(last=new Date(last));first.setHours(0),first.setMinutes(0),first.setSeconds(0),first.setMilliseconds(0),last.setHours(23),last.setMinutes(59),last.setSeconds(59),last.setMilliseconds(999);var dayspan=Math.round((last-first+1)/864e5),years=Math.floor(dayspan/365),ydaysleft=dayspan%365,weeks=Math.floor(ydaysleft/7);return{first:first,last:last,totaldays:dayspan,years:years,weeks:weeks,days:ydaysleft%7,formatted:{first:format_date(first),last:format_date(last)}}},_exports.format_date=format_date,_exports.load_stringkeys=function(string_keys){var _loop2=function(idx){var stringkeys=[];for(var i in string_keys[idx]){var parts=string_keys[idx][i].textkey.split("$"),identifier=parts[0],component=parts.length>1?parts[1]:"local_treestudyplan";stringkeys.push({key:identifier,component:component})}(0,_str.get_strings)(stringkeys).then((function(strings){for(var _i in strings){var s=strings[_i];string_keys[idx][_i].text=s}}))};for(var idx in string_keys)_loop2(idx);return string_keys},_exports.load_strings=function(strings){var _loop=function(idx){var stringkeys=[];for(var handle in strings[idx]){var parts=strings[idx][handle].split(/[$@]/),identifier=parts[0],component=parts.length>1?parts[1]:"local_treestudyplan";stringkeys.push({key:identifier,component:component})}(0,_str.get_strings)(stringkeys).then((function(str){var i=0;for(var _key in strings[idx])strings[idx][_key]=str[i],i++}))};for(var idx in strings)_loop(idx);return strings},_exports.strformat=function(str,values){return str.replace(/\{(\w+)\}/g,(function(m,m1){return m1&&values.hasOwnProperty(m1)?values[m1]:m}))}})); define("local_treestudyplan/util/string-helper",["exports","core/str"],(function(_exports,_str){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.load_stringkeys=function(string_keys){var _loop2=function(idx){var stringkeys=[];for(var i in string_keys[idx]){var parts=string_keys[idx][i].textkey.split("$"),identifier=parts[0],component=parts.length>1?parts[1]:"local_treestudyplan";stringkeys.push({key:identifier,component:component})}(0,_str.get_strings)(stringkeys).then((function(strings){for(var _i in strings){var s=strings[_i];string_keys[idx][_i].text=s}}))};for(var idx in string_keys)_loop2(idx);return string_keys},_exports.load_strings=function(strings){var _loop=function(idx){var stringkeys=[];for(var handle in strings[idx]){var parts=strings[idx][handle].split(/[$@]/),identifier=parts[0],component=parts.length>1?parts[1]:"local_treestudyplan";stringkeys.push({key:identifier,component:component})}(0,_str.get_strings)(stringkeys).then((function(str){var i=0;for(var _key in strings[idx])strings[idx][_key]=str[i],i++}))};for(var idx in strings)_loop(idx);return strings},_exports.strformat=function(str,values){return str.replace(/\{(\w+)\}/g,(function(m,m1){return m1&&values.hasOwnProperty(m1)?values[m1]:m}))}}));
//# sourceMappingURL=string-helper.min.js.map //# sourceMappingURL=string-helper.min.js.map

File diff suppressed because one or more lines are too long

View file

@ -10,7 +10,8 @@ import {SimpleLine} from "./simpleline/simpleline";
import {call} from 'core/ajax'; import {call} from 'core/ajax';
import notification from 'core/notification'; import notification from 'core/notification';
import {get_strings} from 'core/str'; import {get_strings} from 'core/str';
import {load_stringkeys, load_strings, format_date, datespaninfo, strformat } from './util/string-helper'; import {load_stringkeys, load_strings, strformat } from './util/string-helper';
import {format_date, add_days, datespaninfo } from './util/date-helper';
import {objCopy,transportItem} from './studyplan-processor'; import {objCopy,transportItem} from './studyplan-processor';
import Debugger from './util/debugger'; import Debugger from './util/debugger';
import {download,upload} from './downloader'; import {download,upload} from './downloader';
@ -1206,7 +1207,18 @@ export default {
self.$emit('input',self.value); self.$emit('input',self.value);
}).fail(notification.exception); }).fail(notification.exception);
}, },
add_day(date,days) {
if( days === undefined ){
days = 1;
}
return add_days(date,days);
},
sub_day(date,days) {
if( days === undefined ){
days = 1;
}
return add_days(date,0 - days);
},
} }
, ,
template: template:
@ -1247,7 +1259,7 @@ export default {
<b-form-datepicker <b-form-datepicker
v-model="editdata.startdate" v-model="editdata.startdate"
:min="(minstart ? minstart : '')" :min="(minstart ? minstart : '')"
:max="value.enddate" :max="sub_day(value.enddate)"
></b-form-datepicker> ></b-form-datepicker>
</b-col> </b-col>
</b-row> </b-row>
@ -1256,7 +1268,7 @@ export default {
<b-col cols="8"> <b-col cols="8">
<b-form-datepicker <b-form-datepicker
v-model="editdata.enddate" v-model="editdata.enddate"
:min="value.startdate" :min="add_day(value.startdate)"
:max="(maxend ? maxend : '')" :max="(maxend ? maxend : '')"
></b-form-datepicker> ></b-form-datepicker>
</b-col> </b-col>
@ -1575,6 +1587,18 @@ export default {
next.refresh(); next.refresh();
} }
}, },
add_day(date,days) {
if( days === undefined ){
days = 1;
}
return add_days(date,days);
},
sub_day(date,days) {
if( days === undefined ){
days = 1;
}
return add_days(date,0 - days);
},
} }
, ,
template: template:
@ -1664,8 +1688,8 @@ export default {
:ref="'periodeditor-'+index" :ref="'periodeditor-'+index"
@edited="periodEdited" @edited="periodEdited"
v-model="page.perioddesc[index-1]" v-model="page.perioddesc[index-1]"
:minstart="(index > 1) ? page.perioddesc[index-2].startdate : null" :minstart="(index > 1) ? add_day(page.perioddesc[index-2].startdate,2) : null"
:maxend="(index < page.periods) ? page.perioddesc[index].enddate : null" :maxend="(index < page.periods) ? sub_day(page.perioddesc[index].enddate,2) : null"
></t-period-edit ></t-period-edit
></s-studyline-header-period> ></s-studyline-header-period>
<div class="s-studyline-header-filter"></div> <div class="s-studyline-header-filter"></div>

View file

@ -4,7 +4,8 @@
/*eslint-env es6*/ /*eslint-env es6*/
// Put this file in path/to/plugin/amd/src // Put this file in path/to/plugin/amd/src
import {load_strings, format_date} from './util/string-helper'; import {load_strings} from './util/string-helper';
import {format_date} from './util/date-helper';
export default { export default {
studyplanTiming(a) { studyplanTiming(a) {

View file

@ -0,0 +1,94 @@
/**
* Format a date according to localized custom
* @param {Date|string} d The date to convert
* @param {boolean} short Short format (default false)
* @returns {string}
*/
export function format_date(d,short){
if(!(d instanceof Date)){
d = new Date(d);
}
let monthformat = "short";
if(short){
monthformat = "numeric";
}
return d.toLocaleDateString(document.documentElement.lang,{
year: 'numeric', month: monthformat, day: 'numeric'
});
}
/**
* Provides standardized information about the period between two dates
* As
* @param {Date|string} first First day of the span
* @param {Date|string} last Last day of the span
* @returns {Object} Object containing formatted start and end dates and span information
*/
export function datespaninfo(first,last){
if(!(first instanceof Date)){ first = new Date(first);}
if(!(last instanceof Date)){ last = new Date(last);}
// Make sure the end date is at the very end of the day and the start date at the very beginning
first.setHours(0);
first.setMinutes(0);
first.setSeconds(0);
first.setMilliseconds(0);
last.setHours(23);
last.setMinutes(59);
last.setSeconds(59);
last.setMilliseconds(999);
const dayspan = Math.round(((last - first)+1)/(24*60*60*1000)); // Add one millisecond to offset the 999 ms
const years = Math.floor(dayspan/365); // Yes, we ignore leap years/leap days
const ydaysleft = dayspan % 365;
const weeks = Math.floor(ydaysleft/7);
const wdaysleft = ydaysleft % 7;
return {
first: first,
last: last,
totaldays: dayspan,
years: years,
weeks: weeks,
days: wdaysleft,
formatted: {
first: format_date(first),
last: format_date(last),
}
};
}
/**
* Format a Date object to YYYY-MM-DD format
* @param {Date} date Date object
* @returns Date string in YYYY-MM-DD format
*/
function dateYYYYMMDD(date) {
const d = new Date(date);
let month = '' + (d.getMonth() + 1);
let day = '' + d.getDate();
const year = d.getFullYear();
if (month.length < 2) {
month = '0' + month;
}
if (day.length < 2) {
day = '0' + day;
}
return [year, month, day].join('-');
}
/**
* String formatting function - replaces {name} in string by value of same key in values parameter
* @param {string} datestr String containing date format in YYYY-MM-DD
* @param {int} days Object containing keys to replace {key} strings with. Make negative to subtract
* @returns Date string in YYYY-MM-DD format
*/
export function add_days(datestr,days) {
const date = new Date(datestr);
const newdate = new Date(date.getTime() + (days * 86400000) ); // Add n days in ms.
return dateYYYYMMDD(newdate);
}

View file

@ -54,68 +54,6 @@ export function load_stringkeys(string_keys){
} }
return string_keys; return string_keys;
} }
/**
* Format a date according to localized custom
* @param {Date|string} d The date to convert
* @param {boolean} short Short format (default false)
* @returns {string}
*/
export function format_date(d,short){
if(!(d instanceof Date)){
d = new Date(d);
}
let monthformat = "short";
if(short){
monthformat = "numeric";
}
return d.toLocaleDateString(document.documentElement.lang,{
year: 'numeric', month: monthformat, day: 'numeric'
});
}
/**
* Provides standardized information about the period between two dates
* As
* @param {Date|string} first First day of the span
* @param {Date|string} last Last day of the span
* @returns {Object} Object containing formatted start and end dates and span information
*/
export function datespaninfo(first,last){
if(!(first instanceof Date)){ first = new Date(first);}
if(!(last instanceof Date)){ last = new Date(last);}
// Make sure the end date is at the very end of the day and the start date at the very beginning
first.setHours(0);
first.setMinutes(0);
first.setSeconds(0);
first.setMilliseconds(0);
last.setHours(23);
last.setMinutes(59);
last.setSeconds(59);
last.setMilliseconds(999);
const dayspan = Math.round(((last - first)+1)/(24*60*60*1000)); // Add one millisecond to offset the 999 ms
const years = Math.floor(dayspan/365); // Yes, we ignore leap years/leap days
const ydaysleft = dayspan % 365;
const weeks = Math.floor(ydaysleft/7);
const wdaysleft = ydaysleft % 7;
return {
first: first,
last: last,
totaldays: dayspan,
years: years,
weeks: weeks,
days: wdaysleft,
formatted: {
first: format_date(first),
last: format_date(last),
}
};
}
/** /**
* String formatting function - replaces {name} in string by value of same key in values parameter * String formatting function - replaces {name} in string by value of same key in values parameter

View file

@ -321,7 +321,7 @@ class period {
} }
} }
// Adjust start date of previous period if needed // Adjust start date of next period if needed
if (isset($next) && !empty($fields['enddate'])) { if (isset($next) && !empty($fields['enddate'])) {
$mindate = $this->enddate()->add(new DateInterval("P1D")); // Subtract 1 day, since periods include the end day. $mindate = $this->enddate()->add(new DateInterval("P1D")); // Subtract 1 day, since periods include the end day.
$rqdate = $next->startdate(); $rqdate = $next->startdate();