Skip to content

Commit 32a3117

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feature/fileupload
the commit. Merged origin into PR to fix build issues
2 parents e40418f + a35c08f commit 32a3117

File tree

11 files changed

+525
-37
lines changed

11 files changed

+525
-37
lines changed

.env.example

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/app/api/reports/route.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// This file defines the API route for POST /api/reports
2+
// It lets logged-in users submit a report for a specific file.
3+
4+
import { NextResponse } from 'next/server'; // Used to send HTTP responses (JSON, status codes)
5+
import { z } from 'zod'; // Zod validates and parses input data
6+
7+
// Import database connection and the "report" table schema
8+
import { db } from '@src/server/db';
9+
import { report } from '@src/server/db/schema/reports';
10+
11+
// Import session helper to check if a user is logged in
12+
import { getServerAuthSession } from '@src/server/auth';
13+
14+
// This ensures the API only accepts the correct fields
15+
const CreateReportSchema = z.object({
16+
fileId: z.string().min(1),
17+
category: z
18+
.enum(['inappropriate', 'copyright', 'spam', 'other'])
19+
.default('other'),
20+
details: z.string().min(1),
21+
});
22+
23+
// This function runs when someone sends a POST request to /api/reports
24+
export async function POST(req: Request) {
25+
const session = await getServerAuthSession();
26+
27+
// If there’s no session or no user ID, reject the request
28+
if (!session?.user?.id) {
29+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
30+
}
31+
32+
// Try to parse and validate the incoming request body
33+
let body: z.infer<typeof CreateReportSchema>;
34+
try {
35+
body = CreateReportSchema.parse(await req.json());
36+
} catch (e) {
37+
return NextResponse.json(
38+
{ error: 'Invalid body', details: (e as Error).message },
39+
{ status: 400 },
40+
);
41+
}
42+
43+
// Insert the validated report into the database
44+
const [created] = await db
45+
.insert(report)
46+
.values({
47+
userId: session.user.id,
48+
fileId: body.fileId,
49+
category: body.category,
50+
details: body.details,
51+
})
52+
.returning(); // return the newly created record
53+
54+
// Send the inserted report back as JSON
55+
return NextResponse.json(created, { status: 201 });
56+
}

src/app/layout.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
55
import { Bai_Jamjuree, Inter } from 'next/font/google';
66
import { type Metadata } from 'next';
77
import { GoogleAnalytics } from '@next/third-parties/google';
8+
import Link from 'next/link';
89

910
import theme from '@src/utils/theme';
11+
import { ToastProvider } from '@src/components/toast/ToastProvider';
1012

1113
const inter = Inter({
1214
subsets: ['latin'],
@@ -54,11 +56,18 @@ export default function RootLayout({
5456
className={`bg-white dark:bg-black ${inter.variable} font-main ${baiJamjuree.variable} text-haiti dark:text-white`}
5557
>
5658
<AppRouterCacheProvider>
57-
<ThemeProvider theme={theme}>{children}</ThemeProvider>
59+
<ThemeProvider theme={theme}>
60+
<ToastProvider>{children}</ToastProvider>
61+
</ThemeProvider>
5862
</AppRouterCacheProvider>
5963
{process.env.NEXT_PUBLIC_VERCEL_ENV === 'production' && (
6064
<GoogleAnalytics gaId="G-3NDS0P32CZ" />
6165
)}
66+
<nav>
67+
<Link href="/profile" className="px-3 py-1 hover:underline">
68+
Profile
69+
</Link>
70+
</nav>
6271
</body>
6372
</html>
6473
);

src/app/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Metadata } from 'next';
2+
import NavBar from '@components/NavBar';
23

34
export const metadata: Metadata = {
45
alternates: {
@@ -7,7 +8,11 @@ export const metadata: Metadata = {
78
};
89

910
const Home = () => {
10-
return 'UTD Notebook';
11+
return (
12+
<>
13+
<NavBar />
14+
</>
15+
);
1116
};
1217

1318
export default Home;

src/app/profile/page.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use client';
2+
3+
import Avatar from '@mui/material/Avatar';
4+
import Link from 'next/link';
5+
6+
export default function ProfilePage() {
7+
const user = {
8+
name: 'John Doe',
9+
handle: '@johndoe',
10+
11+
avatar: '/images/avatar.jpg',
12+
posts: 0,
13+
reports_submitted: 0,
14+
};
15+
16+
return (
17+
<main className="min-h-screen p-8">
18+
<div className="mx-auto max-w-4xl">
19+
<div className="flex items-center gap-6">
20+
<Avatar
21+
alt={user.name}
22+
src={user.avatar}
23+
sx={{ width: 96, height: 96 }}
24+
/>
25+
<div>
26+
<h1 className="text-2xl font-semibold">{user.name}</h1>
27+
<div className="text-sm text-white">{user.handle}</div>
28+
<p className="mt-2 text-white">{user.email}</p>
29+
<div className="mt-4 flex gap-4 text-sm text-white">
30+
<div>
31+
<strong className="font-bold">{user.posts}</strong> posts
32+
</div>
33+
<div>
34+
<strong className="font-bold">{user.reports_submitted}</strong>{' '}
35+
reports submitted
36+
</div>
37+
</div>
38+
</div>
39+
<div className="ml-auto flex items-center gap-3">
40+
<Link
41+
href="/profile/edit"
42+
className="rounded-md px-4 py-2 text-white"
43+
>
44+
Edit Profile
45+
</Link>
46+
</div>
47+
</div>
48+
<h2 className="mt-10 text-xl font-semibold">Saved Notes:</h2>
49+
<h2 className="mt-10 text-xl font-semibold">Uploaded Notes:</h2>
50+
</div>
51+
</main>
52+
);
53+
}

src/components/NavBar.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import Link from 'next/link';
3+
import Image from 'next/image';
4+
import { IconButton, Tooltip } from '@mui/material';
5+
6+
export default function NavBar() {
7+
return (
8+
<>
9+
<div className="bg-darken relative flex flex-wrap items-center gap-x-2 gap-y-0 overflow-hidden border-b-1 px-4 py-1 sm:flex-nowrap md:gap-x-4 md:px-8 md:py-1 lg:gap-x-8 lg:px-16">
10+
<Image
11+
src={'/background.png'}
12+
alt="background"
13+
className="-z-10 object-cover"
14+
fill
15+
/>
16+
17+
<Link
18+
href="/"
19+
className="font-display flex items-center gap-2 text-lg font-medium md:text-xl md:font-bold"
20+
>
21+
UTD Notebook
22+
</Link>
23+
24+
<div className="ml-auto flex items-center gap-x-2 md:gap-x-4">
25+
<Tooltip title="Profile">
26+
<IconButton size="medium" href="/profile">
27+
<div className="relative size-10 flex-shrink-0 md:size-12">
28+
<Image
29+
src="/icon-white.svg"
30+
alt="profile picture"
31+
fill
32+
className="rounded-full border-2 border-white object-cover"
33+
/>
34+
</div>
35+
</IconButton>
36+
</Tooltip>
37+
</div>
38+
</div>
39+
</>
40+
);
41+
}

0 commit comments

Comments
 (0)