-
-
Notifications
You must be signed in to change notification settings - Fork 836
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* feat(play/tip-calculator): add Tip Calculator #1277 * renamed extensions - js to jsx * sytle.css mod; root:selector removed --------- Co-authored-by: Tapas Adhikary <[email protected]>
- Loading branch information
1 parent
fd84dd6
commit 6fef24b
Showing
7 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Tip Calculator | ||
|
||
An app to calculate tip evenly where user can divide the total bill amount into sharing number of people and the tip, and distribute the bill for each. Split your bill with friends and family and calculate tip easily and no more hassle of calculating tip and bill amount. | ||
|
||
## Play Demographic | ||
|
||
- Language: js | ||
- Level: Beginner | ||
|
||
## Creator Information | ||
|
||
- User: ayushsgithub | ||
- Gihub Link: https://github.com/ayushsgithub |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
'use client'; | ||
import React, { useState, useEffect } from 'react'; | ||
import PlayHeader from 'common/playlists/PlayHeader'; | ||
import './styles.css'; | ||
import BillAmount from './components/BillAmount'; | ||
import TipAmount from './components/TipAmount'; | ||
import PeopleAmount from './components/PeopleAmount'; | ||
import DisplayCard from './components/DisplayCard'; | ||
|
||
// WARNING: Do not change the entry componenet name | ||
|
||
function TipCalculator(props) { | ||
// Your Code Start below. | ||
|
||
const [bill, setBill] = useState(); | ||
const [tip, setTip] = useState(0); | ||
const [people, setPeople] = useState(0); | ||
|
||
const [tipPerPerson, setTipPerPerson] = useState(0); | ||
const [totalPerPerson, setTotalPerPerson] = useState(0); | ||
const [total, setTotal] = useState(0); | ||
|
||
const handleResetTip = (e) => { | ||
e.preventDefault(); | ||
setBill(0); | ||
setPeople(0); | ||
setTipPerPerson(0); | ||
setTotalPerPerson(0); | ||
setTotal(0); | ||
}; | ||
|
||
useEffect(() => { | ||
if (bill && tip && people) { | ||
const tipAmount = Number(bill) * Number(tip); | ||
const totalAmount = Number(bill) + tipAmount; | ||
|
||
setTipPerPerson(tipAmount / people); | ||
setTotalPerPerson(totalAmount / people); | ||
setTotal(totalAmount); | ||
} | ||
}, [bill, tip, people]); | ||
|
||
return ( | ||
<> | ||
<div className="play-details"> | ||
<PlayHeader play={props} /> | ||
<div className="play-details-body"> | ||
{/* Your Code Starts Here */} | ||
<main className="flex h-fit flex-col items-center justify-center p-7 lg:p-5"> | ||
<h1 className="mb-3 text-center text-3xl font-bold uppercase tracking-widest text-cyan-800"> | ||
Tip Splitter | ||
</h1> | ||
<div className="w-full max-w-3xl overflow-hidden rounded-xl bg-white shadow-xl"> | ||
<div className="px-4 py-5 sm:p-6"> | ||
<form className="mx-auto grid max-w-6xl gap-y-5 lg:grid-cols-2 lg:gap-x-8"> | ||
<div className="flex flex-col gap-y-8 py-5 lg:px-5 lg:py-6"> | ||
<BillAmount bill={bill} setBill={setBill} /> | ||
<TipAmount setTip={setTip} /> | ||
<PeopleAmount people={people} setPeople={setPeople} /> | ||
</div> | ||
|
||
<DisplayCard | ||
reset={handleResetTip} | ||
tipPerPerson={tipPerPerson} | ||
total={total} | ||
totalPerPerson={totalPerPerson} | ||
/> | ||
</form> | ||
</div> | ||
</div> | ||
</main> | ||
{/* Your Code Ends Here */} | ||
</div> | ||
</div> | ||
</> | ||
); | ||
} | ||
|
||
export default TipCalculator; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React from 'react'; | ||
|
||
const BillAmount = (props) => { | ||
const { bill, setBill } = props; | ||
|
||
return ( | ||
<div> | ||
<label className="block font-serif text-sm font-light leading-6 text-gray-600" htmlFor="bill"> | ||
Bill | ||
</label> | ||
|
||
<div className="relative mt-2 rounded-md shadow-sm"> | ||
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> | ||
<span className="font-serif text-gray-500 sm:text-sm">₹</span> | ||
</div> | ||
|
||
<input | ||
aria-describedby="bill-currency" | ||
className="block w-full rounded-md border-0 py-1.5 pl-7 pr-2 font-serif text-gray-900 outline-none ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-cyan-500 sm:text-sm sm:leading-6" | ||
id="bill" | ||
name="bill" | ||
placeholder="0.00" | ||
type="number" | ||
value={bill} | ||
onChange={(e) => setBill(e.target.value)} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default BillAmount; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import React from 'react'; | ||
|
||
const DisplayCard = (props) => { | ||
const { reset, tipPerPerson, totalPerPerson, total } = props; | ||
|
||
const data = [ | ||
{ | ||
label: 'Tip Amount', | ||
value: tipPerPerson.toFixed(2) | ||
}, | ||
{ | ||
label: 'Total', | ||
value: totalPerPerson.toFixed(2) | ||
} | ||
]; | ||
|
||
return ( | ||
<div className="flex flex-col justify-between rounded-xl bg-cyan-700 p-5 lg:py-10"> | ||
<div className="flex flex-col gap-y-8"> | ||
{data.map((item, i) => ( | ||
<div className="flex items-end justify-between" key={i}> | ||
<div> | ||
<p className="font-serif text-white lg:text-lg">{item.label}</p> | ||
<p className="font-serif text-xs font-light text-gray-300 lg:text-sm">/ person</p> | ||
</div> | ||
<div className="flex items-baseline gap-x-2"> | ||
<span className="text-xl font-extralight text-white lg:text-xl">₹</span> | ||
<span className="font-serif text-3xl font-medium text-white lg:text-4xl"> | ||
{item.value} | ||
</span> | ||
</div> | ||
</div> | ||
))} | ||
|
||
<div className="flex items-end justify-between"> | ||
<p className="font-serif font-medium text-white lg:text-lg">Total Bill</p> | ||
|
||
<div className="flex items-baseline gap-x-2"> | ||
<span className="text-xl font-extralight text-white lg:text-2xl">₹</span> | ||
<span className="font-serif text-4xl font-medium text-white lg:text-5xl"> | ||
{total?.toFixed(2)} | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<button | ||
className="mt-14 w-full rounded-md bg-cyan-200 px-3.5 py-2.5 text-lg font-semibold uppercase tracking-wide text-cyan-700 shadow-sm hover:bg-cyan-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-white" | ||
type="submit" | ||
onClick={(e) => reset(e)} | ||
> | ||
Reset Tip | ||
</button> | ||
</div> | ||
); | ||
}; | ||
|
||
export default DisplayCard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// import { UsersIcon } from '@heroicons/react/20/solid'; | ||
import PersonIcon from '@mui/icons-material/Person'; | ||
|
||
const PeopleAmount = ({ people, setPeople }) => { | ||
return ( | ||
<div> | ||
<label | ||
className="block font-serif text-sm font-light leading-6 text-gray-600" | ||
htmlFor="people" | ||
> | ||
Number of People | ||
</label> | ||
|
||
<div className="relative mt-2 flex flex-grow items-stretch focus-within:z-10"> | ||
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> | ||
{/* <UsersIcon className="h-5 w-5 text-gray-400" aria-hidden="true" /> */} | ||
<PersonIcon aria-hidden="true" className="h-5 w-5 text-gray-400" /> | ||
</div> | ||
|
||
<input | ||
aria-describedby="number-of-people" | ||
className="block w-full rounded-md border-0 py-1.5 pl-10 pr-3 font-serif text-gray-900 outline-none ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-cyan-500 sm:text-sm sm:leading-6" | ||
id="people" | ||
name="people" | ||
placeholder="0" | ||
type="number" | ||
value={people} | ||
onChange={(e) => setPeople(e.target.value)} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default PeopleAmount; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { useState } from 'react'; | ||
|
||
const tips = [ | ||
{ tip: 5, isCustom: false }, | ||
{ tip: 10, isCustom: false }, | ||
{ tip: 15, isCustom: false }, | ||
{ tip: 20, isCustom: false }, | ||
{ tip: 30, isCustom: false }, | ||
{ tip: 0, isCustom: true } | ||
]; | ||
|
||
const TipAmount = ({ setTip }) => { | ||
const [customSelected, setCustomSelected] = useState(false); | ||
const [activeTip, setActiveTip] = useState(null); | ||
const [customTip, setCustomTip] = useState(0); | ||
|
||
const handleTipClick = (index) => { | ||
setCustomSelected(false); | ||
|
||
if (activeTip === index) { | ||
setActiveTip(null); | ||
|
||
return; | ||
} | ||
|
||
setActiveTip(index); | ||
setTip(tips[index].tip / 100); | ||
}; | ||
|
||
const handleCustomTip = () => { | ||
setActiveTip(null); | ||
setCustomSelected(true); | ||
setTip(customTip / 100); | ||
}; | ||
|
||
const handleCustomTipBlur = () => { | ||
setTip(customTip / 100); | ||
if (customTip > 0) return; | ||
setCustomSelected(false); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<label className="block font-serif text-sm font-light leading-6 text-gray-600" htmlFor="tip"> | ||
Select Tip in % | ||
</label> | ||
|
||
<div className="mt-2 grid grid-cols-3 gap-3"> | ||
{tips.map((tip, index) => ( | ||
<div key={index}> | ||
{tip.isCustom ? ( | ||
<> | ||
{customSelected ? ( | ||
<input | ||
aria-describedby="tip-amount" | ||
className="block h-full w-full rounded-md border-0 px-2 py-1.5 text-gray-900 outline-none ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-cyan-500 sm:text-sm sm:leading-6" | ||
id="tip" | ||
name="tip" | ||
placeholder="0.00" | ||
type="number" | ||
value={customTip} | ||
onBlur={handleCustomTipBlur} | ||
onChange={(e) => setCustomTip(e.target.value)} | ||
/> | ||
) : ( | ||
<button | ||
className="w-full rounded-md bg-gray-100 px-3.5 py-2.5 font-medium text-cyan-700 shadow-sm hover:bg-gray-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cyan-600" | ||
type="button" | ||
onClick={handleCustomTip} | ||
> | ||
Custom | ||
</button> | ||
)} | ||
</> | ||
) : ( | ||
<button | ||
className={`${ | ||
index === activeTip | ||
? 'bg-cyan-200 text-cyan-700 hover:bg-cyan-100' | ||
: 'bg-cyan-600 text-white hover:bg-cyan-500' | ||
} w-full rounded-md px-3.5 py-2.5 shadow-sm file:font-medium focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cyan-600`} | ||
type="button" | ||
onClick={() => handleTipClick(index)} | ||
> | ||
{tip.tip}% | ||
</button> | ||
)} | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default TipAmount; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/* enter stlyes here */ |