From 5095749157da4b742b440af43d32f15496dbf735 Mon Sep 17 00:00:00 2001 From: summersamara Date: Sun, 10 Dec 2023 22:56:37 +0100 Subject: [PATCH 1/9] create Events Calendar View - install full-calendar npm packages - create FullCalendar component - create Events Calendar route - Fix: remove unused imports in NavBar.vue --- package-lock.json | 62 ++++++++++++++++++++++++++++++++ package.json | 6 ++++ src/components/FullCalendar.vue | 51 ++++++++++++++++++++++++++ src/components/NavBar.vue | 30 +++++++++++----- src/router/event.ts | 9 +++++ src/views/Event/CalendarView.vue | 17 +++++++++ 6 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 src/components/FullCalendar.vue create mode 100644 src/views/Event/CalendarView.vue diff --git a/package-lock.json b/package-lock.json index 4940835d0..4c53f1314 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,11 @@ "@apollo/client": "^3.3.16", "@framasoft/socket": "^1.0.0", "@framasoft/socket-apollo-link": "^1.0.0", + "@fullcalendar/core": "^6.1.10", + "@fullcalendar/daygrid": "^6.1.10", + "@fullcalendar/icalendar": "^6.1.10", + "@fullcalendar/interaction": "^6.1.10", + "@fullcalendar/vue3": "^6.1.10", "@oruga-ui/oruga-next": "^0.7.0", "@sentry/tracing": "^7.1", "@sentry/vue": "^7.1", @@ -55,6 +60,7 @@ "graphql": "^16.8.1", "graphql-tag": "^2.10.3", "hammerjs": "^2.0.8", + "ical.js": "^1.5.0", "intersection-observer": "^0.12.0", "jwt-decode": "^4.0.0", "leaflet": "^1.4.0", @@ -2626,6 +2632,48 @@ "zen-observable": "^0.10.0" } }, + "node_modules/@fullcalendar/core": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.10.tgz", + "integrity": "sha512-oTXGJSAGpCf1oY+CKp5qYjMHkJCPBkJ3SHitl63n8Q6xKeiwQ4EF6Au451euUovREwJpLmD1AyZrCnWmtB9AVg==", + "dependencies": { + "preact": "~10.12.1" + } + }, + "node_modules/@fullcalendar/daygrid": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.10.tgz", + "integrity": "sha512-Z4GRm1IyHKgxXFTWGcEI0nTsvYOIkpE0aMt3/o3ER2SZkF+hfwcDFhtj0c9+WhMjXFIWYeoTnA9rUOY7Zl/nxA==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.10" + } + }, + "node_modules/@fullcalendar/icalendar": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@fullcalendar/icalendar/-/icalendar-6.1.10.tgz", + "integrity": "sha512-TXjtZhjYIQZjeqULRjwDd2VWlymdhJmltaN26YS0dcGuCrQhJJ3x/sODVbVaW1mvbMjnjXYUE8AhdpxvhYGIJg==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.10", + "ical.js": "^1.4.0" + } + }, + "node_modules/@fullcalendar/interaction": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.10.tgz", + "integrity": "sha512-aZRlwCpmDasq2RNeWV0ub20Uevare9Cb6iMlxCacx0fhOC14H28G9d1FsduJIecInL84SPGwt5ItqAYMsWv7zw==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.10" + } + }, + "node_modules/@fullcalendar/vue3": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.10.tgz", + "integrity": "sha512-YMYBQx0TlWNuN4G6ra2dkf5cCF5aVi/2zDLGLvLqe2Nk2o7uNbTkrCSG40061OepWQlJv+hYqm1JukLRmyqi4Q==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.10", + "vue": "^3.0.11" + } + }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", @@ -8079,6 +8127,11 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/ical.js": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/ical.js/-/ical.js-1.5.0.tgz", + "integrity": "sha512-7ZxMkogUkkaCx810yp0ZGKvq1ZpRgJeornPttpoxe6nYZ3NLesZe1wWMXDdwTkj/b5NtXT+Y16Aakph/ao98ZQ==" + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -10437,6 +10490,15 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index 8c7ad704b..b0ad36f7a 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,11 @@ "@apollo/client": "^3.3.16", "@framasoft/socket": "^1.0.0", "@framasoft/socket-apollo-link": "^1.0.0", + "@fullcalendar/core": "^6.1.10", + "@fullcalendar/daygrid": "^6.1.10", + "@fullcalendar/icalendar": "^6.1.10", + "@fullcalendar/interaction": "^6.1.10", + "@fullcalendar/vue3": "^6.1.10", "@oruga-ui/oruga-next": "^0.7.0", "@sentry/tracing": "^7.1", "@sentry/vue": "^7.1", @@ -71,6 +76,7 @@ "graphql": "^16.8.1", "graphql-tag": "^2.10.3", "hammerjs": "^2.0.8", + "ical.js": "^1.5.0", "intersection-observer": "^0.12.0", "jwt-decode": "^4.0.0", "leaflet": "^1.4.0", diff --git a/src/components/FullCalendar.vue b/src/components/FullCalendar.vue new file mode 100644 index 000000000..74a85d920 --- /dev/null +++ b/src/components/FullCalendar.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index 0c542a0b8..5071128fe 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -165,9 +165,20 @@ @@ -209,19 +224,18 @@ import MobilizonLogo from "@/components/MobilizonLogo.vue"; import { ICurrentUserRole } from "@/types/enums"; import { logout } from "../utils/auth"; -import { IPerson, displayName } from "../types/actor"; +import { displayName } from "../types/actor"; import RouteName from "../router/name"; -import { computed, onMounted, ref, watch } from "vue"; +import { computed, ref, watch } from "vue"; import { useRoute, useRouter } from "vue-router"; import { useI18n } from "vue-i18n"; import AccountCircle from "vue-material-design-icons/AccountCircle.vue"; -import Inbox from "vue-material-design-icons/Inbox.vue"; import { useCurrentUserClient } from "@/composition/apollo/user"; import { useCurrentActorClient, useCurrentUserIdentities, } from "@/composition/apollo/actor"; -import { useLazyQuery, useMutation } from "@vue/apollo-composable"; +import { useMutation } from "@vue/apollo-composable"; import { UPDATE_DEFAULT_ACTOR } from "@/graphql/actor"; import { changeIdentity } from "@/utils/identity"; import { useRegistrationConfig } from "@/composition/apollo/config"; diff --git a/src/router/event.ts b/src/router/event.ts index 5b1c3c39b..f4273d0f4 100644 --- a/src/router/event.ts +++ b/src/router/event.ts @@ -10,6 +10,7 @@ const myEvents = () => import("@/views/Event/MyEventsView.vue"); export enum EventRouteName { EVENT_LIST = "EventList", + EVENT_CALENDAR = "EventCalendar", CREATE_EVENT = "CreateEvent", MY_EVENTS = "MyEvents", EDIT_EVENT = "EditEvent", @@ -26,6 +27,14 @@ export enum EventRouteName { } export const eventRoutes: RouteRecordRaw[] = [ + { + path: "/events/calendar", + name: EventRouteName.EVENT_CALENDAR, + component: import("../views/Event/CalendarView.vue"), + meta: { + requiredAuth: false, + }, + }, { path: "/events/create", name: EventRouteName.CREATE_EVENT, diff --git a/src/views/Event/CalendarView.vue b/src/views/Event/CalendarView.vue new file mode 100644 index 000000000..4d028ab1c --- /dev/null +++ b/src/views/Event/CalendarView.vue @@ -0,0 +1,17 @@ + + From 81948b45cab1bb4417e09b3565536c2c6ddeeb08 Mon Sep 17 00:00:00 2001 From: 778a69cd <778a69cd@potsda.mn> Date: Mon, 11 Dec 2023 00:30:39 +0100 Subject: [PATCH 2/9] fix: actually route /events/calendar --- lib/web/controllers/page_controller.ex | 2 ++ lib/web/router.ex | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/web/controllers/page_controller.ex b/lib/web/controllers/page_controller.ex index 104097c20..6d5f11f5d 100644 --- a/lib/web/controllers/page_controller.ex +++ b/lib/web/controllers/page_controller.ex @@ -18,6 +18,8 @@ defmodule Mobilizon.Web.PageController do defdelegate my_events(conn, params), to: PageController, as: :index @spec create_event(Plug.Conn.t(), any) :: Plug.Conn.t() defdelegate create_event(conn, params), to: PageController, as: :index + @spec calendar(Plug.Conn.t(), any) :: Plug.Conn.t() + defdelegate calendar(conn, params), to: PageController, as: :index @spec list_events(Plug.Conn.t(), any) :: Plug.Conn.t() defdelegate list_events(conn, params), to: PageController, as: :index @spec edit_event(Plug.Conn.t(), any) :: Plug.Conn.t() diff --git a/lib/web/router.ex b/lib/web/router.ex index 9a1534e88..771aaa7d7 100644 --- a/lib/web/router.ex +++ b/lib/web/router.ex @@ -120,6 +120,7 @@ defmodule Mobilizon.Web.Router do get("/@:name", PageController, :actor) get("/events/me", PageController, :my_events) get("/events/create", PageController, :create_event) + get("/events/calendar", PageController, :calendar) get("/events/:uuid", PageController, :event) get("/comments/:uuid", PageController, :comment) get("/resource/:uuid", PageController, :resource) From 9c0c5b6e8374e49883bf7e3fc287b1e5e33fc461 Mon Sep 17 00:00:00 2001 From: summersamara Date: Sat, 16 Dec 2023 00:11:11 +0100 Subject: [PATCH 3/9] Fix fullcalendar CSP errors - whitelist the 'data:' protocol for fonts - Add CSP hash directive to allow fullcalendar inline style --- lib/web/plugs/http_security_plug.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/web/plugs/http_security_plug.ex b/lib/web/plugs/http_security_plug.ex index 862803b49..eeb2f3702 100644 --- a/lib/web/plugs/http_security_plug.ex +++ b/lib/web/plugs/http_security_plug.ex @@ -77,7 +77,7 @@ defmodule Mobilizon.Web.Plugs.HTTPSecurityPlug do # unsafe-eval is because of JS issues with regenerator-runtime @script_src "script-src 'self' 'unsafe-eval' " @style_src "style-src 'self' " - @font_src "font-src 'self' " + @font_src "font-src 'self' data: " @spec csp_string(Keyword.t()) :: String.t() defp csp_string(options) do @@ -117,6 +117,8 @@ defmodule Mobilizon.Web.Plugs.HTTPSecurityPlug do style_src = [style_src] ++ [get_csp_config(:style_src, options)] + style_src = [style_src] ++ ["'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='"] + font_src = [@font_src] ++ [get_csp_config(:font_src, options)] frame_src = build_csp_field(:frame_src, options) From 69717f26f36dd396bd1235ff1e94595cb2e01477 Mon Sep 17 00:00:00 2001 From: summersamara Date: Mon, 18 Dec 2023 18:29:44 +0100 Subject: [PATCH 4/9] try to fix events calendar routing --- lib/web/router.ex | 1 + src/router/event.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/web/router.ex b/lib/web/router.ex index 771aaa7d7..6233aed7b 100644 --- a/lib/web/router.ex +++ b/lib/web/router.ex @@ -189,6 +189,7 @@ defmodule Mobilizon.Web.Router do get("/events/create", PageController, :create_event) get("/events/list", PageController, :list_events) get("/events/me", PageController, :my_events) + get("/events/calendar", PageController, :calendar) get("/events/:uuid/edit", PageController, :edit_event) # This is a hack to ease link generation into emails diff --git a/src/router/event.ts b/src/router/event.ts index f4273d0f4..5b92a3800 100644 --- a/src/router/event.ts +++ b/src/router/event.ts @@ -7,6 +7,7 @@ const participations = () => import("@/views/Event/ParticipantsView.vue"); const editEvent = () => import("@/views/Event/EditView.vue"); const event = () => import("@/views/Event/EventView.vue"); const myEvents = () => import("@/views/Event/MyEventsView.vue"); +const eventCalendar = () => import("@/views/Event/CalendarView.vue"); export enum EventRouteName { EVENT_LIST = "EventList", @@ -30,7 +31,7 @@ export const eventRoutes: RouteRecordRaw[] = [ { path: "/events/calendar", name: EventRouteName.EVENT_CALENDAR, - component: import("../views/Event/CalendarView.vue"), + component: eventCalendar, meta: { requiredAuth: false, }, From 4ceee2efc74a62c3cff018b04d9ec54a51096606 Mon Sep 17 00:00:00 2001 From: summersamara Date: Tue, 19 Dec 2023 01:23:41 +0100 Subject: [PATCH 5/9] Fix Calendar View style and add features - create ICSCalendar component - create ICSAgenda component (for mobile view) - temporary add rotes.potsda.mn ics feed for testing --- src/components/FullCalendar/ICSAgenda.vue | 137 ++++++++++++++++++ .../ICSCalendar.vue} | 5 + src/views/Event/CalendarView.vue | 16 +- 3 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 src/components/FullCalendar/ICSAgenda.vue rename src/components/{FullCalendar.vue => FullCalendar/ICSCalendar.vue} (85%) diff --git a/src/components/FullCalendar/ICSAgenda.vue b/src/components/FullCalendar/ICSAgenda.vue new file mode 100644 index 000000000..923680811 --- /dev/null +++ b/src/components/FullCalendar/ICSAgenda.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/src/components/FullCalendar.vue b/src/components/FullCalendar/ICSCalendar.vue similarity index 85% rename from src/components/FullCalendar.vue rename to src/components/FullCalendar/ICSCalendar.vue index 74a85d920..be1af1fdd 100644 --- a/src/components/FullCalendar.vue +++ b/src/components/FullCalendar/ICSCalendar.vue @@ -31,6 +31,11 @@ const calendarOptions = computed((): object => { url: props.icsFeedUrl, format: "ics", }, + nextDayThreshold: "09:00:00", + moreLinkClassNames: "bg-mbz-yellow dark:bg-mbz-purple dark:text-white p-2", + moreLinkContent: (arg: { num: number; text: string }) => { + return "+" + arg.num.toString(); + }, eventClassNames: "line-clamp-3 bg-mbz-yellow dark:bg-mbz-purple", headerToolbar: { left: "prev,next,today", diff --git a/src/views/Event/CalendarView.vue b/src/views/Event/CalendarView.vue index 4d028ab1c..ebb80f965 100644 --- a/src/views/Event/CalendarView.vue +++ b/src/views/Event/CalendarView.vue @@ -1,17 +1,27 @@ From 1e0db0d8c97161892af1d0f0147baf456f6e98a9 Mon Sep 17 00:00:00 2001 From: summersamara Date: Tue, 19 Dec 2023 03:03:14 +0100 Subject: [PATCH 6/9] Fix sizing issue + use better type declarations --- src/components/FullCalendar/ICSAgenda.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/FullCalendar/ICSAgenda.vue b/src/components/FullCalendar/ICSAgenda.vue index 923680811..0a1583f39 100644 --- a/src/components/FullCalendar/ICSAgenda.vue +++ b/src/components/FullCalendar/ICSAgenda.vue @@ -44,6 +44,7 @@ import { locale } from "@/utils/i18n"; import { computed, ref } from "vue"; import Clock from "vue-material-design-icons/ClockTimeTenOutline.vue"; import FullCalendar from "@fullcalendar/vue3"; +import { EventSegment } from "@fullcalendar/core"; import dayGridPlugin from "@fullcalendar/daygrid"; import iCalendarPlugin from "@fullcalendar/icalendar"; import interactionPlugin from "@fullcalendar/interaction"; @@ -55,7 +56,7 @@ const props = defineProps<{ const { t } = useI18n({ useScope: "global" }); -const listOfEventsByDate = ref<{ events: object[]; date?: string }>({ +const listOfEventsByDate = ref<{ events: EventSegment[]; date?: string }>({ events: [], date: undefined, }); @@ -70,14 +71,14 @@ const calendarOptions = computed((): object => { }, nextDayThreshold: "09:00:00", dayMaxEventRows: 0, - moreLinkClassNames: "bg-mbz-yellow dark:bg-mbz-purple dark:text-white p-2", + moreLinkClassNames: "bg-mbz-yellow dark:bg-mbz-purple dark:text-white", moreLinkContent: (arg: { num: number; text: string }) => { return "+" + arg.num.toString(); }, moreLinkClick: (info: { date: Date; - allSegs: object[]; - hiddenSegs: []; + allSegs: EventSegment[]; + hiddenSegs: EventSegment[]; jsEvent: object; }) => { listOfEventsByDate.value = { @@ -93,7 +94,7 @@ const calendarOptions = computed((): object => { )[0] as undefined | HTMLElement; moreLinkElement?.click(); }, - contentHeight: 350, + contentHeight: "auto", eventClassNames: "line-clamp-3 bg-mbz-yellow dark:bg-mbz-purple", headerToolbar: { left: "prev,next,today", @@ -124,6 +125,8 @@ const calendarOptions = computed((): object => { .agenda-view .fc-daygrid-day-events { min-height: 1.1rem !important; + margin-bottom: 0.2rem !important; + margin-left: 0.1rem !important; } .clock-icon { From f3051d5f113face855892308d62ad73c28600bd0 Mon Sep 17 00:00:00 2001 From: summersamara Date: Tue, 19 Dec 2023 22:12:42 +0100 Subject: [PATCH 7/9] Remember the calendar state in ICSAgenda component when navigating back --- src/components/FullCalendar/ICSAgenda.vue | 80 +++++++++++++++++------ src/filters/datetime.ts | 11 +++- 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/components/FullCalendar/ICSAgenda.vue b/src/components/FullCalendar/ICSAgenda.vue index 0a1583f39..9867e229e 100644 --- a/src/components/FullCalendar/ICSAgenda.vue +++ b/src/components/FullCalendar/ICSAgenda.vue @@ -1,5 +1,9 @@ @@ -46,7 +31,10 @@ import { useI18n } from "vue-i18n"; import { locale } from "@/utils/i18n"; import { computed, ref } from "vue"; -import Clock from "vue-material-design-icons/ClockTimeTenOutline.vue"; +import { useLazyQuery } from "@vue/apollo-composable"; +import { IEvent } from "@/types/event.model"; +import { Paginate } from "@/types/paginate"; +import { SEARCH_CALENDAR_EVENTS } from "@/graphql/search"; import FullCalendar from "@fullcalendar/vue3"; import { EventSegment } from "@fullcalendar/core"; import dayGridPlugin from "@fullcalendar/daygrid"; @@ -55,38 +43,94 @@ import interactionPlugin from "@fullcalendar/interaction"; import { formatDateISOStringWithoutTime, formatDateString, - formatTimeString, } from "@/filters/datetime"; - -const props = defineProps<{ - icsFeedUrl: string; -}>(); +import EventCard from "../Event/EventCard.vue"; +import EmptyContent from "../Utils/EmptyContent.vue"; const { t } = useI18n({ useScope: "global" }); const calendarRef = ref(); -const lastSelectedDate = ref(undefined); +const lastSelectedDate = ref(new Date().toISOString()); const listOfEventsByDate = ref<{ events: EventSegment[]; date?: string }>({ events: [], date: undefined, }); +const showEventsByDate = (dateStr: string) => { + dateStr = formatDateISOStringWithoutTime(dateStr); + const moreLinkElement = document.querySelectorAll( + `td[data-date='${dateStr}'] a.fc-more-link` + )[0] as undefined | HTMLElement; + + if (moreLinkElement) { + moreLinkElement.click(); + } else { + listOfEventsByDate.value = { + events: [], + date: dateStr, + }; + } + + calendarRef.value.getApi().select(dateStr); +}; + if (window.location.hash.length) { lastSelectedDate.value = formatDateISOStringWithoutTime( window.location.hash.replace("#_", "") ); +} else { + lastSelectedDate.value = formatDateISOStringWithoutTime( + new Date().toISOString() + ); } +const { load: searchEventsLoad, refetch: searchEventsRefetch } = useLazyQuery<{ + searchEvents: Paginate; +}>(SEARCH_CALENDAR_EVENTS); + const calendarOptions = computed((): object => { return { plugins: [dayGridPlugin, iCalendarPlugin, interactionPlugin], initialView: "dayGridMonth", initialDate: lastSelectedDate.value, - events: { - url: props.icsFeedUrl, - format: "ics", + events: async ( + info: { start: Date; end: Date; startStr: string; endStr: string }, + successCallback: (arg: object[]) => unknown, + failureCallback: (err: string) => unknown + ) => { + const queryVars = { + limit: 999, + beginsOn: info.start, + endsOn: info.end, + }; + + const result = + (await searchEventsLoad(undefined, queryVars)) || + (await searchEventsRefetch(queryVars))?.data; + + if (!result) { + failureCallback("failed to fetch calendar events"); + return; + } + + successCallback( + (result.searchEvents.elements ?? []).map((event: IEvent) => { + return { + id: event.id, + title: event.title, + start: event.beginsOn, + end: event.endsOn, + startStr: event.beginsOn, + endStr: event.endsOn, + url: event.url, + extendedProps: { + event: event, + }, + }; + }) + ); }, nextDayThreshold: "09:00:00", dayMaxEventRows: 0, @@ -97,7 +141,7 @@ const calendarOptions = computed((): object => { contentHeight: "auto", eventClassNames: "line-clamp-3 bg-mbz-yellow dark:bg-mbz-purple", headerToolbar: { - left: "prev,next,today", + left: "prev,next,customTodayButton", center: "", right: "title", }, @@ -110,16 +154,19 @@ const calendarOptions = computed((): object => { day: t("Day"), list: t("List"), }, - dateClick: (info: { dateStr: string }) => { - calendarRef.value.getApi().select(info.dateStr); + customButtons: { + customTodayButton: { + text: t("Today"), + click: () => { + calendarRef.value.getApi().today(); + lastSelectedDate.value = formatDateISOStringWithoutTime( + new Date().toISOString() + ); + }, + }, }, - select: (info: { startStr: string }) => { - const startDateStr = formatDateISOStringWithoutTime(info.startStr); - const moreLinkElement = document.querySelectorAll( - `td[data-date='${startDateStr}'] a.fc-more-link` - )[0] as undefined | HTMLElement; - - moreLinkElement?.click(); + dateClick: (info: { dateStr: string }) => { + showEventsByDate(info.dateStr); }, moreLinkClick: (info: { date: Date; @@ -144,7 +191,7 @@ const calendarOptions = computed((): object => { lastSelectedDate.value && arg.el.closest(`td[data-date='${lastSelectedDate.value}']`) ) { - calendarRef.value.getApi().select(lastSelectedDate.value); + showEventsByDate(lastSelectedDate.value); lastSelectedDate.value = undefined; } }, @@ -167,6 +214,10 @@ const calendarOptions = computed((): object => { margin-left: 0.1rem !important; } +.agenda-view .fc-more-link { + pointer-events: none !important; +} + .clock-icon { display: inline-block; vertical-align: middle; diff --git a/src/components/FullCalendar/EventsCalendar.vue b/src/components/FullCalendar/EventsCalendar.vue new file mode 100644 index 000000000..68ac3bfbb --- /dev/null +++ b/src/components/FullCalendar/EventsCalendar.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/src/components/FullCalendar/ICSCalendar.vue b/src/components/FullCalendar/ICSCalendar.vue deleted file mode 100644 index be1af1fdd..000000000 --- a/src/components/FullCalendar/ICSCalendar.vue +++ /dev/null @@ -1,56 +0,0 @@ - - - diff --git a/src/graphql/search.ts b/src/graphql/search.ts index 1782d9c7a..e80ba04d0 100644 --- a/src/graphql/search.ts +++ b/src/graphql/search.ts @@ -201,6 +201,56 @@ export const SEARCH_EVENTS = gql` ${ACTOR_FRAGMENT} `; +export const SEARCH_CALENDAR_EVENTS = gql` + query SearchEvents( + $beginsOn: DateTime + $endsOn: DateTime + $eventPage: Int + $limit: Int + ) { + searchEvents( + beginsOn: $beginsOn + endsOn: $endsOn + page: $eventPage + limit: $limit + ) { + total + elements { + id + title + uuid + beginsOn + endsOn + picture { + id + url + } + status + tags { + ...TagFragment + } + physicalAddress { + ...AdressFragment + } + organizerActor { + ...ActorFragment + } + attributedTo { + ...ActorFragment + } + options { + ...EventOptions + } + __typename + } + } + } + ${EVENT_OPTIONS_FRAGMENT} + ${TAG_FRAGMENT} + ${ADDRESS_FRAGMENT} + ${ACTOR_FRAGMENT} +`; + export const SEARCH_GROUPS = gql` query SearchGroups( $location: String diff --git a/src/views/Event/CalendarView.vue b/src/views/Event/CalendarView.vue index ebb80f965..4b7a2cfdf 100644 --- a/src/views/Event/CalendarView.vue +++ b/src/views/Event/CalendarView.vue @@ -5,21 +5,15 @@
- - + +