Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 38 additions & 32 deletions app/components/app/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,24 @@
</div>

<template #right>
<div class="flex items-center gap-2">
<div class="hidden items-center gap-2 md:flex">
<UButton
v-if="currentApp.invite !== '#'"
label="Add App"
size="sm"
color="primary"
:to="currentApp.invite"
class="hidden rounded-lg font-semibold md:inline-flex"
class="rounded-lg font-semibold"
/>
<ClientOnly>
<LazyAppHeaderAuth />
<template #fallback>
<UButton
size="md"
color="primary"
variant="subtle"
block
class="invisible md:hidden"
icon="ic:round-discord"
tabindex="-1"
/>
<UButton
label="Sign in"
size="md"
color="primary"
variant="subtle"
block
class="invisible hidden md:inline-flex"
class="invisible rounded-lg"
icon="ic:round-discord"
tabindex="-1"
/>
Expand All @@ -62,30 +52,46 @@
</div>
</template>
<template #body>
<UNavigationMenu
orientation="vertical"
:items="mobileLinks"
class="-mx-2.5"
aria-label="Mobile navigation"
/>

<Separator class="my-6" />
<nav class="flex min-h-0 flex-1 flex-col gap-6" aria-label="Mobile menu">
<UNavigationMenu
orientation="vertical"
:items="mobileLinks"
class="-mx-1"
aria-label="Mobile navigation"
:ui="{
link: 'rounded-lg px-3 py-3 text-base font-medium',
childLink: 'rounded-lg px-3 py-2.5 text-sm',
childList: 'ms-2 border-s border-default ps-2',
}"
/>

<ClientOnly>
<LazyAppHeaderAuth mobile />
<template #fallback>
<div class="mt-auto flex flex-col items-center gap-3 border-t border-default pt-5">
<UButton
label="Sign in"
v-if="currentApp.invite !== '#'"
label="Add App"
size="md"
color="primary"
variant="subtle"
block
class="invisible"
icon="ic:round-discord"
tabindex="-1"
:to="currentApp.invite"
class="rounded-lg font-semibold"
/>
</template>
</ClientOnly>
<ClientOnly>
<LazyAppHeaderAuth mobile />
<template #fallback>
<div class="flex justify-center">
<UButton
label="Sign in"
size="md"
color="primary"
variant="subtle"
class="invisible rounded-lg"
icon="ic:round-discord"
tabindex="-1"
/>
</div>
</template>
</ClientOnly>
</div>
</nav>
</template>
</UHeader>
</template>
Expand Down
29 changes: 11 additions & 18 deletions app/components/app/HeaderAuth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,35 +36,28 @@
</div>
</LazyUDropdownMenu>
</div>
<div v-else>
<UButton
v-if="!mobile"
size="md"
color="primary"
variant="subtle"
to="/login"
block
class="md:hidden"
icon="ic:round-discord"
aria-label="Sign in with Discord"
/>
<div v-else :class="mobile ? 'flex justify-center' : undefined">
<UButton
label="Sign in"
size="md"
color="primary"
variant="subtle"
to="/login"
block
:class="mobile ? undefined : 'hidden md:inline-flex'"
:class="mobile ? 'rounded-lg' : 'hidden rounded-lg md:inline-flex'"
icon="ic:round-discord"
aria-label="Sign in with Discord"
/>
</div>
</template>
<template #placeholder>
<div class="flex items-center gap-2">
<USkeleton class="size-6 rounded-full" />
<USkeleton class="h-4 w-16" :class="mobile ? undefined : 'hidden sm:block'" />
<div class="flex items-center gap-2" :class="mobile ? 'justify-center' : undefined">
<template v-if="mobile">
<USkeleton class="h-10 w-36 rounded-lg" />
</template>
<template v-else>
<USkeleton class="size-6 rounded-full" />
<USkeleton class="hidden h-4 w-16 sm:block" />
</template>
</div>
</template>
</BetterAuthState>
Expand All @@ -74,7 +67,7 @@
import type { DropdownMenuItem } from "@nuxt/ui";

const { mobile = false } = defineProps<{
/** Renders a full-width variant suited for the mobile navigation drawer. */
/** Renders a drawer-friendly variant for the mobile navigation panel. */
mobile?: boolean;
}>();

Expand Down
6 changes: 6 additions & 0 deletions app/composables/useHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,19 @@ export function useHeader() {
{
children: [
{
description: "Tools to help you moderate your server",
label: "Moderation Tools",
to: "#moderation-tools",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Hash Links Retain Wrong Route

The shared header appears on routes such as /staryl, /blog, and /login, but to="#moderation-tools" retains the current pathname. Those pages do not contain this section, so tapping the new menu item changes only the hash and does not navigate or scroll to the requested feature; the three links should include the WolfStar route.

Context Used: CLAUDE.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/composables/useHeader.ts
Line: 70

Comment:
**Hash Links Retain Wrong Route**

The shared header appears on routes such as `/staryl`, `/blog`, and `/login`, but `to="#moderation-tools"` retains the current pathname. Those pages do not contain this section, so tapping the new menu item changes only the hash and does not navigate or scroll to the requested feature; the three links should include the WolfStar route.

**Context Used:** CLAUDE.md ([source](https://app.greptile.com/wolfstar-project/github/wolfstar-project/wolfstar.rocks/-/custom-context?memory=d7db7ccf-40f4-43f6-9080-ac8602584fb3))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Cursor Fix in Cursor Cloud Agents

},
{
description: "Track and log events in your server",
label: "Advanced Logging",
to: "#advanced-logging",
},
{
description: "Searchable moderation history for every action",
label: "Moderation Logs",
to: "#moderation-logs",
},
],
label: "Features",
Expand Down
20 changes: 20 additions & 0 deletions test/nuxt/components/app/HeaderAuth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,24 @@ describe("AppHeaderAuth", () => {
expect(second.html()).toBe(first.html());
});
});

describe("responsive sign-in", () => {
it("hides the header sign-in control below md breakpoints", async () => {
const wrapper = await mountSuspended(AppHeaderAuth);
const button = wrapper.get('a[aria-label="Sign in with Discord"]');
expect(button.classes().join(" ")).toContain("hidden");
expect(button.classes().join(" ")).toContain("md:inline-flex");
expect(button.classes().join(" ")).not.toContain("w-full");
});

it("renders a compact non-block sign-in control in the mobile drawer", async () => {
const wrapper = await mountSuspended(AppHeaderAuth, {
props: { mobile: true },
});
const button = wrapper.get('a[aria-label="Sign in with Discord"]');
expect(button.classes().join(" ")).toContain("rounded-lg");
expect(button.classes().join(" ")).not.toContain("w-full");
expect(button.attributes("href")).toBe("/login");
});
});
});
6 changes: 6 additions & 0 deletions test/nuxt/composables/useHeader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,10 @@ describe("useHeader", () => {
const labels = mobileLinks.value.map((l: any) => l.label);
expect(labels).toContain("GitHub");
});

it("should give Features children destinations on mobile", async () => {
const { mobileLinks } = await setup();
const features = mobileLinks.value.find((l: any) => l.label === "Features");
expect(features?.children?.every((child: any) => typeof child.to === "string")).toBe(true);
});
});
Loading