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
38 changes: 32 additions & 6 deletions src/app/notes/[...slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import EmptyStateCard from '@src/components/sections/EmptyStateCard';
import FilesGrid from '@src/components/sections/FilesGrid';
import HandwrittenFilter from '@src/components/sections/Filters';
import LinkCard from '@src/components/sections/LinkCard';
import SectionHeader from '@src/components/sections/SectionHeader';
import type { SectionWithFilesWithUserMetadata } from '@src/server/db/models';
Expand All @@ -13,6 +14,7 @@ import {

type NotesPageProps = {
params: Promise<{ slug: string[] }>;
searchParams: Promise<{ [key: string]: string }>;
};

async function fetchSections(
Expand Down Expand Up @@ -111,8 +113,26 @@ function getCourseLinks(
}));
}

export default async function NotesPage({ params }: NotesPageProps) {
const { slug } = await params;
function filterSections(
sections: SectionWithFilesWithUserMetadata[],
handwritten: string | undefined,
): SectionWithFilesWithUserMetadata[] {
if (handwritten !== 'true' && handwritten !== 'false') return sections;
const isHandwritten = handwritten === 'true';
return sections.map((s) => ({
...s,
files: s.files.filter((f) => f.handwritten === isHandwritten),
}));
}

export default async function NotesPage({
params,
searchParams,
}: NotesPageProps) {
const [{ slug }, resolvedSearchParams] = await Promise.all([
params,
searchParams,
]);
const query = parseNoteSlug(slug);

if (!query) {
Expand All @@ -125,7 +145,11 @@ export default async function NotesPage({ params }: NotesPageProps) {
}

const sections = await fetchSections(query);
const fileCount = totalFileCount(sections);
const filteredSections = filterSections(
sections,
resolvedSearchParams.handwritten,
);
const fileCount = totalFileCount(filteredSections);

return (
<>
Expand All @@ -136,6 +160,8 @@ export default async function NotesPage({ params }: NotesPageProps) {
breadcrumbs={buildBreadcrumbs(query)}
/>

<HandwrittenFilter />

{fileCount === 0 ? (
<EmptyStateCard
title="No notes yet"
Expand All @@ -149,8 +175,8 @@ export default async function NotesPage({ params }: NotesPageProps) {
{(() => {
const links =
query.type === 'course'
? getProfessorLinks(sections, query)
: getCourseLinks(sections, query);
? getProfessorLinks(filteredSections, query)
: getCourseLinks(filteredSections, query);
if (links.length <= 1) return null;
return (
<div className="col-span-full">
Expand All @@ -176,7 +202,7 @@ export default async function NotesPage({ params }: NotesPageProps) {
)}

{/* Notes grouped by section */}
{sections.map((s) => (
{filteredSections.map((s) => (
<div key={s.id} className="col-span-full">
<h2 className="mb-3 text-lg font-semibold">
{s.prefix} {s.number}.{s.sectionCode} — {s.term} {s.year}
Expand Down
42 changes: 42 additions & 0 deletions src/components/sections/Filters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client';

import FormControl from '@mui/material/FormControl';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';

export default function HandwrittenFilter() {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();

const value = searchParams.get('handwritten') ?? '';

const handleChange = (newValue: string) => {
const params = new URLSearchParams(searchParams.toString());
if (newValue === '') {
params.delete('handwritten');
} else {
params.set('handwritten', newValue);
}
const query = params.toString();
router.push(query ? `${pathname}?${query}` : pathname);
};

return (
<FormControl size="small" className="w-48">
<InputLabel>Note type</InputLabel>
<Select
value={value}
label="Note type"
onChange={(e) => handleChange(e.target.value)}
className="bg-white dark:bg-neutral-800"
>
<MenuItem value="">All Notes</MenuItem>
<MenuItem value="true">Handwritten Only</MenuItem>
<MenuItem value="false">Digital Only</MenuItem>
</Select>
</FormControl>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE "file" ADD COLUMN "handwritten" boolean DEFAULT false NOT NULL;
3 changes: 3 additions & 0 deletions src/server/db/schema/file.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { relations, sql } from 'drizzle-orm';
import {
boolean,
index,
pgTable,
text,
Expand Down Expand Up @@ -30,6 +31,8 @@ export const file = pgTable(

publicUrl: text('public_url').notNull(),

handwritten: boolean('handwritten').notNull().default(false),

updatedAt: timestamp('updated_at').notNull().defaultNow(),
},
(t) => [
Expand Down
Loading