feat: replace gear dropdown with direct settings link
Remove the gear dropdown menu from AppHeader; cog icon is now a plain router-link to /settings. Admin-only /admin/users and /admin/logs routes are replaced with redirects to /settings. Mobile menu admin links removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { useTheme } from "@/composables/useTheme";
|
||||
import { useShortcuts } from "@/composables/useShortcuts";
|
||||
@@ -17,8 +17,6 @@ const route = useRoute();
|
||||
const isChatActive = computed(() => route.path.startsWith("/chat"));
|
||||
|
||||
const mobileMenuOpen = ref(false);
|
||||
const gearOpen = ref(false);
|
||||
const gearMenuRef = ref<HTMLElement | null>(null);
|
||||
|
||||
const statusShortLabel = computed(() => {
|
||||
if (chatStore.ollamaStatus === "unavailable") return "Offline";
|
||||
@@ -51,16 +49,6 @@ function toggleMobileMenu() {
|
||||
mobileMenuOpen.value = !mobileMenuOpen.value;
|
||||
}
|
||||
|
||||
function toggleGear() {
|
||||
gearOpen.value = !gearOpen.value;
|
||||
}
|
||||
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
if (gearMenuRef.value && !gearMenuRef.value.contains(e.target as Node)) {
|
||||
gearOpen.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
await authStore.logout();
|
||||
mobileMenuOpen.value = false;
|
||||
@@ -69,11 +57,7 @@ async function handleLogout() {
|
||||
|
||||
router.afterEach(() => {
|
||||
mobileMenuOpen.value = false;
|
||||
gearOpen.value = false;
|
||||
});
|
||||
|
||||
onMounted(() => document.addEventListener("click", handleClickOutside));
|
||||
onUnmounted(() => document.removeEventListener("click", handleClickOutside));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -107,20 +91,13 @@ onUnmounted(() => document.removeEventListener("click", handleClickOutside));
|
||||
{{ theme === "dark" ? "\u2600" : "\u263E" }}
|
||||
</button>
|
||||
|
||||
<!-- Gear dropdown -->
|
||||
<div class="gear-menu" ref="gearMenuRef">
|
||||
<button class="btn-icon btn-gear" @click.stop="toggleGear" :class="{ active: gearOpen }" title="Settings">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div v-if="gearOpen" class="gear-dropdown">
|
||||
<router-link to="/settings" class="gear-item">Settings</router-link>
|
||||
<router-link v-if="authStore.isAdmin" to="/admin/users" class="gear-item">Users</router-link>
|
||||
<router-link v-if="authStore.isAdmin" to="/admin/logs" class="gear-item">Logs</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Settings link -->
|
||||
<router-link to="/settings" class="btn-icon" aria-label="Settings" title="Settings">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||
</svg>
|
||||
</router-link>
|
||||
|
||||
<div class="user-info">
|
||||
<span class="username">{{ authStore.user?.username }}</span>
|
||||
@@ -146,8 +123,6 @@ onUnmounted(() => document.removeEventListener("click", handleClickOutside));
|
||||
<router-link to="/graph" class="nav-link">Graph</router-link>
|
||||
<div class="mobile-divider"></div>
|
||||
<router-link to="/settings" class="nav-link">Settings</router-link>
|
||||
<router-link v-if="authStore.isAdmin" to="/admin/users" class="nav-link">Users</router-link>
|
||||
<router-link v-if="authStore.isAdmin" to="/admin/logs" class="nav-link">Logs</router-link>
|
||||
<div class="mobile-divider"></div>
|
||||
<div class="mobile-actions">
|
||||
<span class="status-indicator" :class="statusClass" :title="statusLabel">
|
||||
@@ -281,40 +256,6 @@ onUnmounted(() => document.removeEventListener("click", handleClickOutside));
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Gear dropdown */
|
||||
.gear-menu {
|
||||
position: relative;
|
||||
}
|
||||
.gear-dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 6px);
|
||||
right: 0;
|
||||
background: var(--color-bg-card);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: 0 4px 16px var(--color-shadow, rgba(0,0,0,0.15));
|
||||
min-width: 140px;
|
||||
z-index: 200;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.gear-item {
|
||||
padding: 0.55rem 0.9rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
text-decoration: none;
|
||||
transition: background 0.12s, color 0.12s;
|
||||
}
|
||||
.gear-item:hover {
|
||||
background: var(--color-bg-secondary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.gear-item.router-link-active {
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* User info */
|
||||
.user-info {
|
||||
display: flex;
|
||||
|
||||
@@ -110,16 +110,8 @@ const router = createRouter({
|
||||
name: "settings",
|
||||
component: () => import("@/views/SettingsView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/users",
|
||||
name: "admin-users",
|
||||
component: () => import("@/views/UserManagementView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/admin/logs",
|
||||
name: "admin-logs",
|
||||
component: () => import("@/views/LogsView.vue"),
|
||||
},
|
||||
{ path: "/admin/users", redirect: "/settings" },
|
||||
{ path: "/admin/logs", redirect: "/settings" },
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user