From e105884c8d23ef2aa1fea5d02dedd9d782926df2 Mon Sep 17 00:00:00 2001
From: BrotherlyHamlet16 <80749000+Clever-Niwagaba@users.noreply.github.com>
Date: Tue, 3 Dec 2024 11:11:54 +0300
Subject: [PATCH] done
---
src/App.css | 76 ++++++++++++++++-----------------
src/App.js | 38 ++++++-----------
src/App.test.js | 16 +++----
src/components/ProductRow.js | 12 ++++++
src/components/ProductTable.js | 27 ++++++++++++
src/components/ProductsPage.js | 50 ++++++++++++++++++++++
src/components/SearchBar.js | 16 +++++++
src/components/style.css | 77 ++++++++++++++++++++++++++++++++++
src/data.json | 20 ++++-----
src/index.css | 26 ++++++------
src/index.js | 34 +++++++--------
src/reportWebVitals.js | 26 ++++++------
src/setupTests.js | 10 ++---
13 files changed, 299 insertions(+), 129 deletions(-)
create mode 100644 src/components/ProductRow.js
create mode 100644 src/components/ProductTable.js
create mode 100644 src/components/ProductsPage.js
create mode 100644 src/components/SearchBar.js
create mode 100644 src/components/style.css
diff --git a/src/App.css b/src/App.css
index 74b5e05..fe8e2a0 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,38 +1,38 @@
-.App {
- text-align: center;
-}
-
-.App-logo {
- height: 40vmin;
- pointer-events: none;
-}
-
-@media (prefers-reduced-motion: no-preference) {
- .App-logo {
- animation: App-logo-spin infinite 20s linear;
- }
-}
-
-.App-header {
- background-color: #282c34;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- font-size: calc(10px + 2vmin);
- color: white;
-}
-
-.App-link {
- color: #61dafb;
-}
-
-@keyframes App-logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
+.App {
+ text-align: center;
+}
+
+.App-logo {
+ height: 40vmin;
+ pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ .App-logo {
+ animation: App-logo-spin infinite 20s linear;
+ }
+}
+
+.App-header {
+ background-color: #282c34;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: calc(10px + 2vmin);
+ color: white;
+}
+
+.App-link {
+ color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/App.js b/src/App.js
index 3784575..c732f28 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,25 +1,13 @@
-import logo from './logo.svg';
-import './App.css';
-
-function App() {
- return (
-
- );
-}
-
-export default App;
+import './App.css';
+import ProductsPage from "./components/ProductsPage";
+
+function App() {
+ return (
+
+ );
+}
+
+export default App;
diff --git a/src/App.test.js b/src/App.test.js
index 1f03afe..4de7037 100644
--- a/src/App.test.js
+++ b/src/App.test.js
@@ -1,8 +1,8 @@
-import { render, screen } from '@testing-library/react';
-import App from './App';
-
-test('renders learn react link', () => {
- render();
- const linkElement = screen.getByText(/learn react/i);
- expect(linkElement).toBeInTheDocument();
-});
+import { render, screen } from '@testing-library/react';
+import App from './App';
+
+test('renders learn react link', () => {
+ render();
+ const linkElement = screen.getByText(/learn react/i);
+ expect(linkElement).toBeInTheDocument();
+});
diff --git a/src/components/ProductRow.js b/src/components/ProductRow.js
new file mode 100644
index 0000000..3291f25
--- /dev/null
+++ b/src/components/ProductRow.js
@@ -0,0 +1,12 @@
+import React from "react";
+
+function ProductRow({ product }) {
+ return (
+
+ | {product.name} |
+ {product.price} |
+
+ );
+}
+
+export default ProductRow;
\ No newline at end of file
diff --git a/src/components/ProductTable.js b/src/components/ProductTable.js
new file mode 100644
index 0000000..07d0e40
--- /dev/null
+++ b/src/components/ProductTable.js
@@ -0,0 +1,27 @@
+import React from 'react'
+import './css/style.css'
+
+function ProductTable({ products }) {
+ return (
+
+
+
+ | Name |
+ Price |
+
+
+
+ {products.map((product) => (
+
+ | {product.name} |
+ {product.price} |
+
+ ))}
+
+
+ );
+}
+export default ProductTable;
\ No newline at end of file
diff --git a/src/components/ProductsPage.js b/src/components/ProductsPage.js
new file mode 100644
index 0000000..cb04acd
--- /dev/null
+++ b/src/components/ProductsPage.js
@@ -0,0 +1,50 @@
+import React, { useState } from "react";
+import SearchBar from "./SearchBar";
+import ProductTable from "./ProductTable";
+import data from "../data.json";
+import "./css/style.css";
+
+function ProductsPage() {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [onlyInStock, setOnlyInStock] = useState(false);
+
+ const handleSearchChange = (query) => {
+ setSearchQuery(query);
+ };
+
+ const handleInStockChange = (event) => {
+ setOnlyInStock(event.target.checked);
+ };
+
+ const filteredProducts = data.filter((product) => {
+ const matchesSearchQuery = product.name
+ .toLowerCase()
+ .includes(searchQuery.toLowerCase());
+ const matchesStockFilter = !onlyInStock || product.inStock;
+ return matchesSearchQuery && matchesStockFilter;
+ });
+
+ return (
+
+
Search
+
+
+
+
+
+
+
+ );
+}
+
+export default ProductsPage;
\ No newline at end of file
diff --git a/src/components/SearchBar.js b/src/components/SearchBar.js
new file mode 100644
index 0000000..f05dbbf
--- /dev/null
+++ b/src/components/SearchBar.js
@@ -0,0 +1,16 @@
+import React from 'react'
+import './css/style.css'
+
+function SearchBar({ searchQuery, onSearchChange }) {
+ return (
+
+ onSearchChange(e.target.value)}
+ />
+
+ );
+}
+
+export default SearchBar
\ No newline at end of file
diff --git a/src/components/style.css b/src/components/style.css
new file mode 100644
index 0000000..d28c542
--- /dev/null
+++ b/src/components/style.css
@@ -0,0 +1,77 @@
+.table-wrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 1500px;
+ margin-left: 0px;
+ padding-left: 200px;
+ }
+
+ table {
+ width: 100%;
+ max-width: 2000px;
+ border-collapse: collapse;
+ }
+
+ table th:first-child
+ {
+ width: 100%;
+ padding-left: 0px;
+ padding-right: -300px;
+ }
+
+ table th:nth-child(2) {
+ width: 300px;
+ margin-left: 500px;
+}
+
+ table th:first-child,
+ table th:nth-child(2)
+ {
+ background-color: rgb(209, 213, 222);
+ color: grey;
+ }
+
+ table th,
+ table td {
+ padding-top: 20px;
+ padding-bottom: 20px;
+ padding-right: 300px;
+ text-align: center;
+ }
+
+ td:first-child {
+ width: 300px;
+ padding-left: 0px;
+ }
+
+ table th:nth-child(2) {
+ width: 150px;
+ padding-right: -200px;
+ }
+
+ th:last-child,
+ .search-bar {
+ width: 100%;
+ margin-bottom: 20px;
+}
+
+.search-bar input {
+ width: 80%;
+ padding: 8px;
+ box-sizing: border-box;
+}
+
+div.search-title {
+ padding-bottom: 15px !important;
+}
+
+.in-stock-filter {
+ padding-bottom: 50px;
+}
+
+
+.out-of-stock {
+ color: red;
+
+}
diff --git a/src/data.json b/src/data.json
index 3d95d98..2eb3dd0 100644
--- a/src/data.json
+++ b/src/data.json
@@ -1,11 +1,11 @@
-[
- {"category": "Sporting Goods", "price": "$49.99", "inStock": true, "name": "Football", "id": "295a4dab-74b2-4e60-b3c2-c1346aba7585"},
- {"category": "Sporting Goods", "price": "$9.99", "inStock": false, "name": "Basketball", "id": "6cf41052-7869-490f-9c2c-8f8efd2a4b5d"},
- {"category": "Electronics", "price": "$99.99", "inStock": true, "name": "iPod Touch", "id": "6fa4681a-61e1-4bf6-823a-24b2fe335543"},
- {"category": "Electronics", "price": "$199.99", "inStock": false, "name": "iPhone X", "id": "bbdabd03-0e02-4e7d-a7fc-ce52cc1164be"},
- {"category": "Sporting Goods", "price": "$9.99", "inStock": true, "name": "Tennis Balls", "id": "5358b8a4-fe62-4f7d-9a22-712be95a1f72"},
- {"category": "Electronics", "price": "$199.99", "inStock": true, "name": "Huawei P10", "id": "10858000-7894-4d77-bd0f-24639d111e74"},
- {"category": "Electronics", "price": "$199.99", "inStock": true, "name": "iPad Mini", "id": "a385a23f-07ed-4340-9ba7-937a0ce5f151"},
- {"category": "Sporting Goods", "price": "$9.99", "inStock": true, "name": "Baseball", "id": "4522f16e-3f55-4863-ae36-e935ec9cb4ef"}
- ]
+[
+ {"category": "Sporting Goods", "price": "$49.99", "inStock": true, "name": "Football", "id": "295a4dab-74b2-4e60-b3c2-c1346aba7585"},
+ {"category": "Sporting Goods", "price": "$9.99", "inStock": false, "name": "Basketball", "id": "6cf41052-7869-490f-9c2c-8f8efd2a4b5d"},
+ {"category": "Electronics", "price": "$99.99", "inStock": true, "name": "iPod Touch", "id": "6fa4681a-61e1-4bf6-823a-24b2fe335543"},
+ {"category": "Electronics", "price": "$199.99", "inStock": false, "name": "iPhone X", "id": "bbdabd03-0e02-4e7d-a7fc-ce52cc1164be"},
+ {"category": "Sporting Goods", "price": "$9.99", "inStock": true, "name": "Tennis Balls", "id": "5358b8a4-fe62-4f7d-9a22-712be95a1f72"},
+ {"category": "Electronics", "price": "$199.99", "inStock": true, "name": "Huawei P10", "id": "10858000-7894-4d77-bd0f-24639d111e74"},
+ {"category": "Electronics", "price": "$199.99", "inStock": true, "name": "iPad Mini", "id": "a385a23f-07ed-4340-9ba7-937a0ce5f151"},
+ {"category": "Sporting Goods", "price": "$9.99", "inStock": true, "name": "Baseball", "id": "4522f16e-3f55-4863-ae36-e935ec9cb4ef"}
+ ]
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index ec2585e..bd5bd6d 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,13 +1,13 @@
-body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
- sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
- monospace;
-}
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
diff --git a/src/index.js b/src/index.js
index d563c0f..176fc7d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,17 +1,17 @@
-import React from 'react';
-import ReactDOM from 'react-dom/client';
-import './index.css';
-import App from './App';
-import reportWebVitals from './reportWebVitals';
-
-const root = ReactDOM.createRoot(document.getElementById('root'));
-root.render(
-
-
-
-);
-
-// If you want to start measuring performance in your app, pass a function
-// to log results (for example: reportWebVitals(console.log))
-// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals();
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import './index.css';
+import App from './App';
+import reportWebVitals from './reportWebVitals';
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render(
+
+
+
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();
diff --git a/src/reportWebVitals.js b/src/reportWebVitals.js
index 5253d3a..77957db 100644
--- a/src/reportWebVitals.js
+++ b/src/reportWebVitals.js
@@ -1,13 +1,13 @@
-const reportWebVitals = onPerfEntry => {
- if (onPerfEntry && onPerfEntry instanceof Function) {
- import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
- getCLS(onPerfEntry);
- getFID(onPerfEntry);
- getFCP(onPerfEntry);
- getLCP(onPerfEntry);
- getTTFB(onPerfEntry);
- });
- }
-};
-
-export default reportWebVitals;
+const reportWebVitals = onPerfEntry => {
+ if (onPerfEntry && onPerfEntry instanceof Function) {
+ import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+ getCLS(onPerfEntry);
+ getFID(onPerfEntry);
+ getFCP(onPerfEntry);
+ getLCP(onPerfEntry);
+ getTTFB(onPerfEntry);
+ });
+ }
+};
+
+export default reportWebVitals;
diff --git a/src/setupTests.js b/src/setupTests.js
index 8f2609b..141e479 100644
--- a/src/setupTests.js
+++ b/src/setupTests.js
@@ -1,5 +1,5 @@
-// jest-dom adds custom jest matchers for asserting on DOM nodes.
-// allows you to do things like:
-// expect(element).toHaveTextContent(/react/i)
-// learn more: https://github.com/testing-library/jest-dom
-import '@testing-library/jest-dom';
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';