diff --git a/js/src/components/Event/GroupedMultiEventMinimalistCard.vue b/js/src/components/Event/GroupedMultiEventMinimalistCard.vue index 01a90c6d2..bdb173b5f 100644 --- a/js/src/components/Event/GroupedMultiEventMinimalistCard.vue +++ b/js/src/components/Event/GroupedMultiEventMinimalistCard.vue @@ -1,7 +1,7 @@ <template> - <div class="events-wrapper"> + <div class="flex flex-col gap-6"> <div class="flex flex-col gap-4" v-for="key of keys" :key="key"> - <h2 class="month-name"> + <h2 class="capitalize inline-block relative"> {{ monthName(groupEvents(key)[0]) }} </h2> <event-minimalist-card @@ -29,7 +29,7 @@ const props = withDefaults( const monthlyGroupedEvents = computed((): Map<string, IEvent[]> => { return props.events.reduce((acc: Map<string, IEvent[]>, event: IEvent) => { const beginsOn = new Date(event.beginsOn); - const month = `${beginsOn.getUTCMonth()}-${beginsOn.getUTCFullYear()}`; + const month = `${beginsOn.getUTCFullYear()}-${beginsOn.getUTCMonth()}`; const monthEvents = acc.get(month) || []; acc.set(month, [...monthEvents, event]); return acc; @@ -37,9 +37,19 @@ const monthlyGroupedEvents = computed((): Map<string, IEvent[]> => { }); const keys = computed((): string[] => { - return Array.from(monthlyGroupedEvents.value.keys()).sort((a, b) => - b.localeCompare(a) - ); + return Array.from(monthlyGroupedEvents.value.keys()).sort((a, b) => { + const aParams = a.split("-").map((x) => parseInt(x, 10)) as [ + number, + number + ]; + const aDate = new Date(...aParams); + const bParams = b.split("-").map((x) => parseInt(x, 10)) as [ + number, + number + ]; + const bDate = new Date(...bParams); + return bDate.getTime() - aDate.getTime(); + }); }); const groupEvents = (key: string): IEvent[] => { @@ -60,24 +70,4 @@ const monthName = (event: IEvent): string => { grid-gap: 20px; grid-template: 1fr; } -.month-group { - .month-name { - text-transform: capitalize; - text-transform: capitalize; - display: inline-block; - position: relative; - font-size: 1.3rem; - - &::after { - position: absolute; - left: 0; - right: 0; - top: 100%; - content: ""; - width: calc(100% + 30px); - height: 3px; - max-width: 150px; - } - } -} </style> diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts index 6cffea1f3..a7c41edcd 100644 --- a/js/src/graphql/event.ts +++ b/js/src/graphql/event.ts @@ -428,7 +428,6 @@ export const FETCH_GROUP_EVENTS = gql` $organisedEventsLimit: Int ) { group(preferredUsername: $name) { - ...ActorFragment organizedEvents( afterDatetime: $afterDateTime beforeDatetime: $beforeDateTime @@ -465,6 +464,7 @@ export const FETCH_GROUP_EVENTS = gql` } total } + ...ActorFragment } } ${EVENT_OPTIONS_FRAGMENT} diff --git a/js/src/views/Event/GroupEvents.vue b/js/src/views/Event/GroupEvents.vue index 0d628c2e7..56f7da3ec 100644 --- a/js/src/views/Event/GroupEvents.vue +++ b/js/src/views/Event/GroupEvents.vue @@ -10,21 +10,21 @@ { name: RouteName.GROUP_EVENTS, params: { preferredUsername: usernameWithDomain(group) }, - text: $t('Events'), + text: t('Events'), }, ]" /> <section> <h1 class="" v-if="group"> {{ - $t("{group}'s events", { + t("{group}'s events", { group: displayName(group), }) }} </h1> <p v-if="isCurrentActorMember"> {{ - $t( + t( "When a moderator from the group creates an event and attributes it to the group, it will show up here." ) }} @@ -37,15 +37,15 @@ name: RouteName.CREATE_EVENT, query: { actorId: group.id }, }" - >{{ $t("+ Create an event") }}</o-button + >{{ t("+ Create an event") }}</o-button > <o-loading v-model:active="groupLoading"></o-loading> <section v-if="group"> <h2 class="text-2xl"> - {{ showPassedEvents ? $t("Past events") : $t("Upcoming events") }} + {{ showPassedEvents ? t("Past events") : t("Upcoming events") }} </h2> <o-switch class="mb-4" v-model="showPassedEvents">{{ - $t("Past events") + t("Past events") }}</o-switch> <grouped-multi-event-minimalist-card :events="group.organizedEvents.elements" @@ -60,18 +60,18 @@ :inline="true" :center="true" > - {{ $t("No events found") }} + {{ t("No events found") }} <template v-if="group.domain !== null"> <div class="mt-4"> <p> {{ - $t( + t( "This group is a remote group, it's possible the original instance has more informations." ) }} </p> <o-button variant="text" tag="a" :href="group.url"> - {{ $t("View the group profile on the original instance") }} + {{ t("View the group profile on the original instance") }} </o-button> </div> </template> @@ -79,12 +79,12 @@ <o-pagination class="mt-4" :total="group.organizedEvents.total" - v-model="page" + v-model:current="page" :per-page="EVENTS_PAGE_LIMIT" - :aria-next-label="$t('Next page')" - :aria-previous-label="$t('Previous page')" - :aria-page-label="$t('Page')" - :aria-current-label="$t('Current page')" + :aria-next-label="t('Next page')" + :aria-previous-label="t('Previous page')" + :aria-page-label="t('Page')" + :aria-current-label="t('Current page')" > </o-pagination> </section> @@ -97,10 +97,15 @@ import GroupedMultiEventMinimalistCard from "@/components/Event/GroupedMultiEven import { PERSON_MEMBERSHIPS } from "@/graphql/actor"; import { FETCH_GROUP_EVENTS } from "@/graphql/event"; import EmptyContent from "../../components/Utils/EmptyContent.vue"; -import { displayName, IPerson, usernameWithDomain } from "../../types/actor"; +import { + displayName, + IGroup, + IPerson, + usernameWithDomain, +} from "../../types/actor"; import { useQuery } from "@vue/apollo-composable"; import { useCurrentActorClient } from "@/composition/apollo/actor"; -import { computed } from "vue"; +import { computed, watch } from "vue"; import { useRoute } from "vue-router"; import { booleanTransformer, @@ -120,7 +125,10 @@ const { result: membershipsResult } = useQuery<{ }>( PERSON_MEMBERSHIPS, () => ({ id: currentActor.value?.id }), - () => ({ enabled: currentActor.value?.id !== undefined }) + () => ({ + enabled: + currentActor.value?.id !== undefined && currentActor.value?.id !== null, + }) ); const memberships = computed( () => membershipsResult.value?.person.memberships?.elements @@ -134,23 +142,50 @@ const showPassedEvents = useRouteQuery( booleanTransformer ); -const { result: groupResult, loading: groupLoading } = useQuery( - FETCH_GROUP_EVENTS, - () => ({ - name: route.params.preferredUsername, - beforeDateTime: showPassedEvents.value ? new Date() : null, - afterDateTime: showPassedEvents.value ? null : new Date(), - organisedEventsPage: page.value, - organisedEventsLimit: EVENTS_PAGE_LIMIT, - }) +/** + * Why is the following hack needed? Page doesn't want to be reactive! + * TODO: investigate + */ +const variables = computed(() => ({ + name: route.params.preferredUsername as string, + beforeDateTime: showPassedEvents.value ? new Date() : null, + afterDateTime: showPassedEvents.value ? null : new Date(), + organisedEventsPage: page.value, + organisedEventsLimit: EVENTS_PAGE_LIMIT, +})); + +watch( + variables, + (newVariables) => { + refetch(newVariables); + }, + { deep: true } ); + +const { + result: groupResult, + loading: groupLoading, + refetch: refetch, +} = useQuery< + { + group: IGroup; + }, + { + name: string; + beforeDateTime: Date | null; + afterDateTime: Date | null; + organisedEventsPage: number; + organisedEventsLimit: number; + } +>(FETCH_GROUP_EVENTS, variables); const group = computed(() => groupResult.value?.group); const { t } = useI18n({ useScope: "global" }); useHead({ - title: t("{group} events", { - group: displayName(group.value), - }), + title: () => + t("{group} events", { + group: displayName(group.value), + }), }); const isCurrentActorMember = computed((): boolean => {