Skip to content

Commit

Permalink
Components elements TypeScript (#577)
Browse files Browse the repository at this point in the history
* ➕ Add types to elements components

* 🔨 Fix optional type properties

* 🔨 Fix data table count and bg coloring

* ➕ Add types to InviteCodePanelView

* 👕 Refactor enum members to PascalCase

* ➕ Add types to SubscriberPanelView

* ➕ Add types to WaitingListPanelView

* 🔨 Fix type order and default argument
  • Loading branch information
devmount authored Jul 26, 2024
1 parent 14c2c9b commit 2ab14b1
Show file tree
Hide file tree
Showing 41 changed files with 742 additions and 644 deletions.
6 changes: 4 additions & 2 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import { useAppointmentStore } from '@/stores/appointment-store';
import { useScheduleStore } from '@/stores/schedule-store';
import RouteNotFoundView from '@/views/errors/RouteNotFoundView.vue';
import NotAuthenticatedView from '@/views/errors/NotAuthenticatedView.vue';
import { callKey } from '@/keys';
import { callKey, refreshKey } from '@/keys';
// component constants
const currentUser = useUserStore(); // data: { username, email, name, level, timezone, id }
Expand Down Expand Up @@ -164,6 +164,8 @@ const getDbData = async () => {
}
};
// provide refresh functions for components
// Deprecated - Please use refreshKey, as it's typed!
provide('refresh', getDbData);
// provide refresh functions for components
provide(refreshKey, getDbData);
</script>
11 changes: 7 additions & 4 deletions frontend/src/assets/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,18 @@

/* custom data table with wrapper */
.data-table {
@apply rounded-xl w-full border py-2 border-gray-100 bg-white text-sm shadow-sm dark:border-gray-500 dark:bg-gray-700 mb-4 ml-auto mr-0;
@apply rounded-xl w-full border py-2 border-gray-200 bg-gray-100 text-sm shadow-sm dark:border-gray-500 dark:bg-gray-700 mb-4 ml-auto mr-0;

table {
@apply w-full table-auto border-collapse bg-white text-sm shadow-sm dark:bg-gray-600;
@apply w-full table-auto border-collapse text-sm;
}

thead, tfoot {
@apply border-gray-200 bg-gray-100 dark:border-gray-500 dark:bg-gray-700 text-gray-600 dark:text-gray-300;
@apply text-gray-600 dark:text-gray-300;
}

th {
@apply px-4 text-left font-semibold border-gray-200 dark:border-gray-500;
@apply px-4 text-left font-semibold;
}
thead th {
@apply pb-4 pt-2;
Expand All @@ -171,6 +171,9 @@
td {
@apply border-y border-gray-200 p-4 dark:border-gray-500;
}
tbody td {
@apply bg-white dark:bg-gray-700;
}
}

/* Force First Time User Experience to be different from the rest of the site */
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/components/CalendarMiniMonth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ import {
IconChevronLeft,
IconChevronRight,
} from '@tabler/icons-vue';
import { appointmentState } from '@/definitions';
import { dayjsKey } from "@/keys";
// component constants
Expand Down Expand Up @@ -121,10 +120,7 @@ const eventsByDate = (d) => {
return props.placeholder
? events.value[key].filter((e) => dj(e.start).isAfter(dj()))
: events.value[key].filter(
(e) => (dj(e.start).add(e.duration, 'minutes').isAfter(dj()) && e.status === appointmentState.pending)
|| (e.attendee && e.status === appointmentState.pending)
|| e.remote
|| e.preview,
(e) => (dj(e.start).add(e.duration, 'minutes').isAfter(dj())) || e.remote || e.preview,
);
}
return [];
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/components/CalendarQalendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { Qalendar } from 'qalendar';
import 'qalendar/dist/style.css';
import CalendarEvent from '@/elements/calendar/CalendarEvent.vue';
import {
appointmentState,
bookingStatus,
ColorSchemes,
dateFormatStrings,
Expand Down Expand Up @@ -202,7 +201,7 @@ const calendarEvents = computed(() => {
customData: {
attendee: null,
slot_status: null,
booking_status: appointmentState.booked,
booking_status: bookingStatus.booked,
calendar_title: event.calendar_title,
calendar_color: event.calendar_color,
duration: event.duration,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/DataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="flex flex-col items-center justify-center gap-4">
<div class="flex w-full flex-col items-center justify-between gap-4 md:flex-row md:gap-0">
<div>
<span class="font-bold">{{ paginatedDataList.length }}</span> {{ dataName }}
<span class="font-bold">{{ totalDataLength }}</span> {{ dataName }}
</div>
<div v-for="filter in filters" :key="filter.name">
<label class="flex items-center gap-4 whitespace-nowrap">
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/composables/dayjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import weekday from 'dayjs/plugin/weekday';
import 'dayjs/locale/de';
import { dayjsKey } from '@/keys';
import { dayjsKey, durationHumanizedKey } from '@/keys';

export type IsoWeekday = {
iso: number,
Expand Down Expand Up @@ -42,10 +42,10 @@ export default function useDayJS(app: App<Element>, locale: string) {
app.provide(dayjsKey, dayjs);
app.provide('tzGuess', dayjs.tz.guess());

const hDuration = (m: number): string => ((m < 60)
? dayjs.duration(m, 'minutes').humanize()
: dayjs.duration(m / 60, 'hours').humanize());
app.provide('hDuration', hDuration);
const durationHumanized = (minutes: number): string => ((minutes < 60)
? dayjs.duration(minutes, 'minutes').humanize()
: dayjs.duration(minutes / 60, 'hours').humanize());
app.provide(durationHumanizedKey, durationHumanized);

// locale aware first day of week
const firstDayOfWeek = dayjs.localeData().firstDayOfWeek();
Expand Down
35 changes: 16 additions & 19 deletions frontend/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,6 @@ export const bookingCalendarViews = {
invalid: 12,
};

/**
* Available appointment states
* @enum
* @readonly
*/
// available appointment views - This doesn't align with anything anymore!
export const appointmentState = {
booked: 1,
pending: 2,
past: 3,
};

/**
* Booking status for slots. This mirrors models.BookingStatus on the backend
* @enum
Expand All @@ -110,9 +98,19 @@ export const bookingStatus = {
booked: 3,
};

/**
* Status to indicate if an invite code ist still valid or no longer valid
*/
export enum InviteStatus {
Active = 1,
Revoked = 2,
};

// available appointment views
export const appointmentViews = {
...appointmentState,
booked: 1,
pending: 2,
past: 3,
all: 4,
};

Expand Down Expand Up @@ -215,11 +213,11 @@ export const modalStates = {
* @enum
* @readonly
*/
export const alertSchemes = {
error: 1, // Alert indicates something's gone wrong
warning: 2, // Alert indicates something important
success: 3, // Alert indicates something's gone right
info: 4, // Alert indicates some neutral information
export enum AlertSchemes {
Error = 1, // Alert indicates something's gone wrong
Warning = 2, // Alert indicates something important
Success = 3, // Alert indicates something's gone right
Info = 4, // Alert indicates some neutral information
};

/**
Expand Down Expand Up @@ -305,7 +303,6 @@ export const waitingListAction = {

export default {
subscriberLevels,
appointmentState,
appointmentViews,
bookingCalendarViews,
calendarViews,
Expand Down
66 changes: 33 additions & 33 deletions frontend/src/elements/AlertBox.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
<script setup lang="ts">
import { IconX } from '@tabler/icons-vue';
import { AlertSchemes } from '@/definitions';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
// component properties
interface Props {
title?: string; // flag showing this event as busy and non-selectable
canClose?: boolean; // flag for making this alert closable
scheme?: AlertSchemes; // flag, are we in month view?
};
withDefaults(defineProps<Props>(), {
canClose: true,
scheme: AlertSchemes.Error,
})
const emit = defineEmits(['close']);
</script>

<template>
<div
class="flex items-center p-2 leading-none text-white shadow-md shadow-black/30 dark:shadow-lg lg:inline-flex lg:rounded-xl"
role="alert"
:class="{
'bg-rose-600 dark:bg-rose-900': scheme === alertSchemes.error,
'bg-orange-400 dark:bg-orange-700': scheme === alertSchemes.warning,
'bg-green-400 dark:bg-green-700': scheme === alertSchemes.success,
'bg-teal-400 dark:bg-teal-700': scheme === alertSchemes.info,
'bg-rose-600 dark:bg-rose-900': scheme === AlertSchemes.Error,
'bg-orange-400 dark:bg-orange-700': scheme === AlertSchemes.Warning,
'bg-green-400 dark:bg-green-700': scheme === AlertSchemes.Success,
'bg-teal-400 dark:bg-teal-700': scheme === AlertSchemes.Info,
}"
>
<span class="mr-3 flex rounded-full px-2 py-1 text-center text-xs font-bold uppercase"
<span
v-if="title"
class="mr-3 flex rounded-full px-2 py-1 text-center text-xs font-bold uppercase"
:class="{
'bg-rose-500 dark:bg-rose-800': scheme === alertSchemes.error,
'bg-orange-500 dark:bg-orange-800': scheme === alertSchemes.warning,
'bg-green-500 dark:bg-green-800': scheme === alertSchemes.success,
'bg-teal-500 dark:bg-teal-800': scheme === alertSchemes.info,
'bg-rose-500 dark:bg-rose-800': scheme === AlertSchemes.Error,
'bg-orange-500 dark:bg-orange-800': scheme === AlertSchemes.Warning,
'bg-green-500 dark:bg-green-800': scheme === AlertSchemes.Success,
'bg-teal-500 dark:bg-teal-800': scheme === AlertSchemes.Info,
}"
v-if="title"
>
{{ title }}
</span>
Expand All @@ -28,26 +51,3 @@
</span>
</div>
</template>

<script setup>
import { IconX } from '@tabler/icons-vue';
import { alertSchemes } from '@/definitions';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
defineProps({
title: String,
canClose: {
type: Boolean,
default: true,
},
scheme: {
type: Number,
default: alertSchemes.error,
},
});
const emit = defineEmits(['close']);
</script>
66 changes: 34 additions & 32 deletions frontend/src/elements/AppointmentGridItem.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
<script setup lang="ts">
import { bookingStatus } from '@/definitions';
import { inject, computed } from 'vue';
import { keyByValue, timeFormat } from '@/utils';
import { useI18n } from 'vue-i18n';
import { Appointment } from '@/models';
// icons
import {
IconBulb,
IconCalendar,
IconClock,
} from '@tabler/icons-vue';
import { dayjsKey, paintBackgroundKey } from "@/keys";
// component constants
const { t } = useI18n();
const paintBackground = inject(paintBackgroundKey);
const dj = inject(dayjsKey);
// component properties
interface Props {
appointment: Appointment; // appointment to show details for
};
const props = defineProps<Props>();
// true if an appointment from the past was given
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);
</script>

<template>
<div
class="rounded border-l-8 border-sky-400"
Expand All @@ -7,7 +40,7 @@
}"
:style="{ borderColor: appointment.calendar_color }"
@mouseover="el => !isPast ? paintBackground(el, appointment.calendar_color, '22') : null"
@mouseout="el => !isPast ? paintBackground(el, appointment.calendar_color, _, true) : null"
@mouseout="el => !isPast ? paintBackground(el, appointment.calendar_color, undefined, true) : null"
>
<div
class="flex h-full flex-col gap-1 rounded-r border-y-2 border-r-2 border-solid border-sky-400 px-4 py-3"
Expand All @@ -33,34 +66,3 @@
</div>
</div>
</template>

<script setup>
import { appointmentState, bookingStatus } from '@/definitions';
import { inject, computed } from 'vue';
import { keyByValue, timeFormat } from '@/utils';
import { useI18n } from 'vue-i18n';
// icons
import {
IconBulb,
IconCalendar,
IconClock,
} from '@tabler/icons-vue';
import { dayjsKey } from "@/keys";
// component constants
const { t } = useI18n();
const paintBackground = inject('paintBackground');
const dj = inject(dayjsKey);
// component properties
const props = defineProps({
appointment: Object, // appointment to show details for
});
// true if an appointment from the past was given
const isPast = computed(() => props.appointment.status === appointmentState.past);
// true if a pending appointment was given
const isPending = computed(() => props.appointment.status === appointmentState.pending);
</script>
Loading

0 comments on commit 2ab14b1

Please sign in to comment.