Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3ac11aa
style: update (dark) sidebar css variables
lorenzocorallo Jun 4, 2026
aaed723
feat: sidebar boilerplate and breadcrumb header
lorenzocorallo Jun 4, 2026
da6760c
some refactor
lorenzocorallo Jun 4, 2026
1d485af
feat: use collapsible
lorenzocorallo Jun 4, 2026
ef68226
fix: add tg/grants and remove boilerplate
lorenzocorallo Jun 5, 2026
c8ad53a
feat: storage hooks and cookies utility
lorenzocorallo Jun 5, 2026
24828cd
feat: user nav, category open state persistence
lorenzocorallo Jun 5, 2026
1a5d70d
fix: cookie expiration, use <Link> instead of <a>
lorenzocorallo Jun 5, 2026
fba8bd3
chore: remove zustand, remove old i18n, biome fixes
lorenzocorallo Jun 5, 2026
73acf8f
feat: sidebar item icons, type cleanup
lorenzocorallo Jun 5, 2026
6099b46
feat: remove category pages, adjust page padding
lorenzocorallo Jun 5, 2026
ec3c62e
chore: biome
lorenzocorallo Jun 5, 2026
454e9db
style: adjust destructive color variable
lorenzocorallo Jun 5, 2026
fc32ad7
style: container mx-auto by default
lorenzocorallo Jun 5, 2026
51e171e
fix: remove category pages, dont create link for category in breadcrumb
lorenzocorallo Jun 5, 2026
6362566
fix: re-render issue with the use-cookie-storage hook
lorenzocorallo Jun 5, 2026
075f156
fix: same as previous commit, but for use-session-storage
lorenzocorallo Jun 5, 2026
21c64c5
fix: coderabbit suggestion
lorenzocorallo Jun 5, 2026
e9fd9e7
fix: match also subroutes in category items
lorenzocorallo Jun 5, 2026
60d3617
fix: catch JSON parse error
lorenzocorallo Jun 5, 2026
2510a36
fix: rename column in groups
lorenzocorallo Jun 5, 2026
f6e4fa3
style: move breadcrumb to center
lorenzocorallo Jun 5, 2026
fd76b34
fix: cleanup home
lorenzocorallo Jun 5, 2026
d7388b5
style: button icon svg size
lorenzocorallo Jun 5, 2026
ba20b0b
Merge branch 'main' into sidebar
lorenzocorallo Jun 6, 2026
57d3a23
style: fix padding mismatch between pages and loadings
lorenzocorallo Jun 6, 2026
5003e80
feat: loading skeleton for account page
lorenzocorallo Jun 6, 2026
0b1596c
perf: make dashboard homepage static for now
lorenzocorallo Jun 6, 2026
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
34 changes: 34 additions & 0 deletions src/app/dashboard/(active)/account/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Skeleton } from "@/components/ui/skeleton"
import { NewPasskeyButton } from "./passkey-button"

export default function Loading() {
return (
<main className="container">
<h2 className="text-accent-foreground mb-4 text-3xl font-bold">Account</h2>
<div className="flex gap-4 mb-12">
<Skeleton className="h-32 w-32 rounded-full" />

<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<span className="text-accent-foreground/70">Name:</span>
<Skeleton className="w-40 h-5" />
</div>

<div className="flex items-center gap-2">
<span className="text-accent-foreground/70">Email:</span>
<Skeleton className="w-70 h-5" />
</div>
<div className="flex items-center gap-2">
<span className="text-accent-foreground/70">Telegram:</span>
<Skeleton className="w-60 h-5" />
</div>
</div>
</div>
<div className="flex flex-col gap-4 justify-start items-start">
<h3>Passkeys</h3>
<Skeleton className="w-full h-10" />
<NewPasskeyButton />
</div>
</main>
)
}
6 changes: 3 additions & 3 deletions src/app/dashboard/(active)/account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ export default async function Account() {
const { user } = session

return (
<main className="container mx-auto px-4 py-8">
<main className="container">
<h2 className="text-accent-foreground mb-4 text-3xl font-bold">Account</h2>
<div className="flex gap-4 mb-12">
<Avatar className="h-32 w-32 rounded-lg after:rounded-lg">
<Avatar className="h-32 w-32">
{user.image && <AvatarImage src={user.image} alt={`propic of ${user.name}`} />}
<AvatarFallback className="rounded-lg text-3xl">
<AvatarFallback className="text-3xl">
{user.name ? getInitials(user.name) : <UserIcon size={48} />}
</AvatarFallback>
</Avatar>
Expand Down
2 changes: 1 addition & 1 deletion src/app/dashboard/(active)/azure/members/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SkeletonAssocTable } from "./table"

export default function Loading() {
return (
<div className="container p-8">
<div className="container">
<SkeletonAssocTable />
</div>
)
Expand Down
4 changes: 1 addition & 3 deletions src/app/dashboard/(active)/azure/members/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Suspense } from "react"
import { ErrorBoundary } from "react-error-boundary"
import { Spinner } from "@/components/spinner"
import { wait } from "@/lib/utils"
import { getAzureMembers } from "@/server/actions/azure"
import { AssocTable } from "./table"

export default async function AssocMembers() {
const members = await getAzureMembers()
// await wait(120_000)

return (
<div className="container p-8">
<div className="container">
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<Suspense fallback={<Spinner />}>
<AssocTable members={members} />
Expand Down
29 changes: 0 additions & 29 deletions src/app/dashboard/(active)/azure/page.tsx

This file was deleted.

74 changes: 74 additions & 0 deletions src/app/dashboard/(active)/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"use client"
import { usePathname } from "next/navigation"
import { Fragment, useMemo } from "react"
import { NAV_MAP } from "@/components/dashboard-sidebar/data"
import {
BreadcrumbItem as BreadcrumbItemComponent,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
Breadcrumb as BreadcrumbRoot,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

export type BreadcrumbItem = {
title: string
url?: string
}

export function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
const pathname = usePathname()
const items = useMemo(() => getBreadcrumbs(NAV_MAP, pathname), [pathname])

return (
<BreadcrumbRoot className={className} {...props}>
<BreadcrumbList>
{items.map((item, i) => (
<Fragment key={`breadcrumb-item-${i}`}>
<BreadcrumbItemComponent className="hidden md:block">
{i === items.length - 1 ? (
<BreadcrumbPage>{item.title}</BreadcrumbPage>
) : item.url ? (
<BreadcrumbLink href={item.url}>{item.title}</BreadcrumbLink>
) : (
item.title
)}
</BreadcrumbItemComponent>
{i < items.length - 1 && <BreadcrumbSeparator className="hidden md:block" />}
</Fragment>
))}
</BreadcrumbList>
</BreadcrumbRoot>
)
}

function getBreadcrumbs(navMap: Map<string, string>, pathname: string): BreadcrumbItem[] {
const segments = pathname.split("/").filter(Boolean)
const breadcrumbs: BreadcrumbItem[] = []

let currentPath = ""
let i = 0

for (const segment of segments) {
currentPath += `/${segment}`
let title = navMap.get(currentPath)
if (!title) {
if (isUUIDorId(segment)) {
title = "Details"
} else {
title = segment.charAt(0).toUpperCase() + segment.slice(1)
}
}

// note: at the moment we do not plan to make category pages.
// If such pages are made in the future, this logic can be removed
breadcrumbs.push({ title, url: i !== 1 ? currentPath : undefined })
i++
}

return breadcrumbs
}

function isUUIDorId(segment: string) {
return !Number.isNaN(Number(segment)) || segment.length > 20 // Regex custom a seconda dei tuoi ID
}
10 changes: 7 additions & 3 deletions src/app/dashboard/(active)/complete-profile.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
"use client"

import type { User } from "better-auth"
import { UserRoundPenIcon } from "lucide-react"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { useSession } from "@/lib/auth"

export function CompleteProfile() {
const { data, isPending } = useSession()

if (!data || isPending) return null

export function CompleteProfile({ user }: { user: User }) {
return (
!user.name && (
!data.user.name && (
<div className="flex items-center p-2 pl-4 mb-4 gap-2 rounded-lg border text-accent-foreground bg-yellow-400/10 border-yellow-400">
<UserRoundPenIcon size={16} />
<p className="grow">Your profile is incomplete, please enter the missing information.</p>
Expand Down
41 changes: 41 additions & 0 deletions src/app/dashboard/(active)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { cookies } from "next/headers"
import { DashboardSidebar } from "@/components/dashboard-sidebar"
import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { COOKIES } from "@/constants"
import { Breadcrumb } from "./breadcrumb"

function parseCookie(cookie: string) {
try {
const parsed = JSON.parse(cookie)
return parsed
} catch (_e) {
return {}
}
}

export default async function AdminLayout({ children }: { children: React.ReactNode }) {
const cookieStore = await cookies()
const cookie = cookieStore.get(COOKIES.SIDEBAR_CATEGORY_STATE)?.value
const DSCategoryState = cookie ? parseCookie(cookie) : {}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
return (
<SidebarProvider
style={
{
"--sidebar-width": "19rem",
} as React.CSSProperties
}
>
Comment on lines +17 to +28

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Qui viene riletto e settato sidebar_category_state, quindi lo stato delle sezioni, ma la sidebar salva anche lo stato expanded o collapsed in sidebar_state, che peró non viene letto qui, quindi la sidebar userà sempre il default open dopo un refresh. Si potrebbe leggere anche quel cookie e passarlo alla sidebar

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ho visto che sidebar_category_state è centralizzato, mentre sidebar_state è hardcoded dentro SidebarProvider. Forse conviene uniformarli e aggiungere anche sidebar_state a cookies in constants

<DashboardSidebar categoryState={DSCategoryState} />
<SidebarInset className="px-4">
<header className="flex h-16 shrink-0 relative items-center justify-between gap-2 border-b mb-8">
<SidebarTrigger className="-ml-1" size="icon" />
<Breadcrumb className="absolute left-1/2 -translate-x-1/2" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Qesto <div> vuoto non mi sembra cambi nulla nella UI, dato che il breadcrumb è già centrato e il SidebarTrigger resta a sinistra.

Forse si potrebbe semplificare così:

<header className="relative mb-8 flex h-16 shrink-0 items-center border-b">
  <SidebarTrigger className="-ml-1" size="icon" />
  <Breadcrumb className="absolute left-1/2 -translate-x-1/2" />
</header>

<div></div>
</header>
{children}
</SidebarInset>
</SidebarProvider>
)
}
// <Separator orientation="vertical" className="mr-2 data-vertical:h-4 data-vertical:self-auto" />
51 changes: 11 additions & 40 deletions src/app/dashboard/(active)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,16 @@
import Image from "next/image"
import Link from "next/link"
import azureSvg from "@/assets/svg/azure.svg"
import telegramSvg from "@/assets/svg/telegram.svg"
import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { getServerSession } from "@/server/auth"
import { InfoIcon } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { CompleteProfile } from "./complete-profile"

export default async function AdminHome() {
const { data: session } = await getServerSession()
export default function AdminHome() {
return (
session && (
<div className="container py-8 px-2">
<CompleteProfile user={session.user} />
<h2 className="text-accent-foreground mb-4 text-3xl font-bold">Home</h2>

<div className="gap-4 flex justify-start flex-wrap items-center">
<Link href="/dashboard/azure">
<Card className="w-90 hover:bg-accent transition-colors">
<CardHeader>
<CardTitle className="flex gap-2">
<Image alt="azure logo" src={azureSvg} className="size-5" />
Azure
</CardTitle>
<CardDescription>Manage Azure related things</CardDescription>
</CardHeader>
</Card>
</Link>

<Link href="/dashboard/telegram">
<Card className="w-90 hover:bg-accent transition-colors">
<CardHeader>
<CardTitle className="flex gap-2">
<Image alt="telegram logo" src={telegramSvg} className="size-5" />
Telegram
</CardTitle>
<CardDescription>Manage Telegram related things</CardDescription>
</CardHeader>
</Card>
</Link>
</div>
</div>
)
<div className="container">
<CompleteProfile />
<Alert variant="info">
<InfoIcon />
<AlertTitle>Page under construction</AlertTitle>
<AlertDescription>Use the sidebar to access the sections</AlertDescription>
</Alert>
</div>
)
}
4 changes: 2 additions & 2 deletions src/app/dashboard/(active)/telegram/grants/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { NewGrant } from "./new-grant"

export default async function Loading() {
return (
<div className="container p-8 h-full">
<div className="py-4 flex gap-4 justify-start items-center">
<div className="container h-full">
<div className="pb-4 flex gap-4 justify-start items-center">
<p>Telegram Grants</p>
<NewGrant />
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/app/dashboard/(active)/telegram/grants/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export default async function GrantsPage() {
const { grants: scheduled } = await trpc.tg.grants.getScheduled.query()

return (
<div className="container p-8 h-full">
<div className="py-4 flex gap-4 justify-start items-center">
<div className="container">
<div className="pb-4 flex gap-4 justify-start items-center">
<p>Telegram Grants</p>
<NewGrant />
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/app/dashboard/(active)/telegram/groups/group-row.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client"
import { Copy, Pen } from "lucide-react"
import { Copy } from "lucide-react"
import { useRouter } from "next/navigation"
import { toast } from "sonner"
import { Badge } from "@/components/ui/badge"
Expand Down
2 changes: 1 addition & 1 deletion src/app/dashboard/(active)/telegram/groups/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SearchInput } from "./search-input"

export default async function Loading() {
return (
<div className="container p-8">
<div className="container">
<SearchInput disabled />
<div className="flex items-center pt-4 gap-1">
<p className="text-sm text-muted-foreground">Count: </p>
Expand Down
2 changes: 1 addition & 1 deletion src/app/dashboard/(active)/telegram/groups/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default async function TgGroups({ searchParams }: { searchParams: Promise
const sorted = rows.sort((a, b) => a.title.localeCompare(b.title))

return (
<div className="container p-8">
<div className="container">
<SearchInput />
<p className="pt-4 text-sm text-muted-foreground">
Count: <span className="text-foreground">{rows.length}</span>
Expand Down
Loading
Loading