Vendor backend and frontend into the monorepo
The submodule collapse was only half applied: .gitmodules was deleted but backend/ and frontend/ were still recorded as gitlinks, so none of their files were tracked. Replace the gitlinks with the real trees. Also untrack .env (it carried placeholder secrets) in favour of a tracked .env.example, drop the committed __pycache__, and narrow the blanket *.png ignore that would have swallowed design assets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3cb16958ba
commit
1916f7f778
@@ -0,0 +1,43 @@
|
||||
import { Briefcase, Home, Users } from "lucide-react"
|
||||
|
||||
import { SidebarAppearance } from "@/components/Common/Appearance"
|
||||
import { Logo } from "@/components/Common/Logo"
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarHeader,
|
||||
} from "@/components/ui/sidebar"
|
||||
import useAuth from "@/hooks/useAuth"
|
||||
import { type Item, Main } from "./Main"
|
||||
import { User } from "./User"
|
||||
|
||||
const baseItems: Item[] = [
|
||||
{ icon: Home, title: "Dashboard", path: "/" },
|
||||
{ icon: Briefcase, title: "Items", path: "/items" },
|
||||
]
|
||||
|
||||
export function AppSidebar() {
|
||||
const { user: currentUser } = useAuth()
|
||||
|
||||
const items = currentUser?.is_superuser
|
||||
? [...baseItems, { icon: Users, title: "Admin", path: "/admin" }]
|
||||
: baseItems
|
||||
|
||||
return (
|
||||
<Sidebar collapsible="icon">
|
||||
<SidebarHeader className="px-4 py-6 group-data-[collapsible=icon]:px-0 group-data-[collapsible=icon]:items-center">
|
||||
<Logo variant="responsive" />
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<Main items={items} />
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<SidebarAppearance />
|
||||
<User user={currentUser} />
|
||||
</SidebarFooter>
|
||||
</Sidebar>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppSidebar
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Link as RouterLink, useRouterState } from "@tanstack/react-router"
|
||||
import type { LucideIcon } from "lucide-react"
|
||||
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
export type Item = {
|
||||
icon: LucideIcon
|
||||
title: string
|
||||
path: string
|
||||
}
|
||||
|
||||
interface MainProps {
|
||||
items: Item[]
|
||||
}
|
||||
|
||||
export function Main({ items }: MainProps) {
|
||||
const { isMobile, setOpenMobile } = useSidebar()
|
||||
const router = useRouterState()
|
||||
const currentPath = router.location.pathname
|
||||
|
||||
const handleMenuClick = () => {
|
||||
if (isMobile) {
|
||||
setOpenMobile(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => {
|
||||
const isActive = currentPath === item.path
|
||||
|
||||
return (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton
|
||||
tooltip={item.title}
|
||||
isActive={isActive}
|
||||
asChild
|
||||
>
|
||||
<RouterLink to={item.path} onClick={handleMenuClick}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</RouterLink>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
)
|
||||
})}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Link as RouterLink } from "@tanstack/react-router"
|
||||
import { ChevronsUpDown, LogOut, Settings } from "lucide-react"
|
||||
|
||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import {
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar"
|
||||
import useAuth from "@/hooks/useAuth"
|
||||
import { getInitials } from "@/utils"
|
||||
|
||||
interface UserInfoProps {
|
||||
fullName?: string
|
||||
email?: string
|
||||
}
|
||||
|
||||
function UserInfo({ fullName, email }: UserInfoProps) {
|
||||
return (
|
||||
<div className="flex items-center gap-2.5 w-full min-w-0">
|
||||
<Avatar className="size-8">
|
||||
<AvatarFallback className="bg-zinc-600 text-white">
|
||||
{getInitials(fullName || "User")}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex flex-col items-start min-w-0">
|
||||
<p className="text-sm font-medium truncate w-full">{fullName}</p>
|
||||
<p className="text-xs text-muted-foreground truncate w-full">{email}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function User({ user }: { user: any }) {
|
||||
const { logout } = useAuth()
|
||||
const { isMobile, setOpenMobile } = useSidebar()
|
||||
|
||||
if (!user) return null
|
||||
|
||||
const handleMenuClick = () => {
|
||||
if (isMobile) {
|
||||
setOpenMobile(false)
|
||||
}
|
||||
}
|
||||
const handleLogout = async () => {
|
||||
logout()
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
data-testid="user-menu"
|
||||
>
|
||||
<UserInfo fullName={user?.full_name} email={user?.email} />
|
||||
<ChevronsUpDown className="ml-auto size-4 text-muted-foreground" />
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenuLabel className="p-0 font-normal">
|
||||
<UserInfo fullName={user?.full_name} email={user?.email} />
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<RouterLink to="/settings" onClick={handleMenuClick}>
|
||||
<DropdownMenuItem>
|
||||
<Settings />
|
||||
User Settings
|
||||
</DropdownMenuItem>
|
||||
</RouterLink>
|
||||
<DropdownMenuItem onClick={handleLogout}>
|
||||
<LogOut />
|
||||
Log Out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user