Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finished tasks #14

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions app/api/products/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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});
}
82 changes: 60 additions & 22 deletions app/products/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
"use client"
import { Product } from "@/lib/products";
import { useEffect, useState } from "react";

async function getProducts(): Promise<Product[]> {
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<Product[]>([]);

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 (
<div className="mx-auto max-w-7xl">
<div className="mt-8 flow-root">
<div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
{/** 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...</> :
<div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
<table className="min-w-full divide-y divide-gray-700">
<thead>
Expand Down Expand Up @@ -54,7 +71,7 @@ export default async function Products() {
</tr>
</thead>
<tbody className="divide-y divide-gray-800">
{products.map((product) => (
{products.map((product: Product) => (
<tr key={product.id}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-white sm:pl-0">
{product.id}
Expand All @@ -72,37 +89,58 @@ export default async function Products() {
{product.isAlcohol ? "Yes" : "No"}
</td>
</tr>
))}
)) }
</tbody>
</table>
{/* TODO: Pagination */}


<nav
className="flex items-center justify-between py-3"
aria-label="Pagination"
>
<div className="hidden sm:block">
<p className="text-sm">
Showing <span className="font-medium">1</span> to{" "}
<span className="font-medium">1</span> of{" "}
<span className="font-medium">N</span> results
Showing <span className="font-medium">{page * 10 + 1}</span> to{" "}
<span className="font-medium">{page * 10 + products.length}</span> of{" "}
<span className="font-medium">93{/* static data because of static products*/}</span> results
</p>
</div>
<div className="flex flex-1 justify-between sm:justify-end">
<a
href="#"
className="relative inline-flex items-center rounded-md px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
{/** If page is first or last I can disable the button */}
{page === 0 ?
<button
className="relative inline-flex items-center rounded-md px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 focus-visible:outline-offset-0"
disabled
>
Previous
</a>
<a
href="#"
</button>:

<button
onClick={() => handlePrevious()}
className="relative inline-flex items-center rounded-md px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
>
Previous
</button>
}
{page === 9 ?
<button
disabled
className="relative ml-3 inline-flex items-center rounded-md px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 focus-visible:outline-offset-0"
>
Next
</button>
:
<button
onClick={() => handleNext()}
className="relative ml-3 inline-flex items-center rounded-md px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
>
Next
</a>
</button>
}

</div>
</nav>
</div>
</div>}
</div>
</div>
</div>
Expand Down
33 changes: 31 additions & 2 deletions lib/products.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import fs from 'fs';
import Papa from 'papaparse';
export type Product = {
id: string;
name: string;
Expand All @@ -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;
}
42 changes: 38 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down