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

Zadanie #7

Open
wants to merge 6 commits 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
17 changes: 14 additions & 3 deletions app/api/products/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
* The endpoint should accept a query parameter "page" to return the corresponding page
*/

export async function GET() {
return Response.json([]);
}
import { fetchProducts } from "@/lib/products";
import { NextRequest, NextResponse } from "next/server";

export async function GET(req: NextRequest) {
try {
const pageParam = req.nextUrl.searchParams.get("page");
const pageNumber = pageParam ? parseInt(pageParam) : 0;
const products = fetchProducts(pageNumber);
return NextResponse.json(products);
} catch (err) {
console.log(err);
return NextResponse.json({ message: "Internal Server Error" });
}
}
49 changes: 34 additions & 15 deletions app/products/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { Product } from "@/lib/products";

async function getProducts(): Promise<Product[]> {
const res = await fetch("http://localhost:3000/api/products");
return res.json();
import axios from "axios";
import { productsLength } from "@/lib/products";
import { redirect } from "next/navigation";
async function getProducts(page: number): Promise<Product[]> {
const res = await axios.get(
`http://localhost:3000/api/products?page=${page}`
);
return res.data;
}

export default async function Products() {
/* TODO: Create an endpoint that returns a list of products, and use that here.
*/
const products = await getProducts();
export default async function Products({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined };
}) {
const page = searchParams.page ? parseInt(searchParams.page.toString()) : 0;
const products = await getProducts(page);
if (products.length === 0 || page<0) redirect("/products");
const nextPage = (page + 1) * 10;

return (
<div className="mx-auto max-w-7xl">
Expand Down Expand Up @@ -82,21 +91,31 @@ export default async function Products() {
>
<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}</span> to{" "}
<span className="font-medium">
{nextPage > productsLength ? productsLength : nextPage}
</span>{" "}
of <span className="font-medium">{productsLength}</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"
href={page === 0 ? "products" : "?page=" + (page - 1)}
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 hover:text-black " +
(page === 0 && "opacity-50 select-none pointer-events-none")
}
>
Previous
</a>
<a
href="#"
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"
href={productsLength <= nextPage ? "" : "?page=" + (page + 1)}
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 hover:text-black " +
(productsLength <= nextPage &&
"opacity-50 select-none pointer-events-none")
}
>
Next
</a>
Expand Down
69 changes: 60 additions & 9 deletions lib/products.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,64 @@
import { z } from "zod";
import fs from "fs";
import path from "path";
import { parse } from "papaparse";

export type Product = {
id: string;
name: string;
price: number;
currency: string;
quantity: number;
isAlcohol: boolean;
id: string;
name: string;
price: number;
currency: string;
quantity: number;
isAlcohol: boolean;
};

const productSchema = z.object({
id: z.string(),
name: z.string(),
price: z.number(),
currency: z.string(),
quantity: z.number(),
isAlcohol: z.boolean(),
});

export let productsLength: number = 0;

export function fetchProducts(page: number): Product[] {
// todo
return [];
}
const json = readJson();
const csv = readCsv();
const outputArray = [...json, ...csv]
.sort((a, b) => a.id.localeCompare(b.id)) // sort by id asc
.filter((p) => !p.isAlcohol) // without alcohol
productsLength = outputArray.length;
return outputArray.slice(page * 10, page * 10 + 10);
}

const readJson = () => {
const jsonRecords = fs.readFileSync(
path.join(process.cwd(), "products.json"),
"utf-8"
);
const parsedJson = productSchema.array().parse(JSON.parse(jsonRecords));
return parsedJson;
};

const readCsv = () => {
const csvrecords = fs.readFileSync(
path.join(process.cwd(), "products.csv"),
"utf-8"
);
const parsed = parse(csvrecords, {
header: true,
transformHeader: (h) => h.charAt(0).toLowerCase() + h.slice(1),
}).data;
const convertedCsvRecords = parsed.map((record: any) => {
return {
...record,
price: parseFloat(record.price),
quantity: parseInt(record.quantity),
isAlcohol: record.isAlcohol === "0" ? false : true,
};
});
const parsedCsv = productSchema.array().parse(convertedCsvRecords);
return parsedCsv;
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
"test": "vitest run"
},
"dependencies": {
"@types/papaparse": "^5.3.14",
"axios": "^1.7.2",
"next": "14.2.4",
"papaparse": "^5.4.1",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20",
Expand Down
Loading