Skip to content

Commit

Permalink
New user experience fixes for Schedule Settings:
Browse files Browse the repository at this point in the history
* Require a connected calendar in order to create a schedule
* Prompt the user to save to create a schedule if they have a calendar connected but no schedule.
* Consolidate component in <snackish-bar/>
  • Loading branch information
MelissaAutumn committed Apr 23, 2024
1 parent a48eb3e commit 270e742
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 25 deletions.
2 changes: 1 addition & 1 deletion frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const call = createFetch({
data.detail?.message || 'Please re-connect with Google',
url,
);
} else if (response.status === 401 && data?.detail?.id === 'INVALID_TOKEN') {
} else if (response && response.status === 401 && data?.detail?.id === 'INVALID_TOKEN') {
// Clear current user data, and ship them to the login screen!
await currentUser.$reset();
await router.push('/login');
Expand Down
58 changes: 36 additions & 22 deletions frontend/src/components/ScheduleCreation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -284,22 +284,36 @@
</div>
</div>
</div>
<!-- Snack-like Bar-ish -->
<div v-if="!scheduleInput.active || isFormDirty" class="h-28 w-full rounded-xl bg-neutral-600 text-white dark:bg-gray-800 dark:text-gray-100">
<!-- Not active -->
<div v-if="!scheduleInput.active" class="m-auto flex h-full w-8/12 flex-row items-center justify-center gap-2 text-wrap text-center text-xs leading-4 tracking-wider">
<div class="flex flex-row items-center justify-center gap-2">
<icon-info-circle class="min-w-4" aria-hidden="true"/>
<p>{{ t('text.scheduleSettings.notActive') }}</p>
</div>
<!-- Snack-ish Bar - The dark info bubble at the bottom of this form -->
<!-- First time no calendars -->
<snackish-bar :show-icon=true v-if="!calendarStore.hasConnectedCalendars">
<div class="flex flex-col gap-2">
<p>{{ t('text.scheduleSettings.noCalendars') }}</p>
<u><a href="/settings/calendar">{{ t('text.scheduleSettings.clickHereToConnect') }}</a></u>
</div>
<!-- Form is dirty, please clean it. -->
<div v-else-if="isFormDirty" class="m-auto flex h-full w-8/12 flex-col items-center justify-center gap-2 text-wrap text-center text-xs leading-4 tracking-wider">
<div class="flex flex-row items-center justify-center gap-2">
<icon-info-circle class="w-4" aria-hidden="true"/>
<p>{{ t('text.scheduleSettings.formDirty') }}</p>
</div>
<div class="flex gap-4">
</snackish-bar>
<!-- No schedule? Create one please! -->
<snackish-bar v-else-if="!existing">
<div class="flex flex-col items-center justify-center gap-2">
<p>{{ t('text.scheduleSettings.create') }}</p>
<primary-button
:label="t('label.save')"
@click="saveSchedule(!existing)"
:waiting="savingInProgress"
:disabled="!scheduleInput.active"
class="w-1/2"
/>
</div>
</snackish-bar>
<!-- Schedule is not active -->
<snackish-bar :show-icon=true v-else-if="!scheduleInput.active">
<p>{{ t('text.scheduleSettings.notActive') }}</p>
</snackish-bar>
<!-- Form is dirty, please clean it -->
<snackish-bar v-else-if="isFormDirty">
<div class="flex flex-col gap-2">
<p>{{ t('text.scheduleSettings.formDirty') }}</p>
<div class="flex gap-4">
<secondary-button
:label="t('label.revert')"
@click="resetSchedule()"
Expand All @@ -314,8 +328,8 @@
class="w-1/2"
/>
</div>
</div>
</div>
</div>
</snackish-bar>
<div v-else>
<div class="my-8 flex justify-center gap-4">
<primary-button
Expand Down Expand Up @@ -354,9 +368,12 @@ import { IconChevronDown, IconInfoCircle } from '@tabler/icons-vue';
import AlertBox from '@/elements/AlertBox';
import SwitchToggle from '@/elements/SwitchToggle';
import ToolTip from '@/elements/ToolTip.vue';
import { useCalendarStore } from '@/stores/calendar-store';
import SnackishBar from '@/elements/SnackishBar.vue';
// component constants
const user = useUserStore();
const calendarStore = useCalendarStore();
const { t } = useI18n();
const dj = inject('dayjs');
const call = inject('call');
Expand Down Expand Up @@ -389,9 +406,6 @@ const activeStep1 = computed(() => state.value === firstStep);
const activeStep2 = computed(() => state.value === scheduleCreationState.settings);
const activeStep3 = computed(() => state.value === scheduleCreationState.details);
const visitedStep1 = ref(false);
const nextStep = () => {
state.value += 1;
};
// calculate calendar titles
const calendarTitles = computed(() => {
Expand All @@ -404,8 +418,8 @@ const calendarTitles = computed(() => {
// default schedule object (for start and reset) and schedule form data
const defaultSchedule = {
active: true,
name: '',
active: calendarStore.hasConnectedCalendars,
name: `${user.data.name}'s Availability`,
calendar_id: props.calendars[0]?.id,
location_type: locationTypes.inPerson,
location_url: '',
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/elements/AlertBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<span class="ml-1 block sm:inline">
<slot></slot>
</span>
<span class="ml-auto" @click="emit('close')">
<span v-if="canClose" class="ml-auto" @click="emit('close')">
<icon-x class="size-6 cursor-pointer fill-transparent stroke-white stroke-1" />
</span>
</div>
Expand All @@ -35,6 +35,10 @@ import { alertSchemes } from '@/definitions';
defineProps({
title: String,
canClose: {
type: Boolean,
default: true,
},
scheme: {
type: Number,
default: alertSchemes.error,
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/elements/SnackishBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<div class="h-28 w-full rounded-xl bg-neutral-600 text-white dark:bg-gray-800 dark:text-gray-100">
<div class="m-auto flex h-full w-8/12 flex-row items-center justify-center gap-2 text-wrap text-center text-xs leading-4 tracking-wider">
<div class="flex flex-row items-center justify-center gap-2">
<icon-info-circle v-if="showIcon" class="min-w-4" aria-hidden="true"/>
<slot></slot>
</div>
</div>
</div>
</template>
<script setup>
import { IconInfoCircle } from '@tabler/icons-vue';
defineProps({
showIcon: {
type: Boolean,
default: false,
},
});
</script>
3 changes: 3 additions & 0 deletions frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,9 @@
},
"nameIsInvitingYou": "{name} is inviting you",
"scheduleSettings": {
"create": "Select a calendar under Scheduling Details and click save to get started!",
"noCalendars": "Scheduling requires at least one connected calendar.",
"clickHereToConnect": "Click here to connect a calendar!",
"notActive": "Schedule is not active, turn on the toggle to edit or share.",
"formDirty": "You have unsaved changes."
},
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/stores/calendar-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const useCalendarStore = defineStore('calendars', () => {
const unconnectedCalendars = computed(() => calendars.value.filter((cal) => !cal.connected));
const connectedCalendars = computed(() => calendars.value.filter((cal) => cal.connected));

const hasConnectedCalendars = computed(() => connectedCalendars.value.length > 0);

/**
* Get all calendars for current user
* @param {function} call preconfigured API fetch function
Expand All @@ -37,6 +39,6 @@ export const useCalendarStore = defineStore('calendars', () => {
};

return {
isLoaded, calendars, unconnectedCalendars, connectedCalendars, fetch, $reset,
isLoaded, hasConnectedCalendars, calendars, unconnectedCalendars, connectedCalendars, fetch, $reset,
};
});

0 comments on commit 270e742

Please sign in to comment.