diff --git a/appinfo/routes.php b/appinfo/routes.php index 6a494159de..533b426e4c 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -31,9 +31,9 @@ ['name' => 'view#index', 'url' => '/new/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'postfix' => 'direct.new.timerange'], ['name' => 'view#index', 'url' => '/edit/{objectId}', 'verb' => 'GET', 'postfix' => 'direct.edit'], ['name' => 'view#index', 'url' => '/edit/{objectId}/{recurrenceId}', 'verb' => 'GET', 'postfix' => 'direct.edit.recurrenceId'], - ['name' => 'view#index', 'url' => '/{view}/{timeRange}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange'], - ['name' => 'view#index', 'url' => '/{view}/{timeRange}/new/{mode}/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange.new'], - ['name' => 'view#index', 'url' => '/{view}/{timeRange}/edit/{mode}/{objectId}/{recurrenceId}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange.edit'], + ['name' => 'view#index', 'url' => '/{view}/{timeRange}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|multiMonthYear|listMonth'], 'postfix' => 'view.timerange'], + ['name' => 'view#index', 'url' => '/{view}/{timeRange}/new/{mode}/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|multiMonthYear|listMonth'], 'postfix' => 'view.timerange.new'], + ['name' => 'view#index', 'url' => '/{view}/{timeRange}/edit/{mode}/{objectId}/{recurrenceId}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|multiMonthYear|listMonth'], 'postfix' => 'view.timerange.edit'], ['name' => 'view#getCalendarDotSvg', 'url' => '/public/getCalendarDotSvg/{color}.svg', 'verb' => 'GET'], // Appointments ['name' => 'appointment#index', 'url' => '/appointments/{userId}', 'verb' => 'GET'], diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 81a78b77ee..fe30d548d2 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -105,7 +105,7 @@ public function setConfig(string $key, * @return JSONResponse */ private function setView(string $view):JSONResponse { - if (!\in_array($view, ['timeGridDay', 'timeGridWeek', 'dayGridMonth', 'listMonth'])) { + if (!\in_array($view, ['timeGridDay', 'timeGridWeek', 'dayGridMonth', 'multiMonthYear', 'listMonth'])) { return new JSONResponse([], Http::STATUS_UNPROCESSABLE_ENTITY); } diff --git a/package-lock.json b/package-lock.json index 1eae1244a3..a30b098cc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@fullcalendar/daygrid": "6.1.8", "@fullcalendar/interaction": "6.1.8", "@fullcalendar/list": "6.1.8", + "@fullcalendar/multimonth": "6.1.8", "@fullcalendar/resource": "6.1.8", "@fullcalendar/resource-timeline": "6.1.8", "@fullcalendar/timegrid": "6.1.8", @@ -2074,6 +2075,17 @@ "@fullcalendar/core": "~6.1.8" } }, + "node_modules/@fullcalendar/multimonth": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.8.tgz", + "integrity": "sha512-3F0NlncQTfeE9x5ICxh/M9DaSdY6XjgM1NazY8k+d6ukd1jthHI7vs6j7tXJI9eGUKs3DNNEyzN/LoP06SIyKw==", + "dependencies": { + "@fullcalendar/daygrid": "~6.1.8" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.8" + } + }, "node_modules/@fullcalendar/premium-common": { "version": "6.1.8", "resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-6.1.8.tgz", @@ -19096,6 +19108,14 @@ "integrity": "sha512-10N0T/vCtId1cE3JGLpnbAivWVnaWCCkVO7wmbsyr5Y+I939kr/zq4BUNwBoP/xSFVVxx59FETh3iyA+MkV8Fw==", "requires": {} }, + "@fullcalendar/multimonth": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.8.tgz", + "integrity": "sha512-3F0NlncQTfeE9x5ICxh/M9DaSdY6XjgM1NazY8k+d6ukd1jthHI7vs6j7tXJI9eGUKs3DNNEyzN/LoP06SIyKw==", + "requires": { + "@fullcalendar/daygrid": "~6.1.8" + } + }, "@fullcalendar/premium-common": { "version": "6.1.8", "resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-6.1.8.tgz", diff --git a/package.json b/package.json index 5bd997cf55..de2d6ff005 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@fullcalendar/daygrid": "6.1.8", "@fullcalendar/interaction": "6.1.8", "@fullcalendar/list": "6.1.8", + "@fullcalendar/multimonth": "6.1.8", "@fullcalendar/resource": "6.1.8", "@fullcalendar/resource-timeline": "6.1.8", "@fullcalendar/timegrid": "6.1.8", diff --git a/src/components/AppNavigation/AppNavigationHeader/AppNavigationHeaderDatePicker.vue b/src/components/AppNavigation/AppNavigationHeader/AppNavigationHeaderDatePicker.vue index 195fbc8979..5cceaf6508 100644 --- a/src/components/AppNavigation/AppNavigationHeader/AppNavigationHeaderDatePicker.vue +++ b/src/components/AppNavigation/AppNavigationHeader/AppNavigationHeaderDatePicker.vue @@ -36,13 +36,14 @@ @click.stop.prevent="toggleDatepicker" @mousedown.stop.prevent="doNothing" @mouseup.stop.prevent="doNothing"> - {{ selectedDate | formatDateRage(view, locale) }} + {{ selectedDate | formatDateRange(view, locale) }} {{ $t('calendar', 'Month') }} + + {{ $t('calendar', 'Year') }} + @@ -64,6 +69,9 @@ export default { isMonthViewSelected() { return this.selectedView === 'dayGridMonth' }, + isYearViewSelected() { + return this.selectedView === 'multiMonthYear' + }, isMonthListViewSelected() { return this.selectedView === 'listMonth' }, diff --git a/src/components/AppNavigation/Settings/ShortcutOverview.vue b/src/components/AppNavigation/Settings/ShortcutOverview.vue index 473411ac18..3124064e9f 100644 --- a/src/components/AppNavigation/Settings/ShortcutOverview.vue +++ b/src/components/AppNavigation/Settings/ShortcutOverview.vue @@ -91,7 +91,10 @@ export default { keys: [['3'], ['m']], label: t('calendar', 'Month view'), }, { - keys: [['4'], ['l']], + keys: [['4'], ['y']], + label: t('calendar', 'Year view'), + }, { + keys: [['5'], ['l']], label: t('calendar', 'List view'), }], }, { diff --git a/src/components/CalendarGrid.vue b/src/components/CalendarGrid.vue index 8d1c519211..fe632d4ba6 100644 --- a/src/components/CalendarGrid.vue +++ b/src/components/CalendarGrid.vue @@ -33,6 +33,7 @@ import dayGridPlugin from '@fullcalendar/daygrid' import interactionPlugin from '@fullcalendar/interaction' import listPlugin from '@fullcalendar/list' import timeGridPlugin from '@fullcalendar/timegrid' +import multiMonthPlugin from '@fullcalendar/multimonth' // Import event sources import eventSource from '../fullcalendar/eventSources/eventSource.js' @@ -164,6 +165,7 @@ export default { interactionPlugin, listPlugin, timeGridPlugin, + multiMonthPlugin, ] }, isEditable() { diff --git a/src/components/Shared/DatePicker.vue b/src/components/Shared/DatePicker.vue index 3f8c2ff4df..873573dbda 100644 --- a/src/components/Shared/DatePicker.vue +++ b/src/components/Shared/DatePicker.vue @@ -26,7 +26,7 @@ :format="'YYYY-MM-DD HH:mm'" :formatter="formatter" :value="date" - :type="type" + :type="actualType" :clearable="false" :minute-step="5" :disabled-date="disabledDate" @@ -142,6 +142,10 @@ export default { type: Boolean, default: false, }, + type: { + type: String, + default: 'datetime', + }, }, data() { return { @@ -187,12 +191,12 @@ export default { * * @return {string} */ - type() { - if (this.isAllDay) { + actualType() { + if (this.type === 'datetime' && this.isAllDay) { return 'date' } - return 'datetime' + return this.type }, /** * The earliest date a user is allowed to pick in the timezone diff --git a/src/filters/dateRangeFormat.js b/src/filters/dateRangeFormat.js index 1b996f8b7a..410d996cda 100644 --- a/src/filters/dateRangeFormat.js +++ b/src/filters/dateRangeFormat.js @@ -41,6 +41,9 @@ export default (value, view, locale) => { year: moment(value).locale(locale).weekYear(), }) + case 'multiMonthYear': + return moment(value).locale(locale).format('YYYY') + case 'dayGridMonth': case 'listMonth': default: diff --git a/src/fullcalendar/localization/dateFormattingConfig.js b/src/fullcalendar/localization/dateFormattingConfig.js index c7765cdfc7..812ba0b4b7 100644 --- a/src/fullcalendar/localization/dateFormattingConfig.js +++ b/src/fullcalendar/localization/dateFormattingConfig.js @@ -40,6 +40,11 @@ const getDateFormattingConfig = () => { ...defaultConfig, dayHeaderFormat: 'ddd', }, + multiMonthYear: { + ...defaultConfig, + dayHeaderFormat: 'ddd', + multiMonthMaxColumns: 4, + }, timeGridDay: defaultConfig, timeGridWeek: defaultConfig, listMonth: { diff --git a/src/utils/date.js b/src/utils/date.js index 4b0c3f9cf3..30ed9b45d0 100644 --- a/src/utils/date.js +++ b/src/utils/date.js @@ -120,13 +120,15 @@ export function getDateFromDateTimeValue(dateTimeValue) { * @param {number} data.day Number of days to add * @param {number} data.week Number of weeks to add * @param {number} data.month Number of months to add + * @param data.year * @return {Date} */ -export function modifyDate(date, { day = 0, week = 0, month = 0 }) { +export function modifyDate(date, { day = 0, week = 0, month = 0, year = 0 }) { date = new Date(date.getTime()) date.setDate(date.getDate() + day) date.setDate(date.getDate() + week * 7) date.setMonth(date.getMonth() + month) + date.setFullYear(date.getFullYear() + year) return date }