From 2cf442fa6d2559bf0ab86265fff1094feafe1d52 Mon Sep 17 00:00:00 2001 From: kiet08hogit Date: Sat, 20 Jun 2026 08:20:56 -0500 Subject: [PATCH] landing page modification --- frontend/app/listings/[id]/edit/page.tsx | 641 +++++----- frontend/app/listings/[id]/page.tsx | 1469 ++++++++++++---------- frontend/app/listings/page.tsx | 466 +++---- frontend/app/page.tsx | 434 ++++++- frontend/components/CustomUserButton.tsx | 2 +- frontend/public/UIC.webp | Bin 0 -> 73172 bytes frontend/public/UIUC.jpg | Bin 0 -> 250797 bytes 7 files changed, 1774 insertions(+), 1238 deletions(-) create mode 100644 frontend/public/UIC.webp create mode 100644 frontend/public/UIUC.jpg diff --git a/frontend/app/listings/[id]/edit/page.tsx b/frontend/app/listings/[id]/edit/page.tsx index a66bfa5..e0e17ed 100644 --- a/frontend/app/listings/[id]/edit/page.tsx +++ b/frontend/app/listings/[id]/edit/page.tsx @@ -1,327 +1,376 @@ -'use client'; +"use client"; -import { useState, useEffect } from 'react'; -import { useAuth } from '@clerk/nextjs'; -import { useRouter, useParams } from 'next/navigation'; -import { motion } from 'framer-motion'; -import { ArrowLeft, Tag, AlignLeft, DollarSign, ListPlus, Loader2, ImagePlus, AlertCircle } from 'lucide-react'; -import Link from 'next/link'; -import axios from 'axios'; -import { Input } from '@/components/ui/input'; -import { Textarea } from '@/components/ui/textarea'; -import { Button } from '@/components/ui/button'; -import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { useState, useEffect } from "react"; +import { useAuth } from "@clerk/nextjs"; +import { useRouter, useParams } from "next/navigation"; +import { motion } from "framer-motion"; +import { + ArrowLeft, + Tag, + AlignLeft, + DollarSign, + ListPlus, + Loader2, + ImagePlus, + AlertCircle, +} from "lucide-react"; +import Link from "next/link"; +import axios from "axios"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Button } from "@/components/ui/button"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; const getImageUrl = (url?: string) => { - if (!url) return ""; - if (url.startsWith("http")) return url; - return `http://127.0.0.1:3000${url}`; + if (!url) return ""; + if (url.startsWith("http")) return url; + return `http://127.0.0.1:3000${url}`; }; export default function EditListingPage() { - const { getToken, isLoaded, isSignedIn } = useAuth(); - const router = useRouter(); - const params = useParams(); - const listingId = params.id as string; + const { getToken, isLoaded, isSignedIn } = useAuth(); + const router = useRouter(); + const params = useParams(); + const listingId = params.id as string; - const [isLoading, setIsLoading] = useState(true); - const [isSaving, setIsSaving] = useState(false); - const [error, setError] = useState(''); - const [images, setImages] = useState<{ id: string; url: string }[]>([]); + const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState(""); + const [images, setImages] = useState<{ id: string; url: string }[]>([]); - const [formData, setFormData] = useState({ - title: '', - description: '', - price: '', - category: 'SCHOOL', - status: 'ACTIVE', - }); + const [formData, setFormData] = useState({ + title: "", + description: "", + price: "", + category: "SCHOOL", + status: "ACTIVE", + }); - const categories = ['SCHOOL', 'CLOTHES', 'HOUSING', 'LEISURE', 'ACCESSORIES', 'OTHER']; + const categories = [ + "SCHOOL", + "CLOTHES", + "HOUSING", + "LEISURE", + "ACCESSORIES", + "OTHER", + ]; - useEffect(() => { - if (!isLoaded) return; - if (!isSignedIn) { - router.push('/'); - return; - } + useEffect(() => { + if (!isLoaded) return; + if (!isSignedIn) { + router.push("/"); + return; + } - const fetchListing = async () => { - try { - const token = await getToken(); - const res = await axios.get(`http://127.0.0.1:3000/listings/${listingId}`, { - headers: { Authorization: `Bearer ${token}` } - }); - const listing = res.data; - setFormData({ - title: listing.title || '', - description: listing.description || '', - price: listing.price?.toString() || '', - category: listing.category || 'SCHOOL', - status: listing.status || 'ACTIVE', - }); + const fetchListing = async () => { + try { + const token = await getToken(); + const res = await axios.get( + `http://127.0.0.1:3000/listings/${listingId}`, + { + headers: { Authorization: `Bearer ${token}` }, + }, + ); + const listing = res.data; + setFormData({ + title: listing.title || "", + description: listing.description || "", + price: listing.price?.toString() || "", + category: listing.category || "SCHOOL", + status: listing.status || "ACTIVE", + }); - if (listing.images) { - setImages(listing.images.map((img: any) => ({ - id: img.id, - url: getImageUrl(img.url) - }))); - } - } catch (err: any) { - console.error('Failed to fetch listing', err); - setError('Could not load listing data. It may have been deleted or you do not have permission.'); - } finally { - setIsLoading(false); - } - }; + if (listing.images) { + setImages( + listing.images.map((img: any) => ({ + id: img.id, + url: getImageUrl(img.url), + })), + ); + } + } catch (err: any) { + console.error("Failed to fetch listing", err); + setError( + "Could not load listing data. It may have been deleted or you do not have permission.", + ); + } finally { + setIsLoading(false); + } + }; - fetchListing(); - }, [listingId, isLoaded, isSignedIn, getToken, router]); + fetchListing(); + }, [listingId, isLoaded, isSignedIn, getToken, router]); - const handleChange = (e: React.ChangeEvent) => { - setFormData({ ...formData, [e.target.name]: e.target.value }); - }; + const handleChange = ( + e: React.ChangeEvent, + ) => { + setFormData({ ...formData, [e.target.name]: e.target.value }); + }; - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setIsSaving(true); - setError(''); + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsSaving(true); + setError(""); - try { - const token = await getToken(); - // The updateListing endpoint expects a JSON body (CreateListingDto) - await axios.put(`http://127.0.0.1:3000/listings/${listingId}`, { - title: formData.title, - description: formData.description, - price: Number(formData.price), - category: formData.category, - status: formData.status - }, { - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - }); + try { + const token = await getToken(); + // The updateListing endpoint expects a JSON body (CreateListingDto) + await axios.put( + `http://127.0.0.1:3000/listings/${listingId}`, + { + title: formData.title, + description: formData.description, + price: Number(formData.price), + category: formData.category, + status: formData.status, + }, + { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }, + ); - router.push(`/listings/${listingId}`); - } catch (err: any) { - const errData = err.response?.data; - const backendMessage = errData?.message - ? Array.isArray(errData.message) - ? errData.message.join(', ') - : errData.message - : err.message || 'An error occurred while saving the listing.'; + router.push(`/listings/${listingId}`); + } catch (err: any) { + const errData = err.response?.data; + const backendMessage = errData?.message + ? Array.isArray(errData.message) + ? errData.message.join(", ") + : errData.message + : err.message || "An error occurred while saving the listing."; - setError(backendMessage); - } finally { - setIsSaving(false); - } - }; + setError(backendMessage); + } finally { + setIsSaving(false); + } + }; - if (isLoading || !isLoaded) { - return ( -
- -
- ); - } + if (isLoading || !isLoaded) { + return ( +
+ +
+ ); + } - return ( -
- - {/* Back Button */} - - - Back to Listing - + return ( +
+ + {/* Back Button */} + + + Back to Listing + - {/* Header */} -
-

- Edit Listing -

-

- Update the details of your marketplace offering. -

-
+ {/* Header */} +
+

+ Edit Listing +

+

+ Update the details of your marketplace offering. +

+
- {/* Form Card */} -
- {error && ( - - - Error - {error} - - )} + {/* Form Card */} +
+ {error && ( + + + Error + {error} + + )} -
- {/* ── Image Display (Read-Only) ── */} -
- + + {/* ── Image Display (Read-Only) ── */} +
+ - {images.length > 0 ? ( -
- {images.map((img, idx) => ( -
- {`Upload - {idx === 0 && ( -
- Cover -
- )} -
- ))} -
- ) : ( -
- No images attached to this listing. -
- )} -

- Image updates are currently disabled. You must delete and recreate the listing to change images. -

-
+ {images.length > 0 ? ( +
+ {images.map((img, idx) => ( +
+ {`Upload + {idx === 0 && ( +
+ Cover +
+ )} +
+ ))} +
+ ) : ( +
+ No images attached to this listing. +
+ )} +

+ Image updates are currently disabled. You must delete and + recreate the listing to change images. +

+
- {/* ── Title ── */} -
- - -
+ {/* ── Title ── */} +
+ + +
- {/* ── Description ── */} -
- -