Michael Genson 80968b02bb
feat: Remove Explore URLs and make the normal URLs public (#2632)
* add groupSlug to most routes

* fixed more routing issues

* fixed jank and incorrect routes

* remove public explore links

* remove unused groupSlug and explore routes

* nuked explore pages

* fixed public toolstore bug

* fixed various routes missing group slug

* restored public app header menu

* fix janky login redirect

* 404 recipe API call returns to login

* removed unused explore layout

* force redirect when using the wrong group slug

* fixed dead admin links

* removed unused middleware from earlier attempt

* 🧹

* improve cookbooks sidebar
fixed sidebar link not working
fixed sidebar link target
hide cookbooks header when there are none

* added group slug to user

* fix $auth typehints

* vastly simplified groupSlug logic

* allow logged-in users to view other groups

* fixed some edgecases that bypassed isOwnGroup

* fixed static home ref

* 🧹

* fixed redirect logic

* lint warning

* removed group slug from group and user pages
refactored all components to use route groupSlug or user group slug
moved some group pages to recipe pages

* fixed some bad types

* 🧹

* moved groupSlug routes under /g/groupSlug

* move /recipe/ to /r/

* fix backend url generation and metadata injection

* moved shopping lists to root/other route fixes

* changed shared from /recipes/ to /r/

* fixed 404 redirect not awaiting

* removed unused import

* fix doc links

* fix public recipe setting not affecting public API

* fixed backend tests

* fix nuxt-generate command

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2023-11-05 16:07:02 -09:00

249 lines
7.5 KiB
Vue

<template>
<v-navigation-drawer v-model="drawer" class="d-flex flex-column d-print-none" clipped app width="240px">
<!-- User Profile -->
<template v-if="loggedIn">
<v-list-item two-line :to="userProfileLink" exact>
<UserAvatar list :user-id="$auth.user.id" />
<v-list-item-content>
<v-list-item-title class="pr-2"> {{ $auth.user.fullName }}</v-list-item-title>
<v-list-item-subtitle>
<v-btn v-if="isOwnGroup" class="px-2 pa-0" text :to="userFavoritesLink" small>
<v-icon left small>
{{ $globals.icons.heart }}
</v-icon>
{{ $t("user.favorite-recipes") }}
</v-btn>
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-divider></v-divider>
</template>
<slot></slot>
<!-- Primary Links -->
<template v-if="topLink">
<v-list nav dense>
<template v-for="nav in topLink">
<div v-if="!nav.restricted || isOwnGroup" :key="nav.title">
<!-- Multi Items -->
<v-list-group
v-if="nav.children"
:key="nav.title + 'multi-item'"
v-model="dropDowns[nav.title]"
color="primary"
:prepend-icon="nav.icon"
>
<template #activator>
<v-list-item-title>{{ nav.title }}</v-list-item-title>
</template>
<v-list-item v-for="child in nav.children" :key="child.title" exact :to="child.to">
<v-list-item-icon>
<v-icon>{{ child.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ child.title }}</v-list-item-title>
</v-list-item>
<v-divider class="mb-4"></v-divider>
</v-list-group>
<!-- Single Item -->
<v-list-item-group
v-else
:key="nav.title + 'single-item'"
v-model="secondarySelected"
color="primary"
>
<v-list-item exact link :to="nav.to">
<v-list-item-icon>
<v-icon>{{ nav.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ nav.title }}</v-list-item-title>
</v-list-item>
</v-list-item-group>
</div>
</template>
</v-list>
</template>
<!-- Secondary Links -->
<template v-if="secondaryLinks">
<router-link v-if="secondaryHeader && secondaryHeaderLink" :to="secondaryHeaderLink" style="text-decoration: none;">
<v-subheader :to="secondaryHeaderLink" class="pb-0">
{{ secondaryHeader }}
</v-subheader>
</router-link>
<div v-else-if="secondaryHeader">
<v-subheader :to="secondaryHeaderLink" class="pb-0">
{{ secondaryHeader }}
</v-subheader>
</div>
<v-divider v-if="secondaryHeader"></v-divider>
<v-list nav dense exact>
<template v-for="nav in secondaryLinks">
<div v-if="!nav.restricted || isOwnGroup" :key="nav.title">
<!-- Multi Items -->
<v-list-group
v-if="nav.children"
:key="nav.title + 'multi-item'"
v-model="dropDowns[nav.title]"
color="primary"
:prepend-icon="nav.icon"
>
<template #activator>
<v-list-item-title>{{ nav.title }}</v-list-item-title>
</template>
<v-list-item v-for="child in nav.children" :key="child.title" exact :to="child.to">
<v-list-item-icon>
<v-icon>{{ child.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ child.title }}</v-list-item-title>
</v-list-item>
<v-divider class="mb-4"></v-divider>
</v-list-group>
<!-- Single Item -->
<v-list-item-group v-else :key="nav.title + 'single-item'" v-model="secondarySelected" color="primary">
<v-list-item exact link :to="nav.to">
<v-list-item-icon>
<v-icon>{{ nav.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ nav.title }}</v-list-item-title>
</v-list-item>
</v-list-item-group>
</div>
</template>
</v-list>
</template>
<!-- Bottom Navigation Links -->
<template v-if="bottomLinks" #append>
<v-list nav dense>
<v-list-item-group v-model="bottomSelected" color="primary">
<template v-for="nav in bottomLinks">
<div v-if="!nav.restricted || isOwnGroup" :key="nav.title">
<v-list-item
:key="nav.title"
exact
link
:to="nav.to || null"
:href="nav.href || null"
:target="nav.href ? '_blank' : null"
>
<v-list-item-icon>
<v-icon>{{ nav.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ nav.title }}</v-list-item-title>
</v-list-item>
</div>
</template>
</v-list-item-group>
<slot name="bottom"></slot>
</v-list>
</template>
</v-navigation-drawer>
</template>
<script lang="ts">
import { computed, defineComponent, reactive, toRefs, useContext } from "@nuxtjs/composition-api";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import { SidebarLinks } from "~/types/application-types";
import UserAvatar from "~/components/Domain/User/UserAvatar.vue";
export default defineComponent({
components: {
UserAvatar,
},
props: {
value: {
type: Boolean,
default: null,
},
user: {
type: Object,
default: null,
},
topLink: {
type: Array as () => SidebarLinks,
required: true,
},
secondaryLinks: {
type: Array as () => SidebarLinks,
required: false,
default: null,
},
bottomLinks: {
type: Array as () => SidebarLinks,
required: false,
default: null,
},
secondaryHeader: {
type: String,
default: null,
},
secondaryHeaderLink: {
type: String,
default: null,
},
},
setup(props, context) {
// V-Model Support
const drawer = computed({
get: () => {
return props.value;
},
set: (val) => {
if (window.innerWidth < 760 && state.hasOpenedBefore === false) {
state.hasOpenedBefore = true;
val = false;
context.emit("input", val);
} else {
context.emit("input", val);
}
},
});
const { $auth } = useContext();
const { loggedIn, isOwnGroup } = useLoggedInState();
const userFavoritesLink = computed(() => $auth.user ? `/user/${$auth.user.id}/favorites` : undefined);
const userProfileLink = computed(() => $auth.user ? "/user/profile" : undefined);
const state = reactive({
dropDowns: {},
topSelected: null as string[] | null,
secondarySelected: null as string[] | null,
bottomSelected: null as string[] | null,
hasOpenedBefore: false as boolean,
});
return {
...toRefs(state),
userFavoritesLink,
userProfileLink,
drawer,
loggedIn,
isOwnGroup,
};
},
});
</script>
<style>
@media print {
.no-print {
display: none;
}
}
.favorites-link {
text-decoration: none;
}
.favorites-link:hover {
text-decoration: underline;
}
</style>