From 581aa929bd5227585850f54f54780801b91057ed Mon Sep 17 00:00:00 2001 From: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com> Date: Wed, 3 Sep 2025 17:07:06 +0200 Subject: [PATCH] feat: consolidate settings gui (#6043) Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> --- frontend/components/Layout/DefaultLayout.vue | 39 --------------- .../Layout/LayoutParts/AppSidebar.vue | 47 +++++++++++-------- frontend/layouts/admin.vue | 10 ---- frontend/lib/icons/icons.ts | 3 +- tests/e2e/login.spec.ts | 12 +++-- 5 files changed, 37 insertions(+), 74 deletions(-) diff --git a/frontend/components/Layout/DefaultLayout.vue b/frontend/components/Layout/DefaultLayout.vue index c1a968465..20587e3bc 100644 --- a/frontend/components/Layout/DefaultLayout.vue +++ b/frontend/components/Layout/DefaultLayout.vue @@ -16,7 +16,6 @@ absolute :top-link="topLinks" :secondary-links="cookbookLinks || []" - :bottom-links="bottomLinks" > - - - - {{ $globals.icons.translate }} - - {{ $t("sidebar.language") }} - - - - - - {{ $vuetify.theme.current.dark ? $globals.icons.weatherSunny : $globals.icons.weatherNight }} - - - - {{ $vuetify.theme.current.dark ? $t("settings.theme.light-mode") : $t("settings.theme.dark-mode") }} - - - @@ -121,7 +101,6 @@ import { useAppInfo } from "~/composables/api"; import { useCookbookPreferences } from "~/composables/use-users/preferences"; import { useCookbookStore, usePublicCookbookStore } from "~/composables/store/use-cookbook-store"; import { useHouseholdStore, usePublicHouseholdStore } from "~/composables/store/use-household-store"; -import { useToggleDarkMode } from "~/composables/use-utils"; import type { ReadCookBook } from "~/lib/api/types/cookbook"; import type { HouseholdSummary } from "~/lib/api/types/household"; @@ -133,7 +112,6 @@ export default defineNuxtComponent({ const $auth = useMealieAuth(); const { isOwnGroup } = useLoggedInState(); - const isAdmin = computed(() => $auth.user.value?.admin); const route = useRoute(); const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || ""); @@ -191,8 +169,6 @@ export default defineNuxtComponent({ const appInfo = useAppInfo(); const showImageImport = computed(() => appInfo.value?.enableOpenaiImageServices); - const toggleDark = useToggleDarkMode(); - const languageDialog = ref(false); const sidebar = ref(false); @@ -286,19 +262,6 @@ export default defineNuxtComponent({ }, ]); - const bottomLinks = computed(() => - isAdmin.value - ? [ - { - icon: $globals.icons.cog, - title: i18n.t("general.settings"), - to: "/admin/site-settings", - restricted: true, - }, - ] - : [], - ); - const topLinks = computed(() => [ { icon: $globals.icons.silverwareForkKnife, @@ -367,11 +330,9 @@ export default defineNuxtComponent({ groupSlug, cookbookLinks, createLinks, - bottomLinks, topLinks, isOwnGroup, languageDialog, - toggleDark, sidebar, }; }, diff --git a/frontend/components/Layout/LayoutParts/AppSidebar.vue b/frontend/components/Layout/LayoutParts/AppSidebar.vue index 68a72873f..ef3fb3db6 100644 --- a/frontend/components/Layout/LayoutParts/AppSidebar.vue +++ b/frontend/components/Layout/LayoutParts/AppSidebar.vue @@ -1,5 +1,6 @@ + @@ -82,20 +83,22 @@ - - - - - - - {{ nav.icon }} - - {{ nav.title }} - - - - + + + + + + + + + + + + + + + + @@ -106,6 +109,7 @@ import { useWindowSize } from "@vueuse/core"; import { useLoggedInState } from "~/composables/use-logged-in-state"; import type { SidebarLinks } from "~/types/application-types"; import UserAvatar from "~/components/Domain/User/UserAvatar.vue"; +import { useToggleDarkMode } from "~/composables/use-utils"; export default defineNuxtComponent({ components: { @@ -130,26 +134,26 @@ export default defineNuxtComponent({ required: false, default: null, }, - bottomLinks: { - type: Array as () => SidebarLinks, - required: false, - default: () => ([]), - }, }, emits: ["update:modelValue"], setup(props, context) { const $auth = useMealieAuth(); const { loggedIn, isOwnGroup } = useLoggedInState(); + const isAdmin = computed(() => $auth.user.value?.admin); + const canManage = computed(() => $auth.user.value?.canManage); const userFavoritesLink = computed(() => $auth.user.value ? `/user/${$auth.user.value.id}/favorites` : undefined); const userProfileLink = computed(() => $auth.user.value ? "/user/profile" : undefined); + const toggleDark = useToggleDarkMode(); + const state = reactive({ dropDowns: {} as Record, topSelected: null as string[] | null, secondarySelected: null as string[] | null, bottomSelected: null as string[] | null, hasOpenedBefore: false as boolean, + languageDialog: false as boolean, }); // model to control the drawer const showDrawer = computed({ @@ -171,7 +175,7 @@ export default defineNuxtComponent({ } }); - const allLinks = computed(() => [...props.topLink, ...(props.secondaryLinks || []), ...(props.bottomLinks || [])]); + const allLinks = computed(() => [...props.topLink, ...(props.secondaryLinks || [])]); function initDropdowns() { allLinks.value.forEach((link) => { state.dropDowns[link.title] = link.childrenStartExpanded || false; @@ -193,8 +197,11 @@ export default defineNuxtComponent({ userProfileLink, showDrawer, loggedIn, + isAdmin, + canManage, isOwnGroup, sessionUser: $auth.user, + toggleDark, }; }, }); diff --git a/frontend/layouts/admin.vue b/frontend/layouts/admin.vue index 9beaffe79..cb29e8e9f 100644 --- a/frontend/layouts/admin.vue +++ b/frontend/layouts/admin.vue @@ -15,7 +15,6 @@ v-model="sidebar" absolute :top-link="topLinks" - :bottom-links="bottomLinks" :user="{ data: true }" :secondary-header="$t('sidebar.developer')" :secondary-links="developerLinks" @@ -114,13 +113,4 @@ const developerLinks: SidebarLinks = [ ], }, ]; - -const bottomLinks: SidebarLinks = [ - { - icon: $globals.icons.heart, - title: i18n.t("about.support"), - href: "https://github.com/sponsors/hay-kot", - restricted: true, - }, -]; diff --git a/frontend/lib/icons/icons.ts b/frontend/lib/icons/icons.ts index 6866adf04..55777b556 100644 --- a/frontend/lib/icons/icons.ts +++ b/frontend/lib/icons/icons.ts @@ -152,6 +152,7 @@ import { mdiCookie, mdiBellPlus, mdiLinkVariantPlus, + mdiTableEdit, } from "@mdi/js"; export const icons = { @@ -240,6 +241,7 @@ export const icons = { linkVariantPlus: mdiLinkVariantPlus, lock: mdiLock, logout: mdiLogout, + manageData: mdiTableEdit, menu: mdiMenu, messageText: mdiMessageText, newBox: mdiNewBox, @@ -324,5 +326,4 @@ export const icons = { preserveLines: mdiText, preserveBlocks: mdiTextBoxOutline, flatten: mdiMinus, - }; diff --git a/tests/e2e/login.spec.ts b/tests/e2e/login.spec.ts index 88d31c0eb..416e9d90f 100644 --- a/tests/e2e/login.spec.ts +++ b/tests/e2e/login.spec.ts @@ -25,7 +25,8 @@ test('ldap login', async ({ page }) => { await page.getByRole('button', { name: 'Login', exact: true }).click(); await expect(page).toHaveURL(/\/g\/home/); await expect(page.getByRole('navigation')).toContainText(name); - await expect(page.getByRole('link', { name: 'Settings' })).not.toBeVisible(); + await page.getByText('Settings', { exact: true }).click(); + await expect(page.getByRole('link', { name: 'Admin Settings' })).not.toBeVisible(); }); test('ldap admin login', async ({ page }) => { @@ -40,7 +41,8 @@ test('ldap admin login', async ({ page }) => { // skip admin setup page await page.getByRole('link', { name: "I'm already set up, just bring me to the homepage" }).click(); await expect(page.getByRole('navigation')).toContainText(name); - await expect(page.getByRole('link', { name: 'Settings' })).toBeVisible(); + await page.getByText('Settings', { exact: true }).click(); + await expect(page.getByRole('link', { name: 'Admin Settings' })).toBeVisible(); }); test('oidc initial login', async ({ page }) => { @@ -61,7 +63,8 @@ test('oidc initial login', async ({ page }) => { await page.getByRole('button', { name: 'Sign-in' }).click(); await expect(page).toHaveURL(/\/g\/home/); await expect(page.getByRole('navigation')).toContainText(name); - await expect(page.getByRole('link', { name: 'Settings' })).not.toBeVisible(); + await page.getByText('Settings', { exact: true }).click(); + await expect(page.getByRole('link', { name: 'Admin Settings' })).not.toBeVisible(); }); test('oidc login with user not in propery group', async ({ page }) => { @@ -167,5 +170,6 @@ test('oidc admin user', async ({ page }) => { await expect(page).toHaveURL(/\/admin\/setup/, { timeout: 15000 }); await page.getByRole('link', { name: "I'm already set up, just bring me to the homepage" }).click(); await expect(page.getByRole('navigation')).toContainText(name); - await expect(page.getByRole('link', { name: 'Settings' })).toBeVisible(); + await page.getByText('Settings', { exact: true }).click(); + await expect(page.getByRole('link', { name: 'Admin Settings' })).toBeVisible(); });