Skip to content

Commit

Permalink
Deploy to stage (#157)
Browse files Browse the repository at this point in the history
* Swap siteNotification to use a pinia store, and make the z-index bigger. (Fixes #151) (#152)

* Bugs/145 ga disclaimer and visual improvements (#153)

* 📜 improving copy

* 💚 improve spacing in home view

* 💚 improve footer bottom position without scroll bars

* 🔨 handle empty schedules list

* Bugs/154 calendar sync (#155)

* 🔨 only json load google token if it exists

* 🔨 fix sync button styles

* Improving copy (#156)

---------

Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com>
  • Loading branch information
devmount and MelissaAutumn authored Oct 18, 2023
1 parent a751671 commit e7fb24b
Show file tree
Hide file tree
Showing 15 changed files with 521 additions and 461 deletions.
664 changes: 334 additions & 330 deletions backend/src/appointment/controller/calendar.py

Large diffs are not rendered by default.

33 changes: 18 additions & 15 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
<!-- authenticated subscriber content -->
<template v-if="isAuthenticated">
<site-notification
v-if="siteNotificationStore.display"
v-if="siteNotificationStore.isVisible"
:title="siteNotificationStore.title"
:action-url="siteNotificationStore.actionUrl"
>
{{ siteNotificationStore.message }}
</site-notification>
<nav-bar :nav-items="navItems" />
<main class="mx-4 pt-24 lg:mx-8 min-h-full">
<main class="mx-4 pt-24 lg:mx-8 min-h-full pb-24">
<router-view
:calendars="calendars"
:appointments="appointments"
Expand All @@ -20,7 +20,7 @@
<!-- for home page and booking page -->
<template v-else-if="routeIsPublic">
<title-bar />
<main class="mx-4 pt-24 lg:mx-8 min-h-full">
<main class="mx-4 pt-24 lg:mx-8 min-h-full pb-24">
<router-view />
</main>
<footer-bar />
Expand All @@ -40,7 +40,7 @@ import { useRoute } from "vue-router";
import NavBar from "@/components/NavBar";
import TitleBar from "@/components/TitleBar";
import SiteNotification from "@/elements/SiteNotification";
import { siteNotificationStore } from "@/stores/alert-store";
import { useSiteNotificationStore } from "@/stores/alert-store";
// stores
import { useUserStore } from '@/stores/user-store';
Expand All @@ -51,6 +51,7 @@ const currentUser = useUserStore(); // data: { username, email, name, level, tim
const apiUrl = inject("apiUrl");
const dj = inject("dayjs");
const route = useRoute();
const siteNotificationStore = useSiteNotificationStore();
// handle auth and fetch
const auth = useAuth0();
Expand All @@ -74,24 +75,23 @@ const call = createFetch({
async onFetchError({ data, response, error }) {
// Catch any google refresh error that may occur
if (
data?.detail?.error === "google_refresh_error" &&
siteNotificationStore.value.id !== "google_refresh_error"
data?.detail?.error === 'google_refresh_error' &&
!siteNotificationStore.isSameNotification('google_refresh_error')
) {
// Ensure other async calls don't reach here
siteNotificationStore.value.id = data.detail.error;
siteNotificationStore.lock(data.detail.error);
// Retrieve the google auth url, and if that fails send them to calendar settings!
const { data: urlData, error: urlError } = await call('google/auth').get();
const url = urlError.value ? '/settings/calendar' : urlData.value.slice(1, -1);
// Update our site notification store with the error details
siteNotificationStore.value = {
id: data.detail.error,
display: true,
actionUrl: url,
title: "Action needed!",
message: data.detail?.message || "Please re-connect with Google",
};
siteNotificationStore.show(
data.detail.error,
'Action needed!',
data.detail?.message || 'Please re-connect with Google',
url,
);
}
// Pass the error along
Expand Down Expand Up @@ -124,10 +124,13 @@ const routeIsPublic = computed(
// check login state of current user first
const checkLogin = async () => {
console.log(auth.user.value, currentUser.data);
if (auth.isAuthenticated.value) {
if (currentUser.exists() && currentUser.data.email === auth.user.value.email) {
// avoid calling the backend unnecessarily
// TODO: There seems to be an issue with different session durations.
// Somehow the frontend `auth` is still valid, but the backend
// 403s calls to the API. Might be Auth0. Check again,
// as soon as Auth0 got replaced by Mozilla accounts.
return;
}
// call backend to create user if they do not exist in database
Expand Down
21 changes: 10 additions & 11 deletions frontend/src/components/CalendarManagement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
<div class="flex max-w-2xl">
<div class="text-xl">{{ title }}</div>
<div class="inline-flex mx-auto mr-0" v-if="type === calendarManagementType.connect">
<button
class="bg-transparent disabled:scale-100 disabled:shadow-none disabled:opacity-50"
<secondary-button
class="text-sm !text-teal-500 disabled:scale-100 disabled:shadow-none disabled:opacity-50"
:disabled="loading"
@click="emit('sync')"
>
<span class="inline-block mr-2">
{{ t('label.syncCalendars') }}
</span>
<icon-refresh class="inline-block h-5 w-5 stroke-2 stroke-white fill-transparent"></icon-refresh>
</button>
<icon-refresh class="inline-block h-4 w-4 stroke-2" :class="{ 'animate-spin': loading }" />
</secondary-button>
</div>
</div>
<div v-if="filteredCalendars?.length" class="pl-6 flex flex-col gap-2 max-w-2xl">
<div v-for="cal in filteredCalendars" :key="cal.id" class="flex gap-2 items-center">
<div class="flex-center w-6 h-6 rounded-lg" :style="{ backgroundColor: cal.color ?? '#38bdf8' }">
<icon-calendar class="w-4 h-4 fill-transparent stroke-2 stroke-white"/>
<icon-calendar class="w-4 h-4 stroke-2 stroke-white"/>
</div>
<span class="calendar-title">
{{ cal.title }}
Expand All @@ -31,7 +31,7 @@
bg-teal-500 text-white disabled:scale-100 disabled:shadow-none disabled:opacity-50
"
>
<icon-arrow-right class="h-3 w-3 stroke-2 stroke-white fill-transparent"/>
<icon-arrow-right class="h-3 w-3 stroke-3"/>
{{ t('label.connectCalendar') }}
</button>
<button
Expand All @@ -43,7 +43,7 @@
bg-teal-500 text-white disabled:scale-100 disabled:shadow-none disabled:opacity-50
"
>
<icon-pencil class="h-3 w-3 stroke-2 stroke-white fill-transparent"/>
<icon-pencil class="h-3 w-3 stroke-3"/>
{{ t('label.editCalendar') }}
</button>
<button
Expand All @@ -52,19 +52,18 @@
:disabled="loading"
@click="emit('remove', cal.id)"
>
<icon-x class="h-5 w-5 stroke-2 stroke-red-500 fill-transparent"/>
<icon-x class="h-5 w-5 stroke-2 stroke-red-500"/>
</button>
</div>
</div>
</template>
<script setup>
import {
IconArrowRight, IconCalendar, IconPencil, IconX, IconRefresh,
} from '@tabler/icons-vue';
import { calendarManagementType } from '@/definitions';
import { computed } from 'vue';
import { IconArrowRight, IconCalendar, IconPencil, IconX, IconRefresh } from '@tabler/icons-vue';
import { useI18n } from 'vue-i18n';
import SecondaryButton from '@/elements/SecondaryButton';
const { t } = useI18n({ useScope: 'global' });
const emit = defineEmits(['modify', 'remove', 'sync']);
Expand Down
30 changes: 14 additions & 16 deletions frontend/src/components/FooterBar.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
<template>
<footer
class="
relative z-50 h-16 w-full px-4 shadow-lg border-t flex justify-between mt-2
bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600
"
>
<footer class="
relative z-50 h-16 w-full -mt-16 px-4 shadow-lg border-t flex justify-between
bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600
">
<ul class="flex justify-center gap-8 w-full">
<li
class="flex text-base border-t-4 border-t-transparent transition-all ease-in-out
text-gray-600 dark:text-gray-300 hover:border-t-gray-200 dark:hover:border-t-gray-400"
>
<router-link class="flex justify-center min-w-[120px] items-center" :to="{ name: 'privacy' }">
<li class="
flex text-base border-t-4 border-t-transparent transition-all ease-in-out
text-gray-600 dark:text-gray-300 hover:border-t-gray-200 dark:hover:border-t-gray-400
">
<router-link class="flex-center min-w-[120px]" :to="{ name: 'privacy' }" target="_blank">
Privacy
</router-link>
</li>
<li
class="flex text-base border-t-4 border-t-transparent transition-all ease-in-out
text-gray-600 dark:text-gray-300 hover:border-t-gray-200 dark:hover:border-t-gray-400"
>
<router-link class="flex justify-center min-w-[120px] items-center" :to="{ name: 'terms' }">
<li class="
flex text-base border-t-4 border-t-transparent transition-all ease-in-out
text-gray-600 dark:text-gray-300 hover:border-t-gray-200 dark:hover:border-t-gray-400
">
<router-link class="flex-center min-w-[120px]" :to="{ name: 'terms' }" target="_blank">
Legal
</router-link>
</li>
Expand Down
41 changes: 22 additions & 19 deletions frontend/src/components/SettingsCalendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,25 @@
<alert-box title="Calendar Connect Error" v-if="calendarConnectError">{{calendarConnectError}}</alert-box>

<!-- list of possible calendars to connect -->
<CalendarManagement
:title="t('heading.calendarsUnconnected')" :type="calendarManagementType.connect" :calendars="calendars"
:loading="loading" @sync="syncCalendars" @modify="connectCalendar"
></CalendarManagement>
<calendar-management
:title="t('heading.calendarsUnconnected')"
:type="calendarManagementType.connect"
:calendars="calendars"
:loading="loading"
@sync="syncCalendars"
@modify="connectCalendar"
/>

<!-- list of calendar connections -->
<CalendarManagement
:title="t('heading.calendarsConnected')" :type="calendarManagementType.edit" :calendars="calendars"
:loading="loading" @remove="deleteCalendar" @modify="editCalendar">
</CalendarManagement>
<calendar-management
:title="t('heading.calendarsConnected')"
:type="calendarManagementType.edit"
:calendars="calendars"
:loading="loading"
@remove="deleteCalendar"
@modify="editCalendar"
/>

<div class="flex gap-4">
<secondary-button
:label="t('label.addCalendar', { provider: t('label.google') })"
Expand Down Expand Up @@ -180,23 +189,17 @@
</template>

<script setup>
import {
ref, reactive, inject, onMounted, computed,
} from 'vue';
import { calendarManagementType } from '@/definitions';
import { IconArrowRight } from '@tabler/icons-vue';
import { ref, reactive, inject, onMounted, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import SecondaryButton from '@/elements/SecondaryButton';
import PrimaryButton from '@/elements/PrimaryButton';
// icons
import {
IconArrowRight,
} from '@tabler/icons-vue';
import { useRoute, useRouter } from 'vue-router';
import AlertBox from '@/elements/AlertBox';
import CalendarManagement from '@/components/CalendarManagement.vue';
import { calendarManagementType } from '@/definitions';
import GoogleSignInBtn from '@/assets/img/google/1x/btn_google_signin_light_normal_web.png';
import GoogleSignInBtn2x from '@/assets/img/google/2x/btn_google_signin_light_normal_web@2x.png';
import PrimaryButton from '@/elements/PrimaryButton';
import SecondaryButton from '@/elements/SecondaryButton';
// component constants
const { t } = useI18n({ useScope: 'global' });
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/elements/PrimaryButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
v-if="copy && copied"
class="h-6 w-6 stroke-2 stroke-current fill-transparent"
/>
{{ label }}
<template v-if="label">
{{ label }}
</template>
<template v-else>
<slot></slot>
</template>
</button>
</template>

Expand Down
7 changes: 6 additions & 1 deletion frontend/src/elements/SecondaryButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
v-if="copy && copied"
class="h-6 w-6 stroke-2 stroke-current fill-transparent"
/>
{{ label }}
<template v-if="label">
{{ label }}
</template>
<template v-else>
<slot></slot>
</template>
</button>
</template>

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/elements/SiteNotification.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<a :href="actionUrl">
<div
class="
fixed left-1/2 -translate-x-1/2 my-2.5 p-2 leading-none lg:rounded-full
z-[999] fixed left-1/2 -translate-x-1/2 my-2.5 p-2 leading-none lg:rounded-full
flex lg:inline-flex items-center shadow-md dark:shadow-lg
bg-rose-600 dark:bg-rose-900 text-white shadow-black/30"
role="alert"
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
"info": {
"bookedPleaseCheckEmail": "Das Event wurde bereits gebucht, bitte überprüfe deinen Posteingang um das Event in deinen Kalender einzutragen.",
"bookingLinkHasAlreadyBeenUsed": "Dieser Buchungslink wurde offenbar schon genutzt.",
"bookingSuccessful": "Die Buchung war erfolgreich!",
"bookingSuccessfullyRequested": "Die Buchung wurde erfolgreich angefragt!",
"bookingSuccessful": "Erfolgreich gebucht!",
"bookingSuccessfullyRequested": "Buchung erfolgreich angefragt!",
"invitationWasSent": "Eine Einladung wurde an deine E-Mail-Adresse gesandt.",
"noPendingAppointmentsInList": "Hier gibt es aktuell keine ausstehenden Termine."
},
Expand Down Expand Up @@ -73,7 +73,7 @@
"attendees": "Teilnehmer",
"availabilityDay": "Verfügbarer Tag",
"availableDays": "Verfügbare Tage",
"bookEvent": "Event buchen",
"bookEvent": "Buchen",
"booked": "Gebucht",
"bookingLink": "Buchungslink",
"bookingSettings": "Buchungseinstellungen",
Expand Down Expand Up @@ -191,9 +191,9 @@
"accountDataNotice": "Lade all Deine Daten von Thunderbird Appointment herunter.",
"accountDeletionFinalWarning": "Warnung: Die Löschung des Benutzerkontos ist dauerhaft! Du wirst all Deine Daten von Thunderbird Appointment verlieren. Deine verbundenen Kalender bleiben unverändert.",
"accountDeletionWarning": "Achtung: Die Löschung des Benutzerkontos ist dauerhaft! Du wirst all Deine Daten von Thunderbird Appointment verlieren. Deine verbundenen Kalender bleiben unverändert. Bestätige die Löschung mit deinen Login-Daten.",
"chooseDayTime": "Wähle eine Zeit an einem Tag aus:",
"chooseDateAndTime": "Wähle einen Tag und eine Zeit für ein Treffen:",
"defineDaysAndTimeSlots": "Definiere Tage und Zeitfenster, damit Teilnehmer die Zeit des Events auswählen können…",
"disclaimerGABooking": "Jede GV-Buchung muss vom Eigentümer der GV bestätigt werden.",
"disclaimerGABooking": "Bitte beachten: Der Eigentümer muss die Buchung bestätigen.",
"googlePermissionCalendarName": "Anzeigen und Herunterladen aller Kalender des Google-Kontos",
"googlePermissionCalendarReason": "Thunderbird Appointment benötigt diese Berechtigung, um eine Liste von Kalendern anzuzeigen, die verbunden werden können.",
"googlePermissionDisclaimer": "Thunderbird Appointment benötigt die folgenden Berechtigungen von Google, um korrekt zu funktionieren",
Expand All @@ -204,7 +204,7 @@
"homepageGreetingIntro": "Herzlich Willkommen zu Thunderbird Appointment!",
"homepageGreetingUnauthenticated": "Wir befinden uns derzeit in der Testphase und hoffen, dass wir sie die Anwendung bald produktiv nutzen können. Um sich an den Tests zu beteiligen, bitte anmelden.",
"invitationSentToAddress": "Eine Einladung für dieses Event wurde dir an {address} geschickt",
"nameIsInvitingYou": "{name} bittet dich, eine Zeit auszuwählen",
"nameIsInvitingYou": "{name} lädt dich ein",
"recipientsCanScheduleBetween": "Empfänger können einen Termin zwischen {earliest} und {farthest} ab dem aktuellen Zeitpunkt wählen. ",
"refreshLinkNotice": "This will refresh your short link. Please note that any old short links will no longer work.",
"titleIsReadyForBookings": "{title} ist für Buchungen bereit",
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
"info": {
"bookedPleaseCheckEmail": "This event has been booked, please check your email to add it to you calendar.",
"bookingLinkHasAlreadyBeenUsed": "It seems that this booking link has already been used.",
"bookingSuccessful": "The booking was successful!",
"bookingSuccessfullyRequested": "The booking was successfully requested!",
"bookingSuccessful": "Booking successful!",
"bookingSuccessfullyRequested": "Booking successfully requested!",
"invitationWasSent": "An invitation was sent to your email.",
"noPendingAppointmentsInList": "There are currently no pending appointments in this list."
},
Expand Down Expand Up @@ -73,7 +73,7 @@
"attendees": "Attendees",
"availabilityDay": "Availability Day",
"availableDays": "Available Days",
"bookEvent": "Book event",
"bookEvent": "Book",
"booked": "Booked",
"bookingLink": "Booking Link",
"bookingSettings": "Booking Settings",
Expand Down Expand Up @@ -191,9 +191,9 @@
"accountDataNotice": "Download all of your data from Thunderbird Appointment.",
"accountDeletionFinalWarning": "Last chance! Deleting your account is permanent, and you all of your saved data on Thunderbird Appointment will be removed. Your calendars will remain unchanged.",
"accountDeletionWarning": "Warning, deleting your account is permanent! You will lose all saved data on Thunderbird Appointment. Your calendars will remain unchanged. There is a second confirmation after you login.",
"chooseDayTime": "Choose the day and time:",
"chooseDateAndTime": "Choose a date and time to meet:",
"defineDaysAndTimeSlots": "Define days and time slots to allow users to select the event time…",
"disclaimerGABooking": "Disclaimer: any GA booking done has to be confirmed by the owner of the GA",
"disclaimerGABooking": "Please note: The calendar owner will confirm your booking.",
"googlePermissionCalendarName": "See and download any calendar you can access using your Google Calendar",
"googlePermissionCalendarReason": "Thunderbird Appointment requires this permission in order to display a list of calendars that you can connect with.",
"googlePermissionDisclaimer": "Thunderbird Appointment requires the following permissions from Google Calendar in order to function correctly",
Expand All @@ -204,7 +204,7 @@
"homepageGreetingIntro": "Welcome to Thunderbird Appointment!",
"homepageGreetingUnauthenticated": "We're currently testing and hope to be available to use soon. If you're taking part in the testing, please log in.",
"invitationSentToAddress": "An invitation for this event has been emailed to you at {address}",
"nameIsInvitingYou": "{name} is inviting you to choose a date",
"nameIsInvitingYou": "{name} is inviting you",
"recipientsCanScheduleBetween": "Recipients can schedule an appointment between {earliest} and {farthest} ahead of time. ",
"refreshLinkNotice": "This will refresh your short link. Please note that any old short links will no longer work.",
"titleIsReadyForBookings": "{title} is ready for bookings",
Expand Down
Loading

0 comments on commit e7fb24b

Please sign in to comment.