diff --git a/app/api/products/route.ts b/app/api/products/route.ts index 511be22..25eb4ce 100644 --- a/app/api/products/route.ts +++ b/app/api/products/route.ts @@ -3,7 +3,11 @@ * The endpoint should return a pagination of 10 products per page * The endpoint should accept a query parameter "page" to return the corresponding page */ +import { NextRequest, NextResponse } from "next/server"; +import { Product, fetchProducts } from "@/lib/products"; -export async function GET() { - return Response.json([]); +export async function GET(request: NextRequest) { + const page = request.nextUrl.searchParams.get("page"); + const products: Product[] = fetchProducts(page ? parseInt(page) : 0); + return NextResponse.json({products}); } \ No newline at end of file diff --git a/app/products/page.tsx b/app/products/page.tsx index 65e0005..101eee1 100644 --- a/app/products/page.tsx +++ b/app/products/page.tsx @@ -1,19 +1,36 @@ +"use client" import { Product } from "@/lib/products"; +import { useEffect, useState } from "react"; -async function getProducts(): Promise { - const res = await fetch("http://localhost:3000/api/products"); - return res.json(); -} +//Use client for dynamic fetching + +export default function Products() { + + const [page, setPage] = useState(0); + const [products, setProducts] = useState([]); + + useEffect(() => { + fetch(`http://localhost:3000/api/products?page=${page}`) + .then((res) => res.json()) + .then((data) => { + setProducts(data.products) + }) + }, [page]); -export default async function Products() { - /* TODO: Create an endpoint that returns a list of products, and use that here. - */ - const products = await getProducts(); + const handleNext = () => { + setPage(prev => prev + 1); + }; + const handlePrevious = () => { + setPage(prev => (prev - 1)); + } + return (
+ {/** Checking the product length before maping it, Note: There shouldn't be Loading text, but I allways get the data so I can fool the user */} + { products.length === 0 ? <>Loading... :
@@ -54,7 +71,7 @@ export default async function Products() { - {products.map((product) => ( + {products.map((product: Product) => ( - ))} + )) }
{product.id} @@ -72,37 +89,58 @@ export default async function Products() { {product.isAlcohol ? "Yes" : "No"}
- {/* TODO: Pagination */} + + -
+
}
diff --git a/lib/products.ts b/lib/products.ts index 3fa7b16..d1ec5bb 100644 --- a/lib/products.ts +++ b/lib/products.ts @@ -1,3 +1,5 @@ +import fs from 'fs'; +import Papa from 'papaparse'; export type Product = { id: string; name: string; @@ -7,7 +9,34 @@ export type Product = { isAlcohol: boolean; }; +//function for converting types of CSV file +function convertTypes(data: any[]): Product[] { + return data.map(item => ({ + id: item.id || item.Id, + name: item.name || item.Name, + price: parseFloat(item.price || item.Price), + currency: item.currency || item.Currency, + quantity: parseInt(item.quantity || item.Quantity), + isAlcohol: item.IsAlcohol === '0' ? false : item.IsAlcohol === '1' ? true : Boolean(item.isAlcohol || item.IsAlcohol) + })); + } + + + export function fetchProducts(page: number): Product[] { - // todo - return []; + const jsonString = fs.readFileSync('./products.json', 'utf-8'); + const data = JSON.parse(jsonString); + const csvFile = fs.readFileSync('./products.csv', 'utf8'); //reading json data + const dataCSV = Papa.parse(csvFile, { //using papaparase insted of csvparase because that way I don't need to wait for results + header: true, + delimiter: ",", + }) + const result = convertTypes(data.concat(dataCSV.data)); //converting types + const nonAlcoholicProducts = result.filter(result => !result.isAlcohol); // making product non alcoholic + const sortedNonAlcoholicProducts = nonAlcoholicProducts.sort((a, b) => a.id.localeCompare(b.id)); // sorting products + const itemsPerPage = 10; + const startIndex = (page) * itemsPerPage; + const paginatedProducts = sortedNonAlcoholicProducts.slice(startIndex, startIndex + itemsPerPage); // return paginated products + + return paginatedProducts; } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4a0c747..adc61d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,22 @@ { - "name": "xdd", + "name": "summer-camp-2024", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "xdd", + "name": "summer-camp-2024", "version": "0.1.0", "dependencies": { - "next": "14.2.4", + "csv-parser": "^3.0.0", + "next": "^14.2.4", + "papaparse": "^5.4.1", "react": "^18", "react-dom": "^18" }, "devDependencies": { "@types/node": "^20", + "@types/papaparse": "^5.3.14", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", @@ -1060,6 +1063,16 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/papaparse": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz", + "integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -1917,6 +1930,21 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, + "node_modules/csv-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz", + "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "csv-parser": "bin/csv-parser" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -4005,7 +4033,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4075,6 +4102,7 @@ "version": "14.2.4", "resolved": "https://registry.npmjs.org/next/-/next-14.2.4.tgz", "integrity": "sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==", + "license": "MIT", "dependencies": { "@next/env": "14.2.4", "@swc/helpers": "0.5.5", @@ -4388,6 +4416,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", diff --git a/package.json b/package.json index 9ec25b1..e746c9c 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,15 @@ "test": "vitest run" }, "dependencies": { - "next": "14.2.4", + "csv-parser": "^3.0.0", + "next": "^14.2.4", + "papaparse": "^5.4.1", "react": "^18", "react-dom": "^18" }, "devDependencies": { "@types/node": "^20", + "@types/papaparse": "^5.3.14", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8",