Skip to content

Commit

Permalink
Features/144 Profile dropdown ga link (#197)
Browse files Browse the repository at this point in the history
* ➕ component element for nav bar items

* ➕ provide unified login and logout methods

* 🔨 fix missing import

* ➕ add profile dropdown

* ➕ add ga link to schedule creation area

* 🔨 fix vue warning for extraneous non-props attributes

* 💚 add active nav item color gradients

* 🔨 fix share my link buttons to copy link into clipboard
  • Loading branch information
devmount authored and MelissaAutumn committed Dec 5, 2023
1 parent b44c369 commit 3fe4dfa
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 128 deletions.
15 changes: 15 additions & 0 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,21 @@ const checkLogin = async () => {
}
};
// provide methods for logging the user in and out for corresponding buttons in child components
const login = () => {
auth.loginWithRedirect();
};
const logout = async () => {
currentUser.reset();
await auth.logout({
logoutParams: {
returnTo: window.location.origin,
},
});
};
provide("login", login);
provide("logout", logout);
// query db for all calendar data
const getDbCalendars = async (onlyConnected = true) => {
const { data, error } = await call(`me/calendars?only_connected=${onlyConnected}`).get().json();
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,14 @@
@apply placeholder:text-gray-300 dark:placeholder:text-gray-500;
}
}

/* vue specific classes */
.v-enter-active,
.v-leave-active {
@apply transition-opacity;
}

.v-enter-from,
.v-leave-to {
@apply opacity-0;
}
79 changes: 57 additions & 22 deletions frontend/src/components/NavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,50 +25,85 @@
:placeholder="t('label.search')"
/>
</label> -->
<nav class="flex gap-4 items-stretch">
<ul class="flex justify-end gap-8">
<li
<nav v-if="user.exists()" class="flex gap-4 items-stretch">
<div class="flex justify-end gap-8">
<nav-bar-item
v-for="item in navItems"
:key="item"
class="flex text-base border-b-4 border-b-transparent transition-all ease-in-out"
:class="{
'font-semibold border-b-teal-500 text-teal-500': route.name == item,
'text-gray-600 dark:text-gray-300 hover:border-b-gray-200 dark:hover:border-b-gray-400':
route.name != item,
}"
>
<router-link class="flex justify-center min-w-[120px] items-center" :to="{ name: item }">
{{ t("label." + item) }}
</router-link>
</li>
</ul>
<router-link
v-if="user.exists()"
:to="{ name: 'profile' }"
class="w-12 h-12 mr-4 self-center flex-center rounded-full bg-teal-500 text-lg font-normal text-white"
>
{{ initials(user.data.name) }}
</router-link>
:active="route.name == item"
:label="t(`label.${item}`)"
:link-name="item"
/>
</div>
<drop-down class="self-center">
<template #trigger>
<div
class="w-12 h-12 mr-4 flex-center rounded-full bg-teal-500 text-lg font-normal text-white"
>
{{ initials(user.data.name) }}
</div>
</template>
<template #default>
<div class="flex flex-col gap-2 rounded-md w-48 p-4 bg-white dark:bg-gray-700 shadow-md">
<router-link :to="{ name: 'profile' }" class="p-2">
{{ t('label.userProfile') }}
</router-link>
<text-button
v-show="signedUserUrl"
:label="t('label.shareMyLink')"
:copy="signedUserUrl"
class="border-none flex-row-reverse justify-between !text-inherit !text-base !font-normal hover:bg-inherit hover:shadow-none"
/>
<hr class="border-teal-500" />
<div @click="logout" class="cursor-pointer p-2">
{{ t('label.logOut') }}
</div>
</div>
</template>
</drop-down>
</nav>
</header>
</template>

<script setup>
import { ref, inject, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { initials } from '@/utils';
import { useUserStore } from '@/stores/user-store';
import NavBarItem from "@/elements/NavBarItem";
import DropDown from "@/elements/DropDown";
import TextButton from "@/elements/TextButton";
// icons
import { IconExternalLink } from '@tabler/icons-vue';
// import { IconSearch } from '@tabler/icons-vue';
// component constants
const user = useUserStore();
const route = useRoute();
const { t } = useI18n();
const logout = inject('logout');
const call = inject('call');
// component properties
defineProps({
navItems: Array, // list of route names that are also lang keys (format: label.<key>), used as nav items
});
const signedUserUrl = ref('');
const getSignedUserUrl = async () => {
// Retrieve the user short url
const { data, error } = await call('me/signature').get().json();
if (error.value) {
return;
}
signedUserUrl.value = data.value.url;
};
onMounted(async () => {
await getSignedUserUrl();
});
</script>
23 changes: 19 additions & 4 deletions frontend/src/components/ScheduleCreation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@
</div>
</div>
<!-- action buttons -->
<div class="flex justify-center gap-4 mt-auto">
<primary-button
v-if="publicLink && existing"
:label="t('label.shareMyLink')"
:copy="publicLink"
/>
</div>
<div class="flex gap-4 mt-auto">
<secondary-button
:label="t('label.cancel')"
Expand Down Expand Up @@ -294,7 +301,7 @@
:open="savedConfirmation.show"
:is-schedule="true"
:title="savedConfirmation.title"
:public-link="savedConfirmation.publicLink"
:public-link="publicLink"
@close="closeCreatedModal"
/>
</template>
Expand All @@ -310,7 +317,7 @@ import SecondaryButton from "@/elements/SecondaryButton";
import TabBar from "@/components/TabBar";
// icons
import { IconChevronDown } from "@tabler/icons-vue";
import { IconChevronDown, IconExternalLink } from "@tabler/icons-vue";
import AlertBox from "@/elements/AlertBox";
import SwitchToggle from "@/elements/SwitchToggle";
Expand Down Expand Up @@ -457,7 +464,6 @@ const farthest = computed(() => dj.duration(scheduleInput.value.farthest_booking
const savedConfirmation = reactive({
show: false,
title: "",
publicLink: "",
});
const closeCreatedModal = () => {
savedConfirmation.show = false;
Expand All @@ -469,6 +475,15 @@ const resetSchedule = () => {
state.value = scheduleCreationState.details;
};
// hold public availability link of user
const publicLink = ref('');
onMounted(async () => {
const { data, error } = await call('me/signature').get().json();
if (!error.value) {
publicLink.value = data.value.url;
}
});
// handle actual schedule creation/update
const savingInProgress = ref(false);
const saveSchedule = async (withConfirmation = true) => {
Expand Down Expand Up @@ -514,7 +529,7 @@ const saveSchedule = async (withConfirmation = true) => {
// show confirmation
savedConfirmation.title = data.value.name;
savedConfirmation.publicLink = sig_data.value.url;
publicLink.value = sig_data.value.url;
savedConfirmation.show = true;
}
Expand Down
12 changes: 3 additions & 9 deletions frontend/src/components/SettingsAccount.vue
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,7 @@
</template>

<script setup>
import {
ref, inject, onMounted, computed,
} from 'vue';
import { ref, inject, onMounted, computed } from 'vue';
import { useAuth0 } from '@auth0/auth0-vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
Expand All @@ -173,6 +171,7 @@ const call = inject('call');
const refresh = inject('refresh');
const router = useRouter();
const user = useUserStore();
const logout = inject('logout');
const externalConnections = ref({});
const hasZoomAccountConnected = computed(() => (externalConnections.value?.zoom?.length ?? []) > 0);
Expand Down Expand Up @@ -351,12 +350,7 @@ const actuallyDeleteAccount = async () => {
}
if (auth0) {
user.reset();
await auth0.logout({
logoutParams: {
returnTo: window.location.origin,
},
});
await logout();
} else {
await router.push('/');
}
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/components/TitleBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@
</template>

<script setup>
import { useAuth0 } from '@auth0/auth0-vue';
import { inject } from 'vue';
import { useI18n } from 'vue-i18n';
import PrimaryButton from '@/elements/PrimaryButton';
const { t } = useI18n();
const auth0 = useAuth0();
const login = inject('login');
const login = () => {
auth0.loginWithRedirect();
};
</script>
21 changes: 21 additions & 0 deletions frontend/src/elements/DropDown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div class="relative" v-on-click-outside="close">
<div class="cursor-pointer select-none" @click="toggle">
<slot name="trigger"></slot>
</div>
<transition>
<div v-show="open" class="absolute right-0" @click="close">
<slot></slot>
</div>
</transition>
</div>
</template>

<script setup>
import { ref } from "vue";
import { vOnClickOutside } from '@vueuse/components';
const open = ref(false);
const toggle = () => { open.value = !open.value; };
const close = () => { open.value = false; };
</script>
26 changes: 26 additions & 0 deletions frontend/src/elements/NavBarItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<div
class="relative group flex font-medium text-base"
:class="{ 'text-gray-600 dark:text-gray-300': !active }"
>
<router-link class="flex justify-center min-w-[120px] items-center" :to="{ name: linkName }">
{{ label }}
</router-link>
<div
class="absolute rounded bottom-0 h-1 w-full bg-transparent transition-colors ease-in-out"
:class="{
'bg-gradient-to-r from-teal-500 to-sky-500': active,
'group-hover:bg-gray-200 group-hover:dark:bg-gray-400': !active,
}"
></div>
</div>
</template>

<script setup>
// component properties
defineProps({
active: Boolean, // indicator for currently active item
label: String, // item text label
linkName: String, // name of the route link target
});
</script>
2 changes: 1 addition & 1 deletion frontend/src/elements/TextButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
transition-all ease-in-out flex items-center justify-center gap-1
text-gray-500 dark:text-gray-400 border-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800
"
@click="copy ? copyToClipboard() : null"
@click.stop="copy ? copyToClipboard() : null"
>
<icon-copy
v-if="copy && !copied"
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@
"light": "Hell",
"location": "Ort",
"logIn": "Anmelden",
"logOut": "Abmelden",
"loginToContinue": "Anmelden um fortzufahren",
"logOut": "Abmelden",
"month": "Monat",
"myLink": "Mein Link",
"name": "Name",
Expand Down Expand Up @@ -167,6 +167,7 @@
"selectCalendar": "Kalender auswählen",
"sendInvitationToAnotherEmail": "Die Einladung an eine andere E-Mail-Adresse senden",
"settings": "Einstellungen",
"shareMyLink": "Meinen Link teilen",
"showSecondaryTimeZone": "Zeige sekundäre Zeitzone",
"signInWithGoogle": "Mit Google anmelden",
"slotLength": "Termindauer",
Expand All @@ -185,6 +186,7 @@
"today": "Heute",
"updateUsername": "Update username",
"username": "Benutzername",
"userProfile": "Benutzerprofil",
"videoLink": "Konferenz-Link",
"viewAll": "Alle ansehen",
"viewBooking": "Buchung anschauen",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
"selectCalendar": "Select calendar",
"sendInvitationToAnotherEmail": "Send to another email",
"settings": "Settings",
"shareMyLink": "Share my link",
"showSecondaryTimeZone": "Show secondary time zone",
"signInWithGoogle": "Sign in with Google",
"slotLength": "Duration",
Expand All @@ -185,6 +186,7 @@
"today": "Today",
"updateUsername": "Update username",
"username": "Username",
"userProfile": "User profile",
"videoLink": "Video Link",
"viewAll": "View all",
"viewBooking": "View booking",
Expand Down
Loading

0 comments on commit 3fe4dfa

Please sign in to comment.