diff --git a/scruter-nextjs/actions/seller/listing.tsx b/scruter-nextjs/actions/seller/listing.tsx index abbcc524..918303dd 100644 --- a/scruter-nextjs/actions/seller/listing.tsx +++ b/scruter-nextjs/actions/seller/listing.tsx @@ -1,6 +1,6 @@ 'use server'; import prismadb from '@/lib/prismadb'; -import { Image, Listing } from '@prisma/client'; +import { Category, Image, Listing } from '@prisma/client'; export interface ListingWithImages extends Listing { images: Image[]; @@ -184,24 +184,65 @@ export async function DeleteListing({ } } -export async function GetAllListing(): Promise<{ + +export async function GetAllListing( + category?: Category, + searchQuery?: string, + sort?: "" | "asc" | "desc" | undefined +): Promise<{ success: boolean; error?: string; data?: ListingWithImages[]; }> { - const resp = await prismadb.listing.findMany({ - include: { - images: true, - }, - }); - - if (!resp) { - return { success: false, error: 'Error occured in fetching all listing' }; + // Set no-cache headers to ensure fresh data + const headers = new Headers(); + headers.append('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); + headers.append('Pragma', 'no-cache'); + headers.append('Expires', '0'); + + // Build the where clause based on provided parameters + let whereClause = {}; + + if (category) { + whereClause = { ...whereClause, category }; + } + + if (searchQuery) { + whereClause = { + ...whereClause, + OR: [ + { name: { contains: searchQuery, mode: 'insensitive' } }, + { description: { contains: searchQuery, mode: 'insensitive' } }, + ], + }; } - return { success: true, data: resp }; + try { + // Query the database for listings with optional filters and sorting + const resp: ListingWithImages[] = await prismadb.listing.findMany({ + where: whereClause, + include: { + images: true, + }, + orderBy: sort ? { price: sort } : undefined, // Sort by price if 'sort' is provided + }); + + if (!resp) { + return { success: false, error: 'Error occurred in fetching all listings' }; + } + + return { success: true, data: resp }; + } catch (error: unknown) { + // Narrowing the error type to handle it properly + if (error instanceof Error) { + return { success: false, error: error.message || 'An error occurred' }; + } else { + return { success: false, error: 'An unknown error occurred' }; + } + } } + export async function getSpecificListing({ listingId, }: { diff --git a/scruter-nextjs/app/(routes)/food/page.tsx b/scruter-nextjs/app/(routes)/food/page.tsx index c8e12f73..67cfd63a 100644 --- a/scruter-nextjs/app/(routes)/food/page.tsx +++ b/scruter-nextjs/app/(routes)/food/page.tsx @@ -5,22 +5,28 @@ import axios from 'axios'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSearch } from '@fortawesome/free-solid-svg-icons'; // Import FontAwesomeIcon import '../../globals.css'; // Ensure your global styles are imported +import { GetAllListing, ListingWithImages } from '@/actions/seller/listing'; +import { Listing } from '@prisma/client'; +import toast, { Toaster } from 'react-hot-toast'; +import ListingCardFE from '@/components/listingCardFE'; const FoodPage: React.FC = () => { - const [foodItems, setFoodItems] = useState([]); + const [foodItems, setFoodItems] = useState([]); const [loading, setLoading] = useState(true); const [query, setQuery] = useState(''); - const [type, setType] = useState('food'); // Default search type is 'food' - const [sort, setSort] = useState(''); + const [sort, setSort] = useState<"" | "asc" | "desc" | undefined>(''); useEffect(() => { const fetchData = async () => { setLoading(true); try { // Example API call to fetch food data - const foodResponse = await axios.get('/api/food', { - params: { query, type, sort }, - }); + const foodResponse = await GetAllListing('Fooding',query,sort); + + if (!foodResponse || !foodResponse.data) { + toast.error('No data fetched from BE'); + return; + } setFoodItems(foodResponse.data); } catch (error) { console.error('Error fetching food data:', error); @@ -29,11 +35,13 @@ const FoodPage: React.FC = () => { } }; - //fetchData(); - }, [query, type, sort]); + fetchData(); + }, [query, sort]); + console.log(foodItems); return (
+ {/* Hero Section with Banner Image */}
{ id="sort-by-price" name="sort" value={sort} - onChange={e => setSort(e.target.value)} + onChange={e => { + const value = e.target.value as "" | "asc" | "desc" | undefined; // Type the value to be one of these options + setSort(value); // Update the sort state + }} > - @@ -111,43 +121,49 @@ const FoodPage: React.FC = () => { {/* Food Items Section */}

Available Food

- {/* +
{loading ? (
) : ( - foodItems.map((food) => ( - + foodItems.map(food => ( + )) )} -
*/} +
); }; -const FoodCard: React.FC<{ food: any }> = ({ food }) => ( -
- {food.title} -

{food.title}

-

{food.description}

-

{food.price}

- - View Details - -
-); +// const FoodCard: React.FC<{ food: any }> = ({ food }) => ( +//
+// {food.title} +//

{food.title}

+//

{food.description}

+//

{food.price}

+// +// View Details +// +//
+// ); export default FoodPage; diff --git a/scruter-nextjs/app/(routes)/for-sale/page.tsx b/scruter-nextjs/app/(routes)/for-sale/page.tsx index f633586c..2d65ad57 100644 --- a/scruter-nextjs/app/(routes)/for-sale/page.tsx +++ b/scruter-nextjs/app/(routes)/for-sale/page.tsx @@ -1,39 +1,43 @@ 'use client'; import React, { useEffect, useState } from 'react'; -import axios from 'axios'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faSearch } from '@fortawesome/free-solid-svg-icons'; // Import FontAwesomeIcon -import '../../globals.css'; // Ensure your global styles are imported +import { faSearch } from '@fortawesome/free-solid-svg-icons'; +import toast, { Toaster } from 'react-hot-toast'; +import { GetAllListing } from '@/actions/seller/listing'; +import ListingCardFE from '@/components/listingCardFE'; const ForSalePage: React.FC = () => { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [query, setQuery] = useState(''); - const [type, setType] = useState('sale'); // Default search type is 'sale' - const [sort, setSort] = useState(''); + const [sort, setSort] = useState<'asc' | 'desc' | ''>(''); useEffect(() => { const fetchData = async () => { setLoading(true); try { - // Example API call to fetch for sale data - const response = await axios.get('/api/sale', { - params: { query, type, sort }, - }); - setItems(response.data); + // Fetching the items with search query and sort applied + const forSaleResp = await GetAllListing('For_Sale', query, sort); + + if (!forSaleResp || !forSaleResp.data) { + toast.error('No data fetched from BE'); + return; + } + setItems(forSaleResp.data); } catch (error) { - console.error('Error fetching sale items:', error); + console.error('Error fetching data:', error); } finally { setLoading(false); } }; - //fetchData(); - }, [query, type, sort]); + fetchData(); + }, [query, sort]); // Only depend on query and sort return (
+ {/* Hero Section with Banner Image */}
{ id="sort-by-price" name="sort" value={sort} - onChange={e => setSort(e.target.value)} + onChange={e => setSort(e.target.value as 'asc' | 'desc' | '')} > @@ -113,43 +117,30 @@ const ForSalePage: React.FC = () => {

Available Items

- {/* +
{loading ? (
) : ( - items.map((item) => ( - + items.map(item => ( + )) )} -
*/} +
); }; -const ForSaleCard: React.FC<{ item: any }> = ({ item }) => ( -
- {item.title} -

{item.title}

-

{item.description}

-

{item.price}

- - View Details - -
-); - export default ForSalePage; diff --git a/scruter-nextjs/app/(routes)/house/page.tsx b/scruter-nextjs/app/(routes)/house/page.tsx index d67a6474..73fc3346 100644 --- a/scruter-nextjs/app/(routes)/house/page.tsx +++ b/scruter-nextjs/app/(routes)/house/page.tsx @@ -3,37 +3,45 @@ import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faSearch } from '@fortawesome/free-solid-svg-icons'; // Import FontAwesomeIcon +import { faSearch } from '@fortawesome/free-solid-svg-icons'; import '../../globals.css'; // Ensure your global styles are imported +import toast, { Toaster } from 'react-hot-toast'; +import { GetAllListing } from '@/actions/seller/listing'; +import ListingCardFE from '@/components/listingCardFE'; const HousePage: React.FC = () => { const [houses, setHouses] = useState([]); const [loading, setLoading] = useState(true); const [query, setQuery] = useState(''); const [type, setType] = useState('house'); // Default search type is 'house' - const [sort, setSort] = useState(''); + const [sort, setSort] = useState<'asc' | 'desc' | ''>(''); useEffect(() => { const fetchData = async () => { setLoading(true); try { - // Example API call to fetch houses data - const housesResponse = await axios.get('/api/houses', { - params: { query, type, sort }, - }); - setHouses(housesResponse.data); + // Example API call to fetch housing data + const forSaleResp = await GetAllListing('Housing', query, sort); + + if (!forSaleResp || !forSaleResp.data) { + toast.error('No data fetched from BE'); + return; + } + setHouses(forSaleResp.data); } catch (error) { - console.error('Error fetching house data:', error); + console.error('Error fetching data:', error); + toast.error('An error occurred while fetching the data.'); } finally { setLoading(false); } }; - //fetchData(); + fetchData(); }, [query, type, sort]); return (
+ {/* Hero Section with Banner Image */}
{ onChange={e => setQuery(e.target.value)} />
@@ -87,7 +95,7 @@ const HousePage: React.FC = () => { id="sort-by-price" name="sort" value={sort} - onChange={e => setSort(e.target.value)} + onChange={e => setSort(e.target.value as 'asc' | 'desc' | '')} > @@ -95,7 +103,7 @@ const HousePage: React.FC = () => { - {/* Search Button with Dark Hover Effect */} + {/* Search Button */}
); }; -const HouseCard: React.FC<{ house: any }> = ({ house }) => ( -
- {house.title} -

{house.title}

-

{house.location}

-

{house.price}

- - View Details - -
-); - export default HousePage; diff --git a/scruter-nextjs/components/listingCardFE.tsx b/scruter-nextjs/components/listingCardFE.tsx new file mode 100644 index 00000000..359e5bf7 --- /dev/null +++ b/scruter-nextjs/components/listingCardFE.tsx @@ -0,0 +1,73 @@ +'use client'; +import React from 'react'; +import { Image as ImageInterface } from '@prisma/client'; +import { + Carousel, + CarouselContent, + CarouselItem, +} from '@/components/ui/carousel'; +import Image from 'next/image'; +import Autoplay from 'embla-carousel-autoplay'; +import { Button } from '@/components/ui/button'; // Importing the Button component from Shadcn +import Link from 'next/link'; + +interface ListingCardProps { + name: string; + price: number; + description: string; + images: ImageInterface[]; +} + +const ListingCardFE: React.FC = ({ + name, + price, + description, + images, +}) => { + return ( +
+ + + {images.map((image, index) => ( + + {name} + + ))} + + +
+

{name}

+

{description}

+

Price: ₹{price}

+
+ + +
+
+
+ ); +}; + +export default ListingCardFE;