mobilizon/js/src/components/Event/EventCard.vue

220 lines
6.8 KiB
Vue

<template>
<LinkOrRouterLink
class="mbz-card snap-center dark:bg-mbz-purple"
:class="{
'sm:flex sm:items-start': mode === 'row',
'sm:max-w-xs sm:w-[18rem] shrink-0 flex flex-col': mode === 'column',
}"
:to="to"
:isInternal="isInternal"
>
<div
class="bg-secondary rounded-lg"
:class="{ 'sm:w-full sm:max-w-[20rem]': mode === 'row' }"
>
<figure class="block relative pt-40">
<lazy-image-wrapper
:picture="event.picture"
style="height: 100%; position: absolute; top: 0; left: 0; width: 100%"
/>
<div
class="absolute top-3 right-0 ltr:-mr-1 rtl:-ml-1 z-10 max-w-xs no-underline flex flex-col gap-1 items-end"
v-show="mode === 'column'"
v-if="event.tags || event.status !== EventStatus.CONFIRMED"
>
<mobilizon-tag
variant="info"
v-if="event.status === EventStatus.TENTATIVE"
>
{{ $t("Tentative") }}
</mobilizon-tag>
<mobilizon-tag
variant="danger"
v-if="event.status === EventStatus.CANCELLED"
>
{{ $t("Cancelled") }}
</mobilizon-tag>
<router-link
:to="{ name: RouteName.TAG, params: { tag: tag.title } }"
v-for="tag in (event.tags || []).slice(0, 3)"
:key="tag.slug"
>
<mobilizon-tag dir="auto" :with-hash-tag="true">{{
tag.title
}}</mobilizon-tag>
</router-link>
</div>
</figure>
</div>
<div class="p-2 flex-auto" :class="{ 'sm:flex-1': mode === 'row' }">
<div class="relative flex flex-col h-full">
<div
class="-mt-3 h-0 flex mb-3 ltr:ml-0 rtl:mr-0 items-end self-start"
:class="{ 'sm:hidden': mode === 'row' }"
>
<date-calendar-icon
:small="true"
v-if="!mergedOptions.hideDate"
:date="event.beginsOn.toString()"
/>
</div>
<span
class="text-gray-700 dark:text-white font-semibold hidden"
:class="{ 'sm:block': mode === 'row' }"
>{{ formatDateTimeWithCurrentLocale }}</span
>
<div class="w-full flex flex-col justify-between h-full">
<h2
class="mt-0 mb-2 text-2xl line-clamp-3 font-bold text-violet-3 dark:text-white"
dir="auto"
:lang="event.language"
>
{{ event.title }}
</h2>
<div class="">
<div
class="flex items-center text-violet-3 dark:text-white"
dir="auto"
>
<figure class="" v-if="actorAvatarURL">
<img
class="rounded-xl"
:src="actorAvatarURL"
alt=""
width="24"
height="24"
/>
</figure>
<account-circle v-else />
<span class="font-semibold ltr:pl-2 rtl:pr-2">
{{ organizerDisplayName(event) }}
</span>
</div>
<inline-address
v-if="event.physicalAddress"
:physical-address="event.physicalAddress"
/>
<div
class="flex items-center text-sm"
dir="auto"
v-else-if="event.options && event.options.isOnline"
>
<Video />
<span class="ltr:pl-2 rtl:pr-2">{{ $t("Online") }}</span>
</div>
<div
class="mt-1 no-underline gap-1 items-center hidden"
:class="{ 'sm:flex': mode === 'row' }"
v-if="event.tags || event.status !== EventStatus.CONFIRMED"
>
<mobilizon-tag
variant="info"
v-if="event.status === EventStatus.TENTATIVE"
>
{{ $t("Tentative") }}
</mobilizon-tag>
<mobilizon-tag
variant="danger"
v-if="event.status === EventStatus.CANCELLED"
>
{{ $t("Cancelled") }}
</mobilizon-tag>
<router-link
:to="{ name: RouteName.TAG, params: { tag: tag.title } }"
v-for="tag in (event.tags || []).slice(0, 3)"
:key="tag.slug"
>
<mobilizon-tag :with-hash-tag="true" dir="auto">{{
tag.title
}}</mobilizon-tag>
</router-link>
</div>
</div>
</div>
</div>
</div>
</LinkOrRouterLink>
</template>
<script lang="ts" setup>
import {
IEvent,
IEventCardOptions,
organizerDisplayName,
organizerAvatarUrl,
} from "@/types/event.model";
import DateCalendarIcon from "@/components/Event/DateCalendarIcon.vue";
import LazyImageWrapper from "@/components/Image/LazyImageWrapper.vue";
import { EventStatus } from "@/types/enums";
import RouteName from "../../router/name";
import InlineAddress from "@/components/Address/InlineAddress.vue";
import { computed, inject } from "vue";
import MobilizonTag from "@/components/TagElement.vue";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Video from "vue-material-design-icons/Video.vue";
import { formatDateTimeForEvent } from "@/utils/datetime";
import type { Locale } from "date-fns";
import LinkOrRouterLink from "../core/LinkOrRouterLink.vue";
const props = withDefaults(
defineProps<{
event: IEvent;
options?: IEventCardOptions;
mode?: "row" | "column";
}>(),
{ mode: "column" }
);
const defaultOptions: IEventCardOptions = {
hideDate: false,
loggedPerson: false,
hideDetails: false,
organizerActor: null,
isRemoteEvent: false,
isLoggedIn: true,
};
const mergedOptions = computed<IEventCardOptions>(() => ({
...defaultOptions,
...props.options,
}));
// const actor = computed<Actor>(() => {
// return Object.assign(
// new Person(),
// props.event.organizerActor ?? mergedOptions.value.organizerActor
// );
// });
const actorAvatarURL = computed<string | null>(() =>
organizerAvatarUrl(props.event)
);
const dateFnsLocale = inject<Locale>("dateFnsLocale");
const formatDateTimeWithCurrentLocale = computed(() => {
if (!dateFnsLocale) return;
return formatDateTimeForEvent(new Date(props.event.beginsOn), dateFnsLocale);
});
const isInternal = computed(() => {
return (
mergedOptions.value.isRemoteEvent &&
mergedOptions.value.isLoggedIn === false
);
});
const to = computed(() => {
if (mergedOptions.value.isRemoteEvent) {
if (mergedOptions.value.isLoggedIn === false) {
return props.event.url;
}
return {
name: RouteName.INTERACT,
query: { uri: encodeURI(props.event.url) },
};
}
return { name: RouteName.EVENT, params: { uuid: props.event.uuid } };
});
</script>