Skip to content

Commit 95d3833

Browse files
committed
feat: Implement mobile responsive layouts for academics and main header, and introduce a new home header.
1 parent 962a70c commit 95d3833

13 files changed

Lines changed: 240 additions & 123 deletions

File tree

app/(main)/academics/AcademicsClientView.tsx

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
SelectTrigger,
1818
SelectValue,
1919
} from "@/components/ui/select";
20-
import { BookOpen } from "lucide-react";
20+
import { BookOpen, ArrowLeft } from "lucide-react";
2121
import { addToRecentFiles } from "@/lib/recentFiles";
2222
import { InitialData } from "@/types/general";
2323
import { ActiveFile } from "@/types/file";
@@ -190,10 +190,10 @@ export default function AcademicsClientView({ initialData }: Props) {
190190

191191
return (
192192
<div className="h-[calc(100vh-7rem)] flex flex-col">
193-
<div className="p-3 border-b flex items-center gap-4">
193+
<div className="p-3 border-b flex flex-col md:flex-row items-stretch md:items-center gap-2 md:gap-4 bg-background z-10">
194194
{/* Branch */}
195195
<Select value={selectedBranchId} onValueChange={handleBranchChange}>
196-
<SelectTrigger className="w-[260px]">
196+
<SelectTrigger className="w-full md:w-[260px]">
197197
<SelectValue placeholder="Select Branch..." />
198198
</SelectTrigger>
199199
<SelectContent>
@@ -211,7 +211,7 @@ export default function AcademicsClientView({ initialData }: Props) {
211211
onValueChange={handleYearChange}
212212
disabled={!selectedBranchId || yearsForSelectedBranch.length === 0}
213213
>
214-
<SelectTrigger className="w-[260px]">
214+
<SelectTrigger className="w-full md:w-[260px]">
215215
<SelectValue placeholder="Select Year..." />
216216
</SelectTrigger>
217217
<SelectContent>
@@ -229,7 +229,7 @@ export default function AcademicsClientView({ initialData }: Props) {
229229
onValueChange={handleSyllabusChange}
230230
disabled={!selectedYearId || syllabiForSelectedYear.length === 0}
231231
>
232-
<SelectTrigger className="w-[260px]">
232+
<SelectTrigger className="w-full md:w-[260px]">
233233
<SelectValue placeholder="Select Syllabus..." />
234234
</SelectTrigger>
235235
<SelectContent>
@@ -242,29 +242,62 @@ export default function AcademicsClientView({ initialData }: Props) {
242242
</Select>
243243

244244
{/* Search Files */}
245-
<div className="ml-auto">
245+
<div className="w-full md:w-auto md:ml-auto">
246246
<AcademicsFileSearch onFileSelect={handleFileSelect} />
247247
</div>
248248
</div>
249249

250-
{/* Main Panel */}
251-
<ResizablePanelGroup direction="horizontal" className="flex-grow">
252-
<ResizablePanel defaultSize={25} minSize={20} maxSize={40}>
253-
<AcademicsSidebar
254-
onFileSelect={handleFileSelect}
255-
subjects={subjectsForSelectedSyllabus}
256-
isSyllabusSelected={!!selectedSyllabusId}
257-
/>
258-
</ResizablePanel>
259-
<ResizableHandle withHandle />
260-
<ResizablePanel defaultSize={75}>
250+
{/* Main Panel Content */}
251+
<div className="flex-grow overflow-hidden relative">
252+
{/* Mobile View */}
253+
<div className="md:hidden h-full flex flex-col">
261254
{activeFile ? (
262-
<PdfViewer file={activeFile} key={activeFile.id} />
255+
<div className="flex flex-col h-full">
256+
<div className="p-2 border-b bg-muted/20 flex items-center">
257+
<button
258+
onClick={() => setActiveFile(null)}
259+
className="flex items-center gap-2 text-sm font-medium text-primary hover:underline px-2 py-1"
260+
>
261+
<ArrowLeft className="h-4 w-4" />
262+
Back to Files
263+
</button>
264+
</div>
265+
<div className="flex-grow overflow-hidden">
266+
<PdfViewer file={activeFile} key={activeFile.id} />
267+
</div>
268+
</div>
263269
) : (
264-
<WelcomePanel />
270+
<div className="flex-grow overflow-hidden">
271+
<AcademicsSidebar
272+
onFileSelect={handleFileSelect}
273+
subjects={subjectsForSelectedSyllabus}
274+
isSyllabusSelected={!!selectedSyllabusId}
275+
/>
276+
</div>
265277
)}
266-
</ResizablePanel>
267-
</ResizablePanelGroup>
278+
</div>
279+
280+
{/* Desktop View */}
281+
<div className="hidden md:block h-full">
282+
<ResizablePanelGroup direction="horizontal" className="h-full">
283+
<ResizablePanel defaultSize={25} minSize={20} maxSize={40}>
284+
<AcademicsSidebar
285+
onFileSelect={handleFileSelect}
286+
subjects={subjectsForSelectedSyllabus}
287+
isSyllabusSelected={!!selectedSyllabusId}
288+
/>
289+
</ResizablePanel>
290+
<ResizableHandle withHandle />
291+
<ResizablePanel defaultSize={75}>
292+
{activeFile ? (
293+
<PdfViewer file={activeFile} key={activeFile.id} />
294+
) : (
295+
<WelcomePanel />
296+
)}
297+
</ResizablePanel>
298+
</ResizablePanelGroup>
299+
</div>
300+
</div>
268301
</div>
269302
);
270303
}

app/(main)/academics/AcademicsFileSearch.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -133,32 +133,32 @@ export default function AcademicsFileSearch({
133133
<>
134134
<Button
135135
variant="outline"
136-
className="relative w-[200px] justify-start text-sm text-muted-foreground"
136+
className="relative w-full md:w-[260px] justify-start text-sm text-muted-foreground font-normal"
137137
onClick={() => setOpen(true)}
138138
>
139-
<Search className="mr-2 h-4 w-4" />
139+
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
140140
<span>Search files...</span>
141141
<kbd className="pointer-events-none absolute right-1.5 top-1.5 hidden h-6 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
142142
<span className="text-xs"></span>K
143143
</kbd>
144144
</Button>
145145
<Dialog open={open} onOpenChange={setOpen}>
146-
<DialogContent className="p-0 max-w-2xl">
147-
<DialogHeader className="px-4 pt-4 pb-2">
146+
<DialogContent className="p-0 gap-0 w-[95vw] sm:w-full max-w-2xl max-h-[85vh] rounded-xl flex flex-col overflow-hidden">
147+
<DialogHeader className="px-4 pt-4 pb-2 shrink-0 border-b bg-muted/10">
148148
<div className="flex items-center justify-between gap-4">
149-
<div className="flex-1 min-w-0">
150-
<DialogTitle>Search Files</DialogTitle>
151-
<DialogDescription>
152-
Search across all files globally
149+
<div className="flex-1 min-w-0 text-left">
150+
<DialogTitle className="text-lg tracking-tight">Search Files</DialogTitle>
151+
<DialogDescription className="text-xs sm:text-sm truncate">
152+
Global file search
153153
{totalResults > 0 && (
154-
<span className="ml-2 text-primary font-medium">
155-
{totalResults} result{totalResults !== 1 ? "s" : ""} found
154+
<span className="ml-1.5 text-primary font-medium">
155+
{totalResults} found
156156
</span>
157157
)}
158158
</DialogDescription>
159159
</div>
160160
<Select value={resultLimit} onValueChange={setResultLimit}>
161-
<SelectTrigger className="w-[110px] h-8 text-xs focus:ring-0 focus:ring-offset-0 mr-8">
161+
<SelectTrigger className="w-[90px] h-7 text-xs mr-6 bg-background/50">
162162
<SelectValue />
163163
</SelectTrigger>
164164
<SelectContent>
@@ -171,13 +171,14 @@ export default function AcademicsFileSearch({
171171
</Select>
172172
</div>
173173
</DialogHeader>
174-
<Command shouldFilter={false}>
174+
<Command shouldFilter={false} className="flex-1 flex flex-col overflow-hidden">
175175
<CommandInput
176176
placeholder="Type to search files..."
177177
value={searchQuery}
178178
onValueChange={setSearchQuery}
179+
className="border-none focus:ring-0"
179180
/>
180-
<CommandList className="max-h-[400px]">
181+
<CommandList className="flex-1 overflow-y-auto">
181182
{isLoading && (
182183
<div className="flex items-center justify-center p-8">
183184
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />

app/(main)/academics/PdfViewer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export default function PdfViewer({ file }: PdfViewerProps) {
7878
{/* Transparent header */}
7979
<header className="absolute top-0 left-0 right-0 z-10 flex h-14 items-center justify-between px-4 bg-background/0 backdrop-blur-xs">
8080
<div className="flex items-center gap-3 min-w-0">
81-
<h3 className="font-semibold text-lg truncate text-foreground">
81+
<h3 className="font-semibold text-lg truncate text-background md:text-foreground">
8282
{file.fileName || "Loading..."}
8383
</h3>
8484
</div>

app/(main)/layout.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import { Header } from "@/components/layout/header";
55
import { Sidebar } from "@/components/layout/sidebar";
66
import { AnnouncementBanner } from "@/components/AnnouncementBanner";
77
import { cn, parseMajor } from "@/lib/utils";
8+
import { X } from "lucide-react";
9+
import { Button } from "@/components/ui/button";
810

911
interface LayoutProps {
1012
children: React.ReactNode;
1113
}
1214

1315
const Layout = ({ children }: LayoutProps) => {
1416
const [isCollapsed, setIsCollapsed] = useState(false);
17+
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
1518

1619
useEffect(() => {
1720
try {
@@ -94,7 +97,7 @@ const Layout = ({ children }: LayoutProps) => {
9497
{/* Sidebar */}
9598
<aside
9699
className={cn(
97-
"fixed inset-y-0 left-0 z-10 hidden flex-col border-r transition-all duration-300 ease-in-out sm:flex",
100+
"fixed inset-y-0 left-0 z-10 hidden flex-col border-r transition-all duration-300 ease-in-out md:flex",
98101
"bg-background/95 backdrop-blur-sm",
99102
isCollapsed ? "w-20" : "w-72"
100103
)}
@@ -106,7 +109,7 @@ const Layout = ({ children }: LayoutProps) => {
106109
<div
107110
className={cn(
108111
"flex flex-col flex-1 w-full transition-all duration-300 ease-in-out",
109-
isCollapsed ? "sm:pl-20" : "sm:pl-72"
112+
isCollapsed ? "md:pl-20" : "md:pl-72"
110113
)}
111114
>
112115
{/* <AnnouncementBanner
@@ -116,11 +119,38 @@ const Layout = ({ children }: LayoutProps) => {
116119
showAlways={true} // Set to true to show every time page loads
117120
118121
/> */}
119-
<Header isCollapsed={isCollapsed} setIsCollapsed={setIsCollapsed} />
122+
<Header
123+
isCollapsed={isCollapsed}
124+
setIsCollapsed={setIsCollapsed}
125+
onOpenMobileMenu={() => setIsMobileMenuOpen(true)}
126+
/>
120127
<main className="flex-1 p-6">
121128
<div className="mx-auto">{children}</div>
122129
</main>
123130
</div>
131+
132+
{/* Mobile Sidebar Overlay */}
133+
{isMobileMenuOpen && (
134+
<div className="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm md:hidden">
135+
<div className="fixed inset-y-0 left-0 z-50 w-72 h-full border-r bg-background shadow-lg transition-transform duration-300 ease-in-out transform translate-x-0">
136+
<div className="relative h-full flex flex-col">
137+
<Button
138+
variant="ghost"
139+
size="icon"
140+
className="absolute right-2 top-4 z-50"
141+
onClick={() => setIsMobileMenuOpen(false)}
142+
>
143+
<X className="h-5 w-5" />
144+
</Button>
145+
<div className="flex-1 overflow-y-auto">
146+
<Sidebar isCollapsed={false} onItemClick={() => setIsMobileMenuOpen(false)} />
147+
</div>
148+
</div>
149+
</div>
150+
{/* Click outside to close */}
151+
<div className="absolute inset-0" onClick={() => setIsMobileMenuOpen(false)} />
152+
</div>
153+
)}
124154
</div>
125155
);
126156
};

app/page.tsx

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ import {
77
} from "@/components/ui/card";
88
import { Badge } from "@/components/ui/badge";
99
import Link from "next/link";
10-
import { ModeToggle } from "@/components/toggle-theme";
11-
import Logo from "@/components/logo";
1210
import { getBranches } from "@/lib/api/branch";
13-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
14-
import TelegramIcon from "@/components/telegram";
11+
import { HomeHeader } from "@/components/home-header";
1512

1613

1714
export const revalidate = 86400; // 24h ISR
@@ -36,32 +33,7 @@ export default async function Home() {
3633

3734

3835
{/* Fixed Logo in Top-Left Corner */}
39-
<div className="absolute top-4 px-4 flex flex-row justify-between w-full z-10">
40-
<Link href="/" className="flex items-center gap-2 font-semibold">
41-
<Logo />
42-
<span>Learnverse</span>
43-
</Link>
44-
45-
<div className="flex items-center gap-2">
46-
<TooltipProvider>
47-
<Tooltip delayDuration={0}>
48-
<TooltipTrigger asChild>
49-
<Link
50-
href="https://t.me/+oxCuF0R1UIwwYTY1"
51-
target="_blank"
52-
rel="noopener noreferrer"
53-
className="flex h-9 w-9 items-center justify-center"
54-
>
55-
<TelegramIcon className="h-5 w-5" />
56-
<span className="sr-only">Join Telegram channel</span>
57-
</Link>
58-
</TooltipTrigger>
59-
<TooltipContent>Join Telegram channel</TooltipContent>
60-
</Tooltip>
61-
</TooltipProvider>
62-
<ModeToggle />
63-
</div>
64-
</div>
36+
<HomeHeader />
6537

6638
<div className="container max-w-4xl text-center">
6739
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl md:text-6xl">
@@ -78,19 +50,19 @@ export default async function Home() {
7850

7951

8052
{/* Clickable Year Cards */}
81-
<div className="mt-12 grid grid-cols-1 gap-4 sm:grid-cols-3">
53+
<div className="mt-8 grid grid-cols-1 gap-3 sm:mt-12 sm:grid-cols-3 sm:gap-4">
8254
{branches.map((branch, index) => (
83-
<Link href={`/academics`} key={branch._id}>
84-
<Card className="group h-full transform transition-transform duration-200 hover:-translate-y-1 hover:border-primary/60">
85-
<CardHeader>
55+
<Link href={`/academics`} key={branch._id} className="w-full">
56+
<Card className="group h-full transform transition-transform duration-200 hover:-translate-y-1 hover:border-primary/60 p-2 sm:p-0">
57+
<CardHeader className="p-4 sm:p-6">
8658
<div className="flex items-center justify-between">
87-
<div className="flex items-center gap-3">
88-
<GraduationCap className="h-5 w-5 text-muted-foreground group-hover:text-primary" />
89-
<CardTitle className="text-lg">{branch.code}</CardTitle>
59+
<div className="flex items-center gap-2 sm:gap-3">
60+
<GraduationCap className="h-4 w-4 sm:h-5 sm:w-5 text-muted-foreground group-hover:text-primary" />
61+
<CardTitle className="text-base sm:text-lg">{branch.code}</CardTitle>
9062
</div>
91-
<ArrowRight className="h-5 w-5 text-muted-foreground opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
63+
<ArrowRight className="h-4 w-4 sm:h-5 sm:w-5 text-muted-foreground opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
9264
</div>
93-
<CardDescription className="pt-2 text-left">
65+
<CardDescription className="pt-1 sm:pt-2 text-left text-xs sm:text-sm">
9466
{branch.name.toLowerCase() + " - " + descriptions[index]}
9567
</CardDescription>
9668
</CardHeader>
@@ -103,17 +75,18 @@ export default async function Home() {
10375

10476

10577
{/* Subtle Footer with Core Offerings */}
106-
<div className="mt-8 flex justify-center gap-x-4">
107-
<Badge variant="secondary">
108-
<BadgeCheckIcon />
78+
{/*hide this for mobile screens */}
79+
<div className="mt-8 flex flex-row items-center justify-center gap-2 sm:flex-row sm:gap-x-4 hidden md:block">
80+
<Badge variant="secondary" className="px-3 py-1 text-xs font-medium sm:text-sm">
81+
<BadgeCheckIcon className="mr-1 h-3 w-3 sm:h-4 sm:w-4" />
10982
Comprehensive Notes
11083
</Badge>
111-
<Badge variant="secondary">
112-
<BadgeCheckIcon />
84+
<Badge variant="secondary" className="px-3 py-1 text-xs font-medium sm:text-sm">
85+
<BadgeCheckIcon className="mr-1 h-3 w-3 sm:h-4 sm:w-4" />
11386
Past Exam Papers
11487
</Badge>
115-
<Badge variant="secondary">
116-
<BadgeCheckIcon />
88+
<Badge variant="secondary" className="px-3 py-1 text-xs font-medium sm:text-sm">
89+
<BadgeCheckIcon className="mr-1 h-3 w-3 sm:h-4 sm:w-4" />
11790
Reference Books
11891
</Badge>
11992
</div>

0 commit comments

Comments
 (0)