Skip to content

Commit

Permalink
feat(dashboard-&-landingPage): completed project
Browse files Browse the repository at this point in the history
  • Loading branch information
51L3N7-X committed Nov 13, 2024
1 parent bc72aaa commit e2ac0c4
Show file tree
Hide file tree
Showing 24 changed files with 1,282 additions and 77 deletions.
54 changes: 54 additions & 0 deletions app/api/burgers/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import db from "@/db/db";
import { NextRequest, NextResponse } from "next/server";

export async function GET(
req: NextRequest,
{ params }: { params: { id: string } }
) {
const id = parseInt(params.id, 10);
await db.read();
const burger = db.data?.burgers.find((b) => b.id === id);

if (!burger) {
return NextResponse.json({ error: "Burger not found" }, { status: 404 });
}

return NextResponse.json(burger, { status: 200 });
}

export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const id = parseInt(params.id, 10);
const updatedData = await request.json();
await db.read();
const index = db.data?.burgers.findIndex((b) => b.id === id);

if (index === undefined || index === -1) {
return NextResponse.json({ error: "Burger not found" }, { status: 404 });
}

db.data!.burgers[index] = { ...db.data!.burgers[index], ...updatedData };
await db.write();

return NextResponse.json(db.data!.burgers[index], { status: 200 });
}

export async function DELETE(
_: Request,
{ params }: { params: { id: string } }
) {
const id = parseInt(params.id, 10);
await db.read();
const index = db.data?.burgers.findIndex((b) => b.id === id);

if (index === undefined || index === -1) {
return NextResponse.json({ error: "Burger not found" }, { status: 404 });
}

const [deletedBurger] = db.data!.burgers.splice(index, 1);
await db.write();

return NextResponse.json(deletedBurger, { status: 200 });
}
21 changes: 21 additions & 0 deletions app/api/burgers/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import db from "@/db/db";
import { Burger } from "@/types/Burger";
import { NextRequest, NextResponse } from "next/server";

export async function GET() {
await db.read();
return NextResponse.json(db.data?.burgers || [], { status: 200 });
}

export async function POST(request: NextRequest) {
const newBurger: Omit<Burger, "id"> = await request.json();
await db.read();
const id = Date.now();
const burger: Burger = { id, ...newBurger };
db.data?.burgers.push(burger);
await db.write();

return NextResponse.json(burger, { status: 201 });
}


23 changes: 23 additions & 0 deletions app/api/uploadthing/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { UploadThingError } from "uploadthing/server";

const f = createUploadthing();

const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function

// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({
image: { maxFileSize: "4MB", maxFileCount: 1 },
}).onUploadComplete(async ({ file }) => {
// This code RUNS ON YOUR SERVER after upload

console.log("file url", file.url);

// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { success: true };
}),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;
11 changes: 11 additions & 0 deletions app/api/uploadthing/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createRouteHandler } from "uploadthing/next";

import { ourFileRouter } from "./core";

// Export routes for Next App Router
export const { GET, POST } = createRouteHandler({
router: ourFileRouter,

// Apply an (optional) custom config:
// config: { ... },
});
45 changes: 45 additions & 0 deletions app/burger/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"use client";

import { Burger } from "@/types/Burger";
import { useQuery } from "@tanstack/react-query";
import Image from "next/image";
import React from "react";

export default function page({ params }: { params: { id: string } }) {
async function fetchBurger(): Promise<Burger> {
const response = await fetch(
`${window.location.origin}/api/burgers/${params.id}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
cache: "no-store",
}
);

if (!response.ok) throw new Error("Failed to fetch burgers");
return response.json();
}

const {
data: burger,
isLoading,
error,
} = useQuery({ queryKey: ["burger"], queryFn: fetchBurger });

if (isLoading) return <p>Loading...</p>;
return (
<div className="flex justify-center h-[89vw] items-center mx-auto mt-6 flex-col w-1/2">
<Image
src={burger?.imageURL || ""}
width={512}
height={512}
className="rounded-full"
alt="burger"
></Image>
<h1 className="font-bold text-5xl text-left my-8">{burger?.name}</h1>
<p className="text-3xl text-left my-6">{burger?.description}</p>
</div>
);
}
1 change: 0 additions & 1 deletion app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default async function DashboardLayout({
children: React.ReactNode;
}) {
const session = await getServerSession();
console.log("==================Session:", session);
if (!session || !session.user) {
redirect("/login");
}
Expand Down
135 changes: 123 additions & 12 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,129 @@
"use client";

import { useSession } from "next-auth/react";
// import { useRouter } from "next/navigation";
// import { useEffect } from "react";
import { Button } from "@/components/ui/button";
import { useQuery } from "@tanstack/react-query";
import { Burger } from "@/types/Burger";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import Image from "next/image";
import { Pencil, Trash } from "lucide-react";
import { useState } from "react";
import { AddOrEditItem } from "@/components/AddItemDialog";
import { DeleteDialog } from "@/components/DeleteDialog";

export default function DashboardPage() {
const { data: session } = useSession();

if (session) {
return (
<div>
<h1>Welcome to the Dashboard, {session!.user?.name}</h1>
<p>Your token : ${session?.accessToken}</p>
</div>
);
const [isAddOpen, setIsAddOpen] = useState(false);
const [editingBurger, setEditingBurger] = useState<Burger | null>(null);
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
const [burgerIdToDelete, setBurgerIdToDelete] = useState<string | null>(null);

async function fetchBurgers(): Promise<Burger[]> {
const response = await fetch(`${window.location.origin}/api/burgers`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
cache: "no-store",
});

if (!response.ok) throw new Error("Failed to fetch burgers");
return response.json();
}

const {
data: burgers,
isLoading,
error,
} = useQuery({ queryKey: ["burgers"], queryFn: fetchBurgers });

if (isLoading) return <p>Loading burgers...</p>;
if (error) return <p>Error loading burgers</p>;

// Open dialog for adding new item
const handleAddClick = () => {
setEditingBurger(null);
setIsAddOpen(true);
};

// Open dialog for editing an existing item
const handleEditClick = (burger: Burger) => {
setEditingBurger(burger);
setIsAddOpen(true);
};

// Open delete dialog and set burger to be deleted
const handleDeleteClick = (burgerId: string) => {
setBurgerIdToDelete(burgerId);
setIsDeleteOpen(true);
};

return (
<div className="container mx-auto p-4">
<h1 className="text-2xl font-semibold mb-4">Burgers Menu</h1>
<Button className="mb-4" onClick={handleAddClick}>
Add Item
</Button>
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Name</TableHead>
<TableHead>Description</TableHead>
<TableHead>Image</TableHead>
<TableHead>Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{burgers?.map((burger: Burger) => (
<TableRow key={burger.id}>
<TableCell>{burger.id}</TableCell>
<TableCell>{burger.name}</TableCell>
<TableCell>{burger.description}</TableCell>
<TableCell>
<Image
src={burger.imageURL || ""}
alt={burger.name}
className="w-16 h-16 object-cover rounded-full"
width={64}
height={64}
/>
</TableCell>
<TableCell className="flex items-center justify-center h-16 w-16 gap-2">
<Pencil
size={16}
className="cursor-pointer"
onClick={() => handleEditClick(burger)}
/>
<Trash
size={16}
className="cursor-pointer"
onClick={() => handleDeleteClick(String(burger.id))}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>

{/* Dialog for Add or Edit */}
<AddOrEditItem
open={isAddOpen}
setOpen={setIsAddOpen}
burgerData={editingBurger}
/>

{/* Delete confirmation dialog */}
<DeleteDialog
open={isDeleteOpen}
setOpen={setIsDeleteOpen}
burgerId={burgerIdToDelete}
/>
</div>
);
}
21 changes: 12 additions & 9 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import localFont from "next/font/local";
import "./globals.css";
import { NavBar } from "@/components/navbar";
import { ThemeProvider } from "@/components/theme-provider";
import ClientProviders from "@/components/ClientProviders";

const geistSans = localFont({
src: "./fonts/GeistVF.woff",
Expand Down Expand Up @@ -30,15 +31,17 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<NavBar />
{children}
</ThemeProvider>
<ClientProviders>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<NavBar />
{children}
</ThemeProvider>
</ClientProviders>
</body>
</html>
);
Expand Down
Loading

0 comments on commit e2ac0c4

Please sign in to comment.