Skip to content

Commit

Permalink
Suggested changes, dead code removal, and clean up:
Browse files Browse the repository at this point in the history
* Added getPreferredTheme which returns either colorSchemes.light or colorSchemes.dark based on preference.
* Added getLocale which returns ['de', 'en', null] depending on preference (or lack of.)
* Cleared out a bunch of unused code.
* Fixed light/dark mode issues with Qalendar integration. (I hope!)
* Removed media query for dark mode check, rely on tailwind dark class.
* Hooked up locale to Qalendar if specified.

* Adjusted various free date formats to our handy-dandy enum.
  • Loading branch information
MelissaAutumn committed Feb 23, 2024
1 parent d635549 commit a136b0c
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 119 deletions.
34 changes: 16 additions & 18 deletions frontend/src/components/CalendarQalendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
import { Qalendar } from 'qalendar';
import 'qalendar/dist/style.css';
import CalendarEvent from '@/elements/CalendarEvent.vue';
import { appointmentState } from '@/definitions';
import { timeFormat } from '@/utils';
import { appointmentState, colorSchemes, dateFormatStrings } from '@/definitions';
import { getLocale, getPreferredTheme, timeFormat } from '@/utils';
// component constants
const dj = inject('dayjs');
Expand All @@ -29,17 +29,11 @@ const {
const qalendarRef = ref();
const dateFormatStrings = {
qalendar: 'YYYY-MM-DD HH:mm',
qalendarFullDay: 'YYYY-MM-DD',
// Display formats
display12Hour: 'hh:mma',
display24Hour: 'HH:mm',
};
const displayFormat = timeFormat();
const calendarColors = ref({});
const selectedDate = ref(null);
const calendarMode = ref('month');
const preferredTheme = getPreferredTheme();
// component emits
const emit = defineEmits(['daySelected', 'eventSelected', 'dateChange']);
Expand Down Expand Up @@ -259,6 +253,12 @@ const config = ref({
},
});
// We only support two locales right now so just stick this in.
const locale = getLocale();
if (locale) {
config.value.locale = locale === 'de' ? 'de-DE' : 'en-US';
}
/**
* Qalendar's selectedDate is only set on init and never updated. So we have to poke at their internals...
*/
Expand All @@ -275,7 +275,7 @@ watch(currentDate, () => {
</script>
<template>
<div class="w-full">
<div class="w-full" :style="{'color-scheme': preferredTheme === colorSchemes.dark ? 'dark' : null}" :class="{'is-light-mode': preferredTheme === colorSchemes.light}">
<qalendar
:events="calendarEvents"
:config="config"
Expand Down Expand Up @@ -318,7 +318,7 @@ watch(currentDate, () => {
/*
* Re-theme of Qalendar for Appointment
*/
:root {
.calendar-root-wrapper {
--qalendar-appointment-bg: theme('backgroundColor.white');
--qalendar-appointment-fg: theme('backgroundColor.gray.100');
--qalendar-appointment-border-color: theme('borderColor.gray.200');
Expand All @@ -330,13 +330,11 @@ watch(currentDate, () => {
--qalendar-appointment-button-hover-scale: theme('scale.102');
}
@media (prefers-color-scheme: dark) {
:root {
--qalendar-appointment-bg: theme('backgroundColor.gray.700');
--qalendar-appointment-fg: theme('backgroundColor.gray.600');
--qalendar-appointment-border-color: theme('borderColor.gray.500');
--qalendar-appointment-text: theme('colors.gray.300');
}
.dark .calendar-root-wrapper {
--qalendar-appointment-bg: theme('backgroundColor.gray.700');
--qalendar-appointment-fg: theme('backgroundColor.gray.600');
--qalendar-appointment-border-color: theme('borderColor.gray.500');
--qalendar-appointment-text: theme('colors.gray.300');
}
/* Override some of Qalendar's css variables */
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/components/ScheduleCreation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,9 @@
</template>

<script setup>
import { locationTypes, meetingLinkProviderType, scheduleCreationState } from '@/definitions';
import {
dateFormatStrings, locationTypes, meetingLinkProviderType, scheduleCreationState,
} from '@/definitions';
import {
ref, reactive, computed, inject, watch, onMounted,
} from 'vue';
Expand All @@ -322,7 +324,7 @@ import SecondaryButton from '@/elements/SecondaryButton';
import TabBar from '@/components/TabBar';
// icons
import { IconChevronDown, IconExternalLink } from '@tabler/icons-vue';
import { IconChevronDown } from '@tabler/icons-vue';
import AlertBox from '@/elements/AlertBox';
import SwitchToggle from '@/elements/SwitchToggle';
Expand All @@ -332,8 +334,7 @@ const { t } = useI18n();
const dj = inject('dayjs');
const call = inject('call');
const isoWeekdays = inject('isoWeekdays');
const dateFormat = 'YYYY-MM-DD';
const dateFormat = dateFormatStrings.qalendarFullDay;
// component emits
const emit = defineEmits(['created', 'updated']);
Expand Down
73 changes: 3 additions & 70 deletions frontend/src/components/bookingView/BookingViewSlotSelection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,91 +36,24 @@
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { bookingCalendarViews as views } from '@/definitions';
import { useBookingViewStore } from '@/stores/booking-view-store';
import { dateFormatStrings } from '@/definitions';
import CalendarDay from '@/components/CalendarDay';
import CalendarMonth from '@/components/CalendarMonth';
import CalendarPageHeading from '@/elements/CalendarPageHeading';
import CalendarWeek from '@/components/CalendarWeek';
import PrimaryButton from '@/elements/PrimaryButton';
import { computed, inject } from 'vue';
import CalendarQalendar from '@/components/CalendarQalendar.vue';
const { t } = useI18n();
const dj = inject('dayjs');
const {
appointment, activeView, activeDate, selectedEvent,
appointment, activeDate, selectedEvent,
} = storeToRefs(useBookingViewStore());
const startOfActiveWeek = computed(() => activeDate.value.startOf('week'));
const endOfActiveWeek = computed(() => activeDate.value.endOf('week'));
const emit = defineEmits(['openModal']);
defineProps({
showNavigation: Boolean,
});
// Computed
/**
* Appointment data formatted for the month calendar view.
* @type {ComputedRef<[{slots: [], title: string}]>}
*/
const dayPlaceholder = computed(() => {
const apmt = { title: t('label.checkAvailableSlots'), slots: [] };
const existingDates = [];
appointment.value?.slots.forEach((slot) => {
const key = dj(slot.start).format('YYYY-MM-DD');
if (!existingDates.includes(key)) {
existingDates.push(key);
apmt.slots.push(slot);
}
});
return [apmt];
});
/**
* Returns the formatted date depending on the current calendar view
* @type {ComputedRef<string>}
*/
const viewTitle = computed(() => {
switch (activeView.value) {
case views.day:
return activeDate.value.format('dddd Do');
case views.week:
case views.weekAfterMonth:
return `${startOfActiveWeek.value.format('ddd Do')} - ${endOfActiveWeek.value.format('ddd Do')}`;
default:
return '';
}
});
// Functions
/**
* Adjusts the date by 1 unit (e.g. month, day, week.) Direction is determined by forward.
* @param unit string
* @param forward bool
*/
const dateNav = (unit = 'month', forward = true) => {
if (forward) {
activeDate.value = activeDate.value.add(1, unit);
} else {
activeDate.value = activeDate.value.subtract(1, unit);
}
};
/**
* Display a specific week within our calendar view.
* This is triggered when you click into an event on the monthly calendar view.
* @param day string
*/
const showWeek = (day) => {
activeDate.value = dj(day);
activeView.value = views.weekAfterMonth;
};
/**
* Select a specific time slot
* @param day string
Expand All @@ -129,7 +62,7 @@ const selectEvent = (day) => {
// set event selected
for (let i = 0; i < appointment.value.slots.length; i += 1) {
const slot = appointment.value.slots[i];
if (slot.start.format('YYYY-MM-DD HH:mm') === day) {
if (slot.start.format(dateFormatStrings.qalendar) === day) {
slot.selected = true;
const e = { ...appointment.value, ...slot };
delete e.slots;
Expand Down
21 changes: 20 additions & 1 deletion frontend/src/definitions.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
/**
* Available date format strings
* @readonly
* @enum
*/
export const dateFormatStrings = {
// Qalendar specific formats (These have to be in this format for qalendar to understand them)
qalendar: 'YYYY-MM-DD HH:mm',
qalendarFullDay: 'YYYY-MM-DD',
// Time display formats
display12Hour: 'hh:mma',
display24Hour: 'HH:mm',
};

// appointment location types
export const subscriberLevels = {
basic: 1,
Expand Down Expand Up @@ -94,7 +108,11 @@ export const settingsSections = {
// faq: 6,
};

// available color schemes for theme
/**
* available color schemes for theme
* @readonly
* @enum
*/
export const colorSchemes = {
system: 1,
dark: 2,
Expand Down Expand Up @@ -141,4 +159,5 @@ export default {
settingsSections,
viewTypes,
meetingLinkProviderType,
dateFormatStrings,
};
41 changes: 39 additions & 2 deletions frontend/src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// get the first key of given object that points to given value
import { colorSchemes } from '@/definitions.js';

export const keyByValue = (o, v) => Object.keys(o).find((k) => o[k] === v);

// create event color for border and background, inherited from calendar color attribute
Expand Down Expand Up @@ -90,12 +92,46 @@ export const showEventPopup = (el, event, position = 'right') => {
return obj;
};

/**
* Returns the stored locale setting or null if none is set.
* TODO: This should be moved to a settings store
* @returns {string|null}
*/
export const getLocale = () => {
const locale = localStorage?.getItem('locale');
if (!locale) {
return null;
}
return locale;
};

/**
* Returns the stored theme value. If the stored value does not exist, it will guess based on prefers-color-scheme.
* TODO: This should be moved to a settings store
* @returns {colorSchemes} - Colour theme value
*/
export const getPreferredTheme = () => {
const theme = localStorage?.getItem('theme');
if (!theme) {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? colorSchemes.dark : colorSchemes.light;
}

switch (theme) {
case 'dark':
return colorSchemes.dark;
case 'light':
return colorSchemes.light;
default:
// This would be colorSchemes.system, but I feel like we need a definitive answer here.
return window.matchMedia('(prefers-color-scheme: dark)').matches ? colorSchemes.dark : colorSchemes.light;
}
};

/**
* via: https://stackoverflow.com/a/11868398
*/
export const getAccessibleColor = (hexcolor) => {
// TODO: Move this to utility and pull it into Vue.
const defaultColor = localStorage?.getItem('theme') === 'dark' ?? !window.matchMedia('(prefers-color-scheme: dark)').matches ? 'white' : 'black';
const defaultColor = getPreferredTheme() === colorSchemes.dark ? 'white' : 'black';
if (!hexcolor) {
return defaultColor;
}
Expand All @@ -115,4 +151,5 @@ export default {
initialEventPopupData,
showEventPopup,
getAccessibleColor,
getLocale,
};
24 changes: 0 additions & 24 deletions frontend/src/views/CalendarView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -133,32 +133,8 @@ const activeDateRange = computed(() => ({
end: activeDate.value.endOf('month'),
}));
// date calculations
const startOfActiveWeek = computed(() => activeDate.value.startOf('week'));
const endOfActiveWeek = computed(() => activeDate.value.endOf('week'));
// active menu item for tab navigation of calendar views
const tabActive = ref(calendarViews[route.params.view]);
const updateTab = (view) => {
router.replace({
name: route.name,
params: { view, date: route.params.date ?? dj().format('YYYY-MM-DD') },
});
tabActive.value = calendarViews[view];
};
// calculate page title
const pageTitle = computed(() => {
switch (tabActive.value) {
case calendarViews.day:
return activeDate.value.format('dddd Do');
case calendarViews.week:
return `${startOfActiveWeek.value.format('ddd Do')} - ${endOfActiveWeek.value.format('ddd Do')}`;
case calendarViews.month:
default:
return '';
}
});
// appointment creation state
const creationStatus = ref(appointmentCreationState.hidden);
Expand Down

0 comments on commit a136b0c

Please sign in to comment.