Skip to content
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
4 changes: 2 additions & 2 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@import "tailwindcss";

:root {
--background: #ffffff;
--background: white;
--foreground: #171717;
}

Expand All @@ -20,7 +20,7 @@
}

body {
background: var(--background);
background: white;
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
10 changes: 4 additions & 6 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ const geistMono = Geist_Mono({
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Frontend Assessment",
description: "A simple product listing page with data fetching",
};

export default function RootLayout({
Expand All @@ -23,10 +23,8 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<html lang="en" className="h-full bg-white">
<body className={`${geistSans.variable} ${geistMono.variable} antialiased h-full bg-white`}>
{children}
</body>
</html>
Expand Down
105 changes: 3 additions & 102 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,103 +1,4 @@
import Image from "next/image";

export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2 tracking-[-.01em]">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
src/app/page.tsx
</code>
.
</li>
<li className="tracking-[-.01em]">
Save and see your changes instantly.
</li>
</ol>

<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org →
</a>
</footer>
</div>
);
import Main from "../components/Main";
export default async function Home() {
return ( <Main/> );
}
13 changes: 13 additions & 0 deletions frontend/src/components/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

export default function Card({productName, price, imageURL, onClick}) {
return (
<div className="group" onClick={onClick}>
<img
src={imageURL}
className="aspect-square w-full rounded-lg bg-gray-200 object-contain group-hover:opacity-75 xl:aspect-7/8"
/>
<h3 className="mt-4 text-sm text-gray-700">{productName}</h3>
<p className="mt-1 text-lg font-medium text-gray-900">${price}</p>
</div>
);
}
81 changes: 81 additions & 0 deletions frontend/src/components/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use client"

import Card from "./Card";
import Preview from "./Preview";
import { useState, useEffect } from 'react';

export default function Main() {

var [productData, setProductData ] = useState([]);
var [filteredData, setFilteredProductData] = useState([]);
var [previewProductData, setPreviewProductData] = useState(null);

var searchItems = function(value){
if(productData.length == 0) return;

if(value.trim() == ""){
setFilteredProductData(productData);
return;
}

const results = productData.filter(item =>
item.title.toLowerCase().includes(value.toLowerCase())
);

setFilteredProductData(results);
}

useEffect(() => {

(async () => {
const res = await fetch('https://fakestoreapi.com/products');
const data = await res.json();
if(data){
setProductData(data);
setFilteredProductData(data);
}
})()

}, []);

return (
<div className="bg-white">
<input onChange={ (e) => searchItems(e.target.value) } type="text" placeholder="Search Product" className="z-[90] max-w-[90%] fixed top-[20px] left-[20px] rounded-[20px] p-[10px] w-[300px] bg-gray-800 text-base text-white placeholder:text-gray-500 focus:outline-none sm:text-sm/6" />

<div className="mx-auto max-w-2xl px-4 py-16 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8">

{ previewProductData &&
(
<Preview
productName = { previewProductData.title }
description = { previewProductData.description }
price = { previewProductData.price }
imageURL = { previewProductData.image }
ratings = { previewProductData.rating }
onClick = {() => { setPreviewProductData(null) }}
/>
)
}

{ filteredData.length == 0 && <div className="text-[black] text-[30px]">No Product Avaible</div> }

<div className="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">

{
filteredData.map( data =>
(<Card
key={data.id}
productName = {data.title}
price = {data.price}
imageURL = {data.image}
onClick = {() => { setPreviewProductData(data) }}
/>)
)
}

</div>
</div>
</div>

);
}
34 changes: 34 additions & 0 deletions frontend/src/components/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default function Preview({ productName, description, price, imageURL, ratings, onClick }) {

var starRatingStr = "";
for (var i = 0; i < 5; i++) {
if(i < Math.round(ratings.rate)) starRatingStr += "★";
else starRatingStr += "☆";
}

return (
<div className="block w-full h-full fixed z-[100] bg-[#00000082] left-0 top-0">
<div className="overflow-hidden text-black relative w-[95%] h-auto bg-[white] rounded-[10px] top-2/4 left-2/4 -translate-x-1/2 -translate-y-1/2 max-w-[700px]">

<div style={{ backgroundImage: `url(${imageURL})` }} className="bg-[#e5e7eb] w-full h-[300px] bg-no-repeat bg-contain bg-center"></div>

<div className="w-full h-auto p-[20px] box-border">
<div className=" mb-[20px] text-[20px] flex justify-between text-[30px]">
<span className="mr-[20px] leading-[50px] text-[#101828] text-[40px] font-bold">{ productName }</span>
<span>${price}</span>
</div>

<div className="mb-[20px] text-[20px]">
<span className="mr-[20px]">{ starRatingStr }</span>
<span>{ratings.count || 1} reviews</span>
</div>

<div className="mb-[20px] text-[20px]">{description}</div>
</div>

<span onClick={onClick} className="flex absolute top-[20px] right-[20px] w-[25px] h-[25px] justify-center items-center text-[25px] text-[#828282] font-[arial] select-none">X</span>
</div>

</div>
);
}