Skip to content

Commit

Permalink
➕ Add types to appointment modal
Browse files Browse the repository at this point in the history
  • Loading branch information
devmount committed Aug 1, 2024
1 parent 02a3915 commit 094d6c7
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 258 deletions.
108 changes: 55 additions & 53 deletions frontend/src/components/AppointmentModal.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
<script setup lang="ts">
import { BookingStatus } from '@/definitions';
import { timeFormat } from '@/utils';
import { computed, inject } from 'vue';
import { useI18n } from 'vue-i18n';
import { Appointment } from '@/models';
// icons
import {
IconCalendar,
IconCalendarEvent,
IconClock,
IconNotes,
IconUsers,
IconVideo,
IconX,
} from '@tabler/icons-vue';
import PrimaryButton from '@/elements/PrimaryButton.vue';
import CautionButton from '@/elements/CautionButton.vue';
import { useUserStore } from '@/stores/user-store';
import { dayjsKey } from "@/keys";
const user = useUserStore();
// component constants
const { t } = useI18n();
const dj = inject(dayjsKey);
// component properties
interface Props {
open: boolean, // modal state
appointment?: Appointment; // appointment data to display
};
const props = defineProps<Props>();
// attendees list
const attendeesSlots = computed(() => props.appointment.slots.filter((s) => s.attendee));
// calculate initials
const initials = (name: string) => name.split(' ').map((p) => p[0]).join('');
const confirmationUrl = computed(() => `${user.data.signedUrl}/confirm/${props.appointment.slots[0].id}/${props.appointment.slots[0].booking_tkn}/1`);
const denyUrl = computed(() => `${user.data.signedUrl}/confirm/${props.appointment.slots[0].id}/${props.appointment.slots[0].booking_tkn}/0`);
const answer = (isConfirmed: boolean) => {
window.location.href = isConfirmed ? confirmationUrl.value : denyUrl.value;
};
// component emits
const emit = defineEmits(['close']);
</script>

<template>
<transition>
<div
Expand Down Expand Up @@ -110,7 +163,7 @@
</div>
<div class="rounded-lg border border-gray-400 p-4 dark:border-gray-600">{{ appointment.details }}</div>
</div>
<div class="p-6" v-if="appointment?.slots[0].booking_status === bookingStatus.requested">
<div class="p-6" v-if="appointment?.slots[0].booking_status === BookingStatus.Requested">
<p>{{ attendeesSlots.map((s) => s.attendee.email).join(', ') }} have requested a booking at this time.</p>
<div class="mt-4 flex justify-center gap-4">
<primary-button class="btn-confirm" @click="answer(true)" :title="t('label.confirm')">
Expand All @@ -121,60 +174,9 @@
</caution-button>
</div>
</div>
<div class="p-6" v-if="appointment?.slots[0].booking_status === bookingStatus.booked">
<div class="p-6" v-if="appointment?.slots[0].booking_status === BookingStatus.Booked">
<p>This booking is confirmed.</p>
</div>
</div>
</transition>
</template>

<script setup>
import { bookingStatus } from '@/definitions';
import { timeFormat } from '@/utils';
import { computed, inject } from 'vue';
import { useI18n } from 'vue-i18n';

// icons
import {
IconCalendar,
IconCalendarEvent,
IconClock,
IconNotes,
IconUsers,
IconVideo,
IconX,
} from '@tabler/icons-vue';
import PrimaryButton from '@/elements/PrimaryButton.vue';
import CautionButton from '@/elements/CautionButton.vue';
import { useUserStore } from '@/stores/user-store';
import { dayjsKey } from "@/keys";

const user = useUserStore();

// component constants
const { t } = useI18n();
const dj = inject(dayjsKey);

// component properties
const props = defineProps({
open: Boolean, // modal state
appointment: Object, // appointment data to display
});

// attendees list
const attendeesSlots = computed(() => props.appointment.slots.filter((s) => s.attendee));

// calculate initials
const initials = (name) => name.split(' ').map((p) => p[0]).join('');

const confirmationUrl = computed(() => `${user.data.signedUrl}/confirm/${props.appointment.slots[0].id}/${props.appointment.slots[0].booking_tkn}/1`);
const denyUrl = computed(() => `${user.data.signedUrl}/confirm/${props.appointment.slots[0].id}/${props.appointment.slots[0].booking_tkn}/0`);

const answer = (isConfirmed) => {
window.location.href = isConfirmed ? confirmationUrl.value : denyUrl.value;
};

// component emits
const emit = defineEmits(['close']);

</script>
6 changes: 3 additions & 3 deletions frontend/src/elements/AppointmentGridItem.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { bookingStatus } from '@/definitions';
import { BookingStatus } from '@/definitions';
import { inject, computed } from 'vue';
import { keyByValue, timeFormat } from '@/utils';
import { useI18n } from 'vue-i18n';
Expand Down Expand Up @@ -28,7 +28,7 @@ const props = defineProps<Props>();
const isPast = computed(() => props.appointment.slots[0].start < dj());
// true if a pending appointment was given
const isPending = computed(() => props.appointment.slots[0].booking_status === bookingStatus.requested);
const isPending = computed(() => props.appointment.slots[0].booking_status === BookingStatus.Requested);
</script>

<template>
Expand All @@ -50,7 +50,7 @@ const isPending = computed(() => props.appointment.slots[0].booking_status === b
<div>{{ appointment.title }}</div>
<div class="flex items-center gap-1 text-sm">
<icon-bulb class="size-4 shrink-0 fill-transparent stroke-gray-500 stroke-2"/>
{{ t('label.' + keyByValue(bookingStatus, appointment?.slots[0].booking_status ?? 'Unknown')) }}
{{ t('label.' + keyByValue(BookingStatus, appointment?.slots[0].booking_status ?? 'Unknown', true)) }}
</div>
<div class="flex items-center gap-1 text-sm">
<icon-calendar class="size-4 shrink-0 fill-transparent stroke-gray-500 stroke-2"/>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/elements/calendar/CalendarEvent.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { bookingStatus } from '@/definitions';
import { BookingStatus } from '@/definitions';
import { computed, inject, ref, toRefs } from 'vue';
import { timeFormat, initialEventPopupData, showEventPopup } from '@/utils';
import CalendarEventPlaceholder from '@/elements/calendar/CalendarEventPlaceholder.vue';
Expand Down Expand Up @@ -31,7 +31,7 @@ const { event, timeSlotDuration, timeSlotHeight } = toRefs(props);
const eventData = event.value.customData;
const elementHeight = computed(() => (eventData.duration / timeSlotDuration.value) * timeSlotHeight.value);
const isBusy = computed(() => eventData.slot_status === bookingStatus.booked);
const isBusy = computed(() => eventData.slot_status === BookingStatus.Booked);
// component emits
const emit = defineEmits(['eventSelected']);
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/stores/appointment-store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Dayjs, ConfigType } from 'dayjs';
import { defineStore } from 'pinia';
import { ref, computed, inject } from 'vue';
import { bookingStatus } from '@/definitions';
import { BookingStatus } from '@/definitions';
import { useUserStore } from '@/stores/user-store';
import {
Appointment, AppointmentListResponse, Fetch, Slot,
Expand All @@ -19,7 +19,7 @@ export const useAppointmentStore = defineStore('appointments', () => {
// Data
const appointments = ref<Appointment[]>([]);
const pendingAppointments = computed(
(): Appointment[] => appointments.value.filter((a) => a?.slots[0]?.booking_status === bookingStatus.requested),
(): Appointment[] => appointments.value.filter((a) => a?.slots[0]?.booking_status === BookingStatus.Requested),
);

/**
Expand All @@ -29,7 +29,7 @@ export const useAppointmentStore = defineStore('appointments', () => {
const userStore = useUserStore();

appointments.value.forEach((a) => {
a.active = a.status !== bookingStatus.booked;
a.active = a.status !== BookingStatus.Booked;
// convert start dates from UTC back to users timezone
a.slots.forEach((s: Slot) => {
s.start = dj(s.start).utc(true).tz(userStore.data.timezone ?? tzGuess);
Expand Down
Loading

0 comments on commit 094d6c7

Please sign in to comment.