Skip to content

Commit

Permalink
Merge pull request #9 from JuzerShakir/edit-page
Browse files Browse the repository at this point in the history
Create Edit Page
  • Loading branch information
JuzerShakir authored May 4, 2024
2 parents 8092e10 + bdbabd3 commit af732c5
Show file tree
Hide file tree
Showing 15 changed files with 304 additions and 115 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<body class="bg-yellow-400">
<div
id="root"
class="h-svh flex flex-col justify-between items-center pt-6"
class="h-svh flex flex-col justify-between items-center py-6"
></div>
<script type="module" src="/src/main.jsx"></script>
</body>
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.0"
},
"devDependencies": {
"@types/react": "^18.2.66",
Expand Down
66 changes: 18 additions & 48 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,27 @@
import { useState } from "react";
// import assets
// import editIcon from "./assets/edit.svg";
// import homeIcon from "./assets/home.svg";
// components
import Header from "./components/Header";
import ScrapItems from "./components/ScrapItems";
import GrandTotal from "./components/GrandTotal";
import Footer from "./components/Footer";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import { scrapItemsList } from "./scrapItemsList";

const scrapItems = [
{ id: 1, name: "plastic", earningPerKg: 15, earnings: 0 },
{ id: 2, name: "cardboard", earningPerKg: 8, earnings: 0 },
{ id: 3, name: "paper", earningPerKg: 10, earnings: 0 },
{ id: 4, name: "steel", earningPerKg: 40, earnings: 0 },
{ id: 5, name: "iron", earningPerKg: 30, earnings: 0 },
{ id: 6, name: "german", earningPerKg: 120, earnings: 0 },
];

function roundToNearestTenth(number) {
return Math.round(number * 10) / 10;
}
// pages
import HomePage from "./pages/HomePage";
import Edit from "./pages/Edit";

function App() {
const [items, setItems] = useState(scrapItems);
const newTotalEarnings = items.reduce(
(accumulator, item) => roundToNearestTenth(accumulator + item.earnings),
0
);

function handleItemEarnings(id, weight) {
// rescue against negative values
if (weight < 0) return;
const [items, setItems] = useState(scrapItemsList);

setItems((items) =>
items.map((item) =>
item.id === id
? {
...item,
earnings: roundToNearestTenth(weight * item.earningPerKg),
}
: item
)
);
}
// routing
const router = createBrowserRouter([
{
path: "/",
element: <HomePage items={items} setItems={setItems} />,
},
{
path: "/edit",
element: <Edit items={items} setItems={setItems} />,
},
]);

return (
<>
<Header />
<ScrapItems items={items} onHandleItemEarnings={handleItemEarnings} />
<GrandTotal totalEarnings={newTotalEarnings} />
<Footer />
</>
);
return <RouterProvider router={router} />;
}

export default App;
14 changes: 14 additions & 0 deletions src/components/Aside.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Link } from "react-router-dom";
import { ValidateAsideProps } from "./../propValidations";

Aside.propTypes = ValidateAsideProps;

export default function Aside({ icon, alt, linkTo }) {
return (
<aside className="w-1/4 self-end">
<Link to={linkTo}>
<img src={icon} alt={alt} className="w-7 cursor-pointer" />
</Link>
</aside>
);
}
5 changes: 3 additions & 2 deletions src/components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export default function Footer() {
return (
<footer className="mb-2">
<footer>
<copy className="roboto-condensed-light-italic text-xs md:text-sm tracking-wider md:tracking-wide">
&copy; 2024 Created with ❤️ by Juzer Shakir
&copy; 2024 Created with ❤️ by{" "}
<strong className="font-light">Juzer Shakir</strong>
</copy>
</footer>
);
Expand Down
18 changes: 11 additions & 7 deletions src/components/GrandTotal.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import PropTypes from "prop-types";
import largeRupeeIcon from "./../assets/large_rupee.svg";
import { ValidateItemsProp } from "../propValidations";
import largeRupeeIcon from "../assets/large_rupee.svg";
import roundToNearestPlace from "../roundToNearestPlace";

GrandTotal.propTypes = {
totalEarnings: PropTypes.number.isRequired,
};
GrandTotal.propTypes = ValidateItemsProp;

export default function GrandTotal({ items }) {
const totalEarnings = items.reduce(
(accumulator, item) => roundToNearestPlace(accumulator + item.earnings),
0
);

export default function GrandTotal({ totalEarnings }) {
return (
<section className="flex">
<img src={largeRupeeIcon} alt="rupee icon" />
<strong className="patrick-hand-regular text-7xl text-green-800">
{totalEarnings}
{Math.round(totalEarnings)}
</strong>
</section>
);
Expand Down
34 changes: 16 additions & 18 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { useState, useEffect } from "react";
import { ValidateHeaderProps } from "./../propValidations";

export default function Header() {
Header.propTypes = ValidateHeaderProps;

export default function Header({ title, subHeadings }) {
return (
<header className="flex flex-col items-center gap-1 w-full">
<h1 className="patrick-hand-sc-regular text-6xl text-yellow-700">
Scrapulator
</h1>
<h2 className="patrick-hand-regular text-lg text-yellow-700 tracking-wider">
<SubHeading />
</h2>
</header>
<>
<header className="flex flex-col items-center gap-4 w-full">
<h1 className="patrick-hand-sc-regular text-6xl text-yellow-700">
{title}
</h1>
<h2 className="patrick-hand-regular text-xl text-center text-yellow-700 tracking-wider">
<SubHeading subHeadings={subHeadings} />
</h2>
</header>
</>
);
}

function SubHeading() {
const subHeadings = [
"Scrap calculations made easy",
"Scrap value at your fingertips",
"Calculate your scrap, calculate your earnings",
];

function SubHeading({ subHeadings }) {
const [currentSubheadingIndex, setCurrentSubheadingIndex] = useState(0);

// Function to cycle through subheadings
Expand All @@ -29,11 +28,10 @@ function SubHeading() {
);
};

// Change subheading every 5 seconds
useEffect(() => {
const intervalId = setInterval(() => {
changeSubheading();
}, 5000); // Change subheading every 5 seconds
}, 2500); // Change subheading every 2.5 seconds

return () => clearInterval(intervalId);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import PropTypes from "prop-types";
import smallRupeeIcon from "./../assets/small_rupee.svg";
import {
ValidateItemsStateProps,
ValidateItemEarningsProps,
} from "../propValidations";
import smallRupeeIcon from "../assets/small_rupee.svg";
import roundToNearestPlace from "../roundToNearestPlace";

ScrapItems.propTypes = {
items: PropTypes.array.isRequired,
onHandleItemEarnings: PropTypes.func.isRequired,
};
ScrapItemsEarning.propTypes = ValidateItemsStateProps;
Item.propTypes = ValidateItemEarningsProps;

Item.propTypes = {
item: PropTypes.object.isRequired,
onHandleItemEarnings: PropTypes.func.isRequired,
};
export default function ScrapItemsEarning({ items, setItems }) {
function handleItemEarnings(id, weight) {
// rescue against negative values
if (weight < 0) return;

ItemName.propTypes = {
name: PropTypes.string.isRequired,
};
setItems((items) =>
items.map((item) =>
item.id === id
? {
...item,
earnings: roundToNearestPlace(weight * item.earningPerKg),
weight: Number(weight),
}
: item
)
);
}

export default function ScrapItems({ items, onHandleItemEarnings }) {
return (
<main>
<ul
Expand All @@ -26,7 +36,7 @@ export default function ScrapItems({ items, onHandleItemEarnings }) {
<Item
key={item.id}
item={item}
onHandleItemEarnings={onHandleItemEarnings}
onHandleItemEarnings={handleItemEarnings}
/>
))}
</ul>
Expand All @@ -36,10 +46,15 @@ export default function ScrapItems({ items, onHandleItemEarnings }) {

function Item({ item, onHandleItemEarnings }) {
return (
<li className="flex justify-end gap-7">
<li className="flex justify-end gap-x-7">
<form className="flex gap-7" onSubmit={(e) => e.preventDefault()}>
{/* title */}
<ItemName name={item.name} />
<label
htmlFor={item.name}
className="patrick-hand-regular text-3xl text-green-900"
>
{item.name}
</label>

{/* weight input */}
<span className="flex gap-x-1 items-center">
Expand All @@ -48,7 +63,7 @@ function Item({ item, onHandleItemEarnings }) {
id={item.name}
name={item.name}
className="nunito-sans-semibold bg-amber-100 w-10 text-end text-amber-900 pr-1 border-b-2 border-yellow-600 focus:border-yellow-900 focus:outline-none"
placeholder="0"
placeholder={item.weight}
min="0"
step="0.1"
onChange={(e) => onHandleItemEarnings(item.id, e.target.value)}
Expand All @@ -67,14 +82,3 @@ function Item({ item, onHandleItemEarnings }) {
</li>
);
}

function ItemName({ name }) {
return (
<label
htmlFor={name}
className="patrick-hand-regular text-3xl text-green-900"
>
{name}
</label>
);
}
87 changes: 87 additions & 0 deletions src/components/ScrapItemsEditEarning.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
ValidateItemsStateProps,
ValidateItemEarningsPerKgProps,
} from "../propValidations";
import smallRupeeIcon from "../assets/small_rupee.svg";

ScrapItemsEditEarning.propTypes = ValidateItemsStateProps;
Item.propTypes = ValidateItemEarningsPerKgProps;

export default function ScrapItemsEditEarning({ items, setItems }) {
function handleItemEarningPerKg(id, amount) {
// rescue against negative values
if (amount < 1) return;

const roundingAmount = Math.round(amount);

setItems((items) =>
items.map((item) =>
item.id === id
? {
...item,
earningPerKg: roundingAmount,
earnings: roundingAmount * item.weight,
}
: item
)
);
}

return (
<main>
<ul
id="scrapItems"
className="grow flex flex-col justify-center gap-5 px-6"
>
{items.map((item) => (
<Item
key={item.id}
item={item}
onHandleItemEarningPerKg={handleItemEarningPerKg}
/>
))}
</ul>
</main>
);
}

function Item({ item, onHandleItemEarningPerKg }) {
return (
<li className="flex justify-end">
<form
className="flex items-center gap-x-12"
onSubmit={(e) => e.preventDefault()}
>
{/* title */}
<label
htmlFor={item.name}
className="patrick-hand-regular text-3xl text-green-900"
>
{item.name}
</label>

{/* weight input */}
<span className="flex gap-x-1 items-end">
<img
src={smallRupeeIcon}
className="h-5 self-center"
alt="rupee icon"
/>
<input
type="number"
id={item.name}
name={item.name}
className="nunito-sans-semibold bg-amber-100 w-14 text-end text-amber-900 pr-1 border-b-2 border-yellow-600 focus:border-yellow-900 focus:outline-none"
placeholder={item.earningPerKg}
min="1"
step="1"
onChange={(e) =>
onHandleItemEarningPerKg(item.id, Number(e.target.value))
}
></input>
<strong className="roboto-condensed-normal">per kg</strong>
</span>
</form>
</li>
);
}
Loading

0 comments on commit af732c5

Please sign in to comment.