Skip to content
Merged
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
3 changes: 3 additions & 0 deletions app/(dashboard)/faculty/analytics/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function FacultyAnalyticsPage() {
return <h1 className="text-2xl font-semibold">Faculty Analytics</h1>
}
3 changes: 3 additions & 0 deletions app/(dashboard)/faculty/courses/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function FacultyCoursesPage() {
return <h1 className="text-2xl font-semibold">Faculty Courses</h1>
}
5 changes: 5 additions & 0 deletions app/(dashboard)/faculty/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from "next/navigation"

export default function FacultyPage() {
redirect("/faculty/courses")
}
21 changes: 21 additions & 0 deletions app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ReactNode } from "react"

import { AppSidebar } from "@/components/app-sidebar"
import { DashboardContentHeader } from "@/components/dashboard-content-header"
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"

type DashboardLayoutProps = {
children: ReactNode
}

export default function DashboardLayout({ children }: DashboardLayoutProps) {
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<DashboardContentHeader />
<main className="flex flex-1 flex-col p-4">{children}</main>
</SidebarInset>
</SidebarProvider>
)
}
3 changes: 3 additions & 0 deletions app/(dashboard)/student/courses/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function StudentCoursesPage() {
return <h1 className="text-2xl font-semibold">Student Courses</h1>
}
5 changes: 5 additions & 0 deletions app/(dashboard)/student/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from "next/navigation"

export default function StudentPage() {
redirect("/student/courses")
}
1 change: 0 additions & 1 deletion app/auth/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useLogin } from "@/hooks/api/use-login"
import { LoginRequest, loginRequestSchema } from "@/types/kubb/gen";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Field, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { BackgroundGradientAnimation } from "@/components/ui/background-gradient-animation";
Expand Down
55 changes: 55 additions & 0 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { AppSidebar } from "@/components/app-sidebar"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import { Separator } from "@/components/ui/separator"
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar"

export default function Page() {
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator
orientation="vertical"
className="mr-2 data-[orientation=vertical]:h-4"
/>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">
Build Your Application
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="bg-muted/50 aspect-video rounded-xl" />
<div className="bg-muted/50 aspect-video rounded-xl" />
<div className="bg-muted/50 aspect-video rounded-xl" />
</div>
<div className="bg-muted/50 min-h-[100vh] flex-1 rounded-xl md:min-h-min" />
</div>
</SidebarInset>
</SidebarProvider>
)
}
2 changes: 0 additions & 2 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);


/* Gradient Animation */
--animate-first: moveVertical 60s ease infinite;
--animate-second: moveInCircle 40s reverse infinite;
Expand Down Expand Up @@ -94,7 +93,6 @@
}
/* End of Gradient Animation */


:root {
--radius: 0.625rem;
--brand-blue: oklch(0.428 0.25 264.53);
Expand Down
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 92 additions & 0 deletions components/app-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"use client"

import type { ComponentProps, ComponentType } from "react"
import { BarChart3, BookOpen, GraduationCap } from "lucide-react"
import Link from "next/link"
import { usePathname } from "next/navigation"

import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
} from "@/components/ui/sidebar"

type NavItem = {
title: string
href: string
icon: ComponentType<{ className?: string }>
}

const studentNavItems: NavItem[] = [
{
title: "Courses",
href: "/student/courses",
icon: BookOpen,
},
]

const facultyNavItems: NavItem[] = [
{
title: "Courses",
href: "/faculty/courses",
icon: BookOpen,
},
{
title: "Analytics",
href: "/faculty/analytics",
icon: BarChart3,
},
]

export function AppSidebar({ ...props }: ComponentProps<typeof Sidebar>) {
const pathname = usePathname()
const isFaculty = pathname.startsWith("/faculty")
const roleLabel = isFaculty ? "Faculty" : "Student"
const navItems = isFaculty ? facultyNavItems : studentNavItems

return (
<Sidebar collapsible="icon" {...props}>
<SidebarHeader>
<div className="flex items-center gap-2 px-2 py-1 group-data-[collapsible=icon]:p-0">
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex size-8 shrink-0 items-center justify-center rounded-md">
<GraduationCap className="size-4" />
</div>
<div className="group-data-[collapsible=icon]:hidden">
<p className="text-sm font-semibold leading-none">Faculytics</p>
<p className="text-sidebar-foreground/70 text-xs">{roleLabel}</p>
</div>
</div>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>{roleLabel} Navigation</SidebarGroupLabel>
<SidebarMenu>
{navItems.map((item) => (
<SidebarMenuItem key={item.href}>
<SidebarMenuButton
asChild
tooltip={item.title}
isActive={
pathname === item.href || pathname.startsWith(`${item.href}/`)
}
>
<Link href={item.href}>
<item.icon />
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<SidebarRail />
</Sidebar>
)
}
61 changes: 61 additions & 0 deletions components/dashboard-content-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"use client"

import Link from "next/link"
import { usePathname } from "next/navigation"
import { Fragment } from "react"

import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import { Separator } from "@/components/ui/separator"
import { SidebarTrigger } from "@/components/ui/sidebar"

function formatSegment(segment: string) {
return segment
.split("-")
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(" ")
}

export function DashboardContentHeader() {
const pathname = usePathname()
const segments = pathname.split("/").filter(Boolean)

return (
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
<SidebarTrigger className="-ml-1" />
<Separator
orientation="vertical"
className="mr-2 data-[orientation=vertical]:h-4"
/>
<Breadcrumb>
<BreadcrumbList>
{segments.map((segment, index) => {
const href = `/${segments.slice(0, index + 1).join("/")}`
const isLast = index === segments.length - 1

return (
<Fragment key={href}>
<BreadcrumbItem>
{!isLast ? (
<BreadcrumbLink asChild>
<Link href={href}>{formatSegment(segment)}</Link>
</BreadcrumbLink>
) : (
<BreadcrumbPage>{formatSegment(segment)}</BreadcrumbPage>
)}
</BreadcrumbItem>
{!isLast && <BreadcrumbSeparator />}
</Fragment>
)
})}
</BreadcrumbList>
</Breadcrumb>
</header>
)
}
73 changes: 73 additions & 0 deletions components/nav-main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"use client"

import { ChevronRight, type LucideIcon } from "lucide-react"

import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "@/components/ui/sidebar"

export function NavMain({
items,
}: {
items: {
title: string
url: string
icon?: LucideIcon
isActive?: boolean
items?: {
title: string
url: string
}[]
}[]
}) {
return (
<SidebarGroup>
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarMenu>
{items.map((item) => (
<Collapsible
key={item.title}
asChild
defaultOpen={item.isActive}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton tooltip={item.title}>
{item.icon && <item.icon />}
<span>{item.title}</span>
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
</SidebarGroup>
)
}
Loading