Support basic formatting in the display of the description fields to locations, events and shifts by rendering them as Markdown using the micromark library.
123 lines
3 KiB
Vue
123 lines
3 KiB
Vue
<!--
|
||
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
|
||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||
-->
|
||
<template>
|
||
<section class="event">
|
||
<h3>{{ event.name }}</h3>
|
||
<p v-if=event.host>
|
||
Host: {{ event.host }}
|
||
</p>
|
||
<div v-if="event.notice" class="notice preWrap">
|
||
<div class="noticeIcon">
|
||
⚠️
|
||
</div>
|
||
<p>
|
||
{{ event.notice }}
|
||
</p>
|
||
</div>
|
||
<div
|
||
v-if="descriptionHtml"
|
||
class="flow"
|
||
v-html="descriptionHtml"
|
||
/>
|
||
<p v-if="event.interested">
|
||
{{ event.interested }} interested
|
||
</p>
|
||
<p v-if="accountStore.valid">
|
||
<button
|
||
class="interested"
|
||
:class="{ active: accountStore.interestedEventIds.has(event.id) }"
|
||
@click="toggle('event', event.id, [...event.slots.keys()])"
|
||
>
|
||
{{ accountStore.interestedEventIds.has(event.id) ? "✔ interested" : "🔔 interested?" }}
|
||
</button>
|
||
</p>
|
||
|
||
<h4>Timeslots</h4>
|
||
<ul>
|
||
<li v-for="slot in event.slots.values()" :key="slot.id">
|
||
{{ formatTime(slot.start) }} - {{ formatTime(slot.end) }}
|
||
<button
|
||
v-if="accountStore.valid && event.slots.size > 1"
|
||
class="interested"
|
||
:disabled="accountStore.interestedEventIds.has(event.id)"
|
||
:class="{ active: accountStore.interestedEventIds.has(event.id) || accountStore.interestedEventSlotIds.has(slot.id) }"
|
||
@click="toggle('slot', slot.id)"
|
||
>
|
||
{{ accountStore.interestedEventIds.has(event.id) || accountStore.interestedEventSlotIds.has(slot.id) ? "✔ interested" : "🔔 interested?" }}
|
||
</button>
|
||
<template v-if="slot.interested">
|
||
({{ slot.interested }} interested)
|
||
</template>
|
||
<p v-if="slot.assigned.size">
|
||
Crew:
|
||
{{ [...slot.assigned].map(id => usersStore.users.get(id)?.name).join(", ") }}
|
||
</p>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { micromark } from 'micromark';
|
||
import { DateTime } from '~/shared/utils/luxon';
|
||
|
||
const props = defineProps<{
|
||
event: ClientScheduleEvent
|
||
}>()
|
||
|
||
const descriptionHtml = computed(() => {
|
||
if (props.event.description) {
|
||
return micromark(props.event.description);
|
||
}
|
||
});
|
||
|
||
const accountStore = useAccountStore();
|
||
const usersStore = useUsersStore();
|
||
|
||
function formatTime(time: DateTime) {
|
||
return time.toFormat("yyyy-LL-dd HH:mm");
|
||
}
|
||
|
||
async function toggle(type: "event" | "slot", id: number, slotIds?: number[]) {
|
||
await accountStore.toggleInterestedId(type, id, slotIds);
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.event {
|
||
background: color-mix(in oklab, var(--background), grey 20%);
|
||
padding: 0.5rem;
|
||
border-radius: 0.5rem;
|
||
}
|
||
.event h3 {
|
||
margin: 0;
|
||
}
|
||
.event + .event {
|
||
margin-block-start: 0.5rem;
|
||
}
|
||
|
||
.notice {
|
||
display: flex;
|
||
width: fit-content;
|
||
gap: 0.5rem;
|
||
padding: 0.5rem;
|
||
margin-block: 0.5rem;
|
||
border-radius: 0.25rem;
|
||
border: 1px solid color-mix(in oklab, CanvasText, orange 50%);
|
||
background-color: color-mix(in oklab, Canvas, orange 40%);
|
||
}
|
||
.noticeIcon {
|
||
flex: 0 0 auto;
|
||
align-self: center;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
button {
|
||
padding-inline: 0.2em;
|
||
}
|
||
button.active {
|
||
color: color-mix(in oklab, var(--foreground), green 50%);
|
||
}
|
||
</style>
|