From e6b08299fbf9c0520d949bdcf6fec16dba03660d Mon Sep 17 00:00:00 2001 From: Zaid Abdulameer Date: Sat, 8 Feb 2025 21:47:43 -0500 Subject: [PATCH 1/5] Start admin page --- app/admin/AddProduct.tsx | 49 +++++++++++++++++++++++ app/admin/Inventory.tsx | 85 ++++++++++++++++++++++++++++++++++++++++ app/admin/Orders.tsx | 34 ++++++++++++++++ app/admin/page.tsx | 14 +++++++ 4 files changed, 182 insertions(+) create mode 100644 app/admin/AddProduct.tsx create mode 100644 app/admin/Inventory.tsx create mode 100644 app/admin/Orders.tsx create mode 100644 app/admin/page.tsx diff --git a/app/admin/AddProduct.tsx b/app/admin/AddProduct.tsx new file mode 100644 index 0000000..17b4d8e --- /dev/null +++ b/app/admin/AddProduct.tsx @@ -0,0 +1,49 @@ +export default function AddProduct() { + return ( +
+

Add Product

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ ) +} + diff --git a/app/admin/Inventory.tsx b/app/admin/Inventory.tsx new file mode 100644 index 0000000..ec2b310 --- /dev/null +++ b/app/admin/Inventory.tsx @@ -0,0 +1,85 @@ +'use client' +import { useState } from "react" + +interface InventoryItem { + id: number + name: string + stock: number + price: number +} + +const initialInventoryData: InventoryItem[] = [ + { id: 1, name: "Product 1", stock: 50, price: 19.99 }, + { id: 2, name: "Product 2", stock: 30, price: 29.99 }, + { id: 3, name: "Product 3", stock: 20, price: 39.99 }, +] + +export default function Inventory() { + const [inventoryData, setInventoryData] = useState(initialInventoryData) + const [editingId, setEditingId] = useState(null) + + const handleStockChange = (id: number, newStock: number) => { + setInventoryData(inventoryData.map((item) => (item.id === id ? { ...item, stock: newStock } : item))) + } + + const handleSave = (id: number) => { + console.log(`Saving stock update for product ${id}`) + setEditingId(null) + } + + return ( +
+

Inventory

+ + + + + + + + + + + + {inventoryData.map((item) => ( + + + + + + + + ))} + +
IDNameStockPriceActions
{item.id}{item.name} + {editingId === item.id ? ( + handleStockChange(item.id, Number.parseInt(e.target.value))} + className="w-20 p-1 border rounded" + /> + ) : ( + item.stock + )} + ${item.price.toFixed(2)} + {editingId === item.id ? ( + + ) : ( + + )} +
+
+ ) +} + diff --git a/app/admin/Orders.tsx b/app/admin/Orders.tsx new file mode 100644 index 0000000..b600e46 --- /dev/null +++ b/app/admin/Orders.tsx @@ -0,0 +1,34 @@ +const ordersData = [ + { id: 1, customer: "John Doe", total: 59.98, status: "Shipped" }, + { id: 2, customer: "Jane Smith", total: 89.97, status: "Processing" }, + { id: 3, customer: "Bob Johnson", total: 39.99, status: "Delivered" }, +] + +export default function Orders() { + return ( +
+

Recent Orders

+ + + + + + + + + + + {ordersData.map((order) => ( + + + + + + + ))} + +
Order IDCustomerTotalStatus
{order.id}{order.customer}${order.total.toFixed(2)}{order.status}
+
+ ) +} + diff --git a/app/admin/page.tsx b/app/admin/page.tsx new file mode 100644 index 0000000..4334b86 --- /dev/null +++ b/app/admin/page.tsx @@ -0,0 +1,14 @@ +import Inventory from "./Inventory" +import AddProduct from "./AddProduct" +import Orders from "./Orders" + +export default function AdminPage() { + return ( +
+

Admin Dashboard

+ + + +
+ ) +} \ No newline at end of file From b1e32810cff02274fada771a128a1a2149bf1664 Mon Sep 17 00:00:00 2001 From: Zaid Abdulameer Date: Sat, 22 Feb 2025 13:34:53 -0500 Subject: [PATCH 2/5] Added editing with image upload to buckets --- app/admin/AddProduct.tsx | 176 +++++++++++++++++++++++++++++++++--- app/admin/Inventory.tsx | 188 +++++++++++++++++++++++++++++++-------- app/admin/page.tsx | 14 ++- app/api/upload/route.ts | 35 ++++++++ 4 files changed, 364 insertions(+), 49 deletions(-) create mode 100644 app/api/upload/route.ts diff --git a/app/admin/AddProduct.tsx b/app/admin/AddProduct.tsx index 17b4d8e..3403277 100644 --- a/app/admin/AddProduct.tsx +++ b/app/admin/AddProduct.tsx @@ -1,46 +1,196 @@ +"use client" + +import { useState, type ChangeEvent } from "react" +import type { Bike } from "@/utils/getBike" +import type React from "react" // Import React + export default function AddProduct() { + const [newBike, setNewBike] = useState>({ + name: "", + description: "", + image: null, + amount_stocked: 0, + rental_rate: 0, + sell_price: 0, + damage_rate: 0, + for_rent: false, + }) + const [uploading, setUploading] = useState(false) + + const handleChange = (field: keyof Omit, value: string | number | boolean) => { + setNewBike((prev) => ({ ...prev, [field]: value })) + } + + const handleImageUpload = async (e: ChangeEvent) => { + if (!e.target.files || e.target.files.length === 0) { + console.error("No file selected") + return + } + + const file = e.target.files[0] + const formData = new FormData() + formData.append("file", file) + + setUploading(true) + + try { + const response = await fetch("/api/upload", { + method: "POST", + body: formData, + }) + + if (!response.ok) { + throw new Error("Upload failed") + } + + const data = await response.json() + + if (data.success) { + handleChange("image", data.filename) + } else { + console.error("Upload failed") + } + } catch (error) { + console.error("Error uploading file:", error) + } finally { + setUploading(false) + } + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + console.log("New bike to be added:", newBike) + // Here you would typically send the data to your backend API + // Reset the form after submission + setNewBike({ + name: "", + description: "", + image: null, + amount_stocked: 0, + rental_rate: 0, + sell_price: 0, + damage_rate: 0, + for_rent: false, + }) + } + return (
-

Add Product

-
+

Add New Bike

+
handleChange("name", e.target.value)} + className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" + required + /> +
+
+ +