refactor(ux): consolidate BMAD skills, update design system, and clean up Prisma generated client

This commit is contained in:
Sepehr Ramezani
2026-04-19 19:21:27 +02:00
parent 5296c4da2c
commit 25529a24b8
2476 changed files with 127934 additions and 101962 deletions

View File

@@ -1,7 +1,7 @@
'use client'
import Link from 'next/link'
import { usePathname, useSearchParams } from 'next/navigation'
import { usePathname, useSearchParams, useRouter } from 'next/navigation'
import { cn } from '@/lib/utils'
import {
Lightbulb,
@@ -10,6 +10,8 @@ import {
Trash2,
Plus,
Sparkles,
X,
Tag,
} from 'lucide-react'
import { Button } from '@/components/ui/button'
import {
@@ -21,12 +23,51 @@ import {
import { useLanguage } from '@/lib/i18n'
import { NotebooksList } from './notebooks-list'
import { useHomeViewOptional } from '@/context/home-view-context'
import { useEffect, useState } from 'react'
import { getTrashCount } from '@/app/actions/notes'
const HIDDEN_ROUTES = ['/agents', '/chat', '/lab']
export function Sidebar({ className, user }: { className?: string, user?: any }) {
const pathname = usePathname()
const searchParams = useSearchParams()
const router = useRouter()
const { t } = useLanguage()
const homeBridge = useHomeViewOptional()
const [trashCount, setTrashCount] = useState(0)
// Fetch trash count
useEffect(() => {
getTrashCount().then(setTrashCount)
}, [pathname, searchParams])
// Hide sidebar on Agents, Chat IA and Lab routes
if (HIDDEN_ROUTES.some(r => pathname.startsWith(r))) return null
// Active label filter
const activeLabel = searchParams.get('label')
const activeLabels = searchParams.get('labels')?.split(',').filter(Boolean) || []
const clearLabelFilter = () => {
const params = new URLSearchParams(searchParams)
params.delete('label')
router.push(`/?${params.toString()}`)
}
const clearLabelsFilter = (labelToRemove?: string) => {
const params = new URLSearchParams(searchParams)
if (labelToRemove) {
const remaining = activeLabels.filter(l => l !== labelToRemove)
if (remaining.length > 0) {
params.set('labels', remaining.join(','))
} else {
params.delete('labels')
}
} else {
params.delete('labels')
}
router.push(`/?${params.toString()}`)
}
// Helper to determine if a link is active
const isActive = (href: string, exact = false) => {
@@ -50,7 +91,7 @@ export function Sidebar({ className, user }: { className?: string, user?: any })
return pathname === href
}
const NavItem = ({ href, icon: Icon, label, active }: any) => (
const NavItem = ({ href, icon: Icon, label, active, badge }: any) => (
<Link
href={href}
className={cn(
@@ -63,6 +104,16 @@ export function Sidebar({ className, user }: { className?: string, user?: any })
>
<Icon className={cn("w-5 h-5", active ? "fill-current" : "")} />
<span className="truncate">{label}</span>
{badge > 0 && (
<span className={cn(
"ms-auto text-[10px] font-semibold px-1.5 py-0.5 rounded-full min-w-[20px] text-center",
active
? "bg-primary/20 text-primary"
: "bg-muted text-muted-foreground"
)}>
{badge}
</span>
)}
</Link>
)
@@ -112,7 +163,42 @@ export function Sidebar({ className, user }: { className?: string, user?: any })
<NotebooksList />
</div>
{/* Active Label Filter Chips */}
{pathname === '/' && (activeLabel || activeLabels.length > 0) && (
<div className="px-4 pt-2 flex flex-col gap-1">
{activeLabel && (
<div className="flex items-center gap-2 ps-2 pe-1 py-1.5 rounded-e-full me-2 bg-primary/10 dark:bg-primary/20 text-primary dark:text-primary-foreground">
<Tag className="w-3.5 h-3.5 shrink-0" />
<span className="text-xs font-medium truncate flex-1">{activeLabel}</span>
<button
type="button"
onClick={clearLabelFilter}
className="shrink-0 p-0.5 rounded-full hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors"
title={t('sidebar.clearFilter') || 'Remove filter'}
>
<X className="w-3 h-3" />
</button>
</div>
)}
{activeLabels.map((label) => (
<div
key={label}
className="flex items-center gap-2 ps-2 pe-1 py-1.5 rounded-e-full me-2 bg-primary/10 dark:bg-primary/20 text-primary dark:text-primary-foreground"
>
<Tag className="w-3.5 h-3.5 shrink-0" />
<span className="text-xs font-medium truncate flex-1">{label}</span>
<button
type="button"
onClick={() => clearLabelsFilter(label)}
className="shrink-0 p-0.5 rounded-full hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors"
title={t('sidebar.clearFilter') || 'Remove filter'}
>
<X className="w-3 h-3" />
</button>
</div>
))}
</div>
)}
{/* Archive & Trash */}
<div className="flex flex-col mt-2 border-t border-transparent">
@@ -127,6 +213,7 @@ export function Sidebar({ className, user }: { className?: string, user?: any })
icon={Trash2}
label={t('sidebar.trash') || 'Corbeille'}
active={pathname === '/trash'}
badge={trashCount}
/>
</div>