Import
Import
css'
return (
<div className="money-details-container">
<div className="balance-container">
<img
src="https://assets.ccbp.in/frontend/react-js/money-manager/balance-
image.png"
alt="balance"
className="details-img"
/>
<div>
Rs {balanceAmount}
</p>
</div>
</div>
<div className="income-container">
<img
src="https://assets.ccbp.in/frontend/react-js/money-manager/income-
image.png"
alt="income"
className="details-img"
/>
<div>
Rs {incomeAmount}
</p>
</div>
</div>
<div className="expenses-container">
<img
src="https://assets.ccbp.in/frontend/react-js/money-manager/expenses-
image.png"
alt="expenses"
className="details-img"
/>
<div>
Rs {expensesAmount}
</p>
</div>
</div>
</div>
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
margin-bottom: 32px;
margin-top: 32px;
.money-details-container {
flex-direction: row;
justify-content: space-between;
.balance-container {
display: flex;
justify-content: flex-start;
align-items: center;
background-color: #ecfccb;
width: 100%;
border-radius: 16px;
margin-bottom: 16px;
width: 30%;
margin-right: 0px;
.details-img {
width: 64px;
height: 64px;
margin: 24px;
.details-text {
color: #475569;
font-size: 12px;
font-family: 'Roboto';
line-height: 1.7;
margin: 0px;
margin-bottom: 8px;
.details-text {
font-size: 14px;
.details-money {
color: #475569;
font-size: 18px;
font-family: 'Roboto';
line-height: 1.3;
margin: 0px;
.details-money {
font-size: 24px;
.income-container {
display: flex;
justify-content: flex-start;
align-items: center;
background-color: #cffafe;
width: 100%;
border-radius: 16px;
margin-bottom: 16px;
.income-container {
width: 30%;
margin-right: 0px;
}
.expenses-container {
display: flex;
justify-content: flex-start;
align-items: center;
background-color: #ede9fe;
width: 100%;
min-width: 200px;
border-radius: 16px;
margin-bottom: 16px;
.expenses-container {
width: 30%;
margin-right: 0px;
import './index.css'
const transactionTypeOptions = [
optionId: 'INCOME',
displayText: 'Income',
},
optionId: 'EXPENSES',
displayText: 'Expenses',
},
state = {
transactionsList: [],
titleInput: '',
amountInput: '',
optionId: transactionTypeOptions[0].optionId,
deleteTransaction = id => {
)
this.setState({
transactionsList: updatedTransactionList,
})
event.preventDefault()
const newTransaction = {
id: v4(),
title: titleInput,
amount: parseInt(amountInput),
type: displayText,
this.setState(prevState => ({
titleInput: '',
amountInput: '',
optionId: transactionTypeOptions[0].optionId,
}))
this.setState({amountInput: event.target.value})
this.setState({titleInput: event.target.value})
getExpenses = () => {
let expensesAmount = 0
transactionsList.forEach(eachTransaction => {
expensesAmount += eachTransaction.amount
})
return expensesAmount
getIncome = () => {
let incomeAmount = 0
transactionsList.forEach(eachTransaction => {
})
return incomeAmount
getBalance = () => {
let balanceAmount = 0
let incomeAmount = 0
let expensesAmount = 0
transactionsList.forEach(eachTransaction => {
incomeAmount += eachTransaction.amount
} else {
expensesAmount += eachTransaction.amount
})
return balanceAmount
render() {
return (
<div className="app-container">
<div className="responsive-container">
<div className="header-container">
<p className="header-content">
</p>
</div>
<MoneyDetails
balanceAmount={balanceAmount}
incomeAmount={incomeAmount}
expensesAmount={expensesAmount}
/>
<div className="transaction-details">
<form className="transaction-form"
onSubmit={this.onAddTransaction}>
TITLE
</label>
<input
type="text"
id="title"
value={titleInput}
onChange={this.onChangeTitleInput}
className="input"
placeholder="TITLE"
/>
AMOUNT
</label>
<input
type="text"
id="amount"
className="input"
value={amountInput}
onChange={this.onChangeAmountInput}
placeholder="AMOUNT"
/>
TYPE
</label>
<select
id="select"
className="input"
value={optionId}
onChange={this.onChangeOptionId}
>
{transactionTypeOptions.map(eachOption => (
<option key={eachOption.optionId}
value={eachOption.optionId}>
{eachOption.displayText}
</option>
))}
</select>
Add
</button>
</form>
<div className="history-transactions">
<h1 className="transaction-header">History</h1>
<div className="transactions-table-container">
<ul className="transactions-table">
<li className="table-header">
<p className="table-header-cell">Title</p>
<p className="table-header-cell">Amount</p>
<p className="table-header-cell">Type</p>
</li>
{transactionsList.map(eachTransaction => (
<TransactionItem
key={eachTransaction.id}
transactionDetails={eachTransaction}
deleteTransaction={this.deleteTransaction}
/>
))}
</ul>
</div>
</div>
</div>
</div>
</div>
)
}
.app-container {
display: flex;
justify-content: center;
min-height: 100vh;
.responsive-container {
width: 80%;
max-width: 550px;
.responsive-container {
max-width: 1140px;
.header-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
background-image:
url('https://res.cloudinary.com/do4qwwms8/image/upload/v1626263091/
Money%20Manager/money-manager-header_rxlcrn.png');
background-size: cover;
height: 172px;
border-radius: 16px;
margin-top: 64px;
.header-container {
align-items: flex-start;
padding: 48px;
.heading {
color: #475569;
font-family: 'Roboto';
font-size: 24px;
font-weight: 500;
margin-bottom: 16px;
.heading {
font-size: 30px;
}
.header-content {
color: #475569;
font-family: 'Roboto';
font-size: 14px;
margin: 0px;
.header-content {
font-size: 18px;
.money-manager-text {
color: #0b69ff;
font-weight: 500;
.transaction-details {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 100%;
margin-bottom: 24px;
.transaction-details {
flex-direction: row;
.transaction-form {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: flex-start;
height: 424px;
width: 100%;
border-radius: 12px;
padding-top: 24px;
padding-right: 24px;
padding-left: 24px;
padding-bottom: 32px;
.transaction-form {
width: 40%;
padding-right: 43px;
padding-left: 48px;
.transaction-header {
color: #475569;
font-family: 'Roboto';
font-size: 16px;
font-weight: 500;
line-height: 1.6;
.transaction-header {
font-size: 20px;
.input-label {
color: #7e858e;
font-family: 'Roboto';
font-weight: 500;
font-size: 12px;
line-height: 1.4;
margin-bottom: 0px;
.input {
color: #1e293b;
background-color: #ffffff;
font-family: 'Roboto';
border-radius: 2px;
width: 100%;
height: 40px;
outline: none;
padding: 10px;
margin-bottom: 10px;
min-width: 157px;
.button {
background-color: #0b69ff;
color: #ffffff;
font-family: 'Roboto';
font-size: 14px;
font-weight: 600;
min-width: 25px;
border-radius: 6px;
border: none;
padding-left: 24px;
padding-top: 12px;
padding-bottom: 12px;
padding-right: 24px;
outline: none;
cursor: pointer;
margin-bottom: 10px;
.history-transactions {
width: 100%;
min-height: 326px;
border-radius: 12px;
padding-left: 24px;
padding-top: 12px;
padding-bottom: 12px;
padding-right: 24px;
margin-top: 32px;
.history-transactions {
width: 55%;
min-height: 424px;
padding-top: 24px;
padding-left: 28px;
padding-right: 41px;
padding-bottom: 40px;
margin-top: 0px;
margin-left: 32px;
.transactions-table-container {
height: 100%;
border-radius: 8px;
.transactions-table {
padding-left: 0;
margin: 0px;
}
.table-header {
display: flex;
justify-content: flex-start;
align-items: center;
height: 48px;
list-style-type: none;
padding-left: 10px;
padding-right: 10px;
.table-header {
padding-left: 24px;
padding-right: 24px;
.table-header-cell {
color: #334155;
font-family: 'Roboto';
font-size: 12px;
font-weight: 500;
width: 30%;
border-right: none;
font-size: 16px;
import './index.css'
deleteTransaction(id)
return (
<li className="table-row">
<p className="transaction-text">{title}</p>
<p className="transaction-text">{type}</p>
<div className="delete-container">
<button
className="delete-button"
type="button"
onClick={onDeleteTransaction}
data-testid="delete"
>
<img
className="delete-img"
src="https://assets.ccbp.in/frontend/react-js/money-manager/delete.png"
alt="delete"
/>
</button>
</div>
</li>
.table-row {
display: flex;
justify-content: space-between;
list-style-type: none;
padding-left: 10px;
padding-right: 10px;
}
@media screen and (min-width: 768px) {
.table-row {
padding-left: 24px;
padding-right: 24px;
.transaction-text {
color: #475569;
font-family: 'Roboto';
font-size: 12px;
width: 30%;
.transaction-text {
font-size: 14px;
.delete-container {
display: flex;
width: 10%;
.delete-button {
background: transparent;
outline: none;
e border: none;
cursor: pointer;
.delete-img {
background: transparent;
width: 20px;
height: 20px;
align-self: center;
border: none;
outline: none;
.delete-img {
margin-right: 24px;
Emoji game:
.emoji-item {
width: 32%;
list-style-type: none;
margin-top: 8px;
margin-bottom: 8px;
}
.emoji-btn {
background-color: #ffffff33;
cursor: pointer;
outline: none;
width: 100%;
height: 100px;
border: 3px solid #ffffff30;
border-radius: 24px;
}
.emoji-icon {
width: 50px;
}
Emoji Game:
import './index.css'
resetGame = () => {
this.setState({clickedEmojisList: [], isGameInProgress: true})
}
renderScoreCard = () => {
const {emojisList} = this.props
const {clickedEmojisList} = this.state
const isWon = clickedEmojisList.length === emojisList.length
return (
<WinOrLoseCard
isWon={isWon}
onClickPlayAgain={this.resetGame}
score={clickedEmojisList.length}
/>
)
}
clickEmoji = id => {
const {emojisList} = this.props
const {clickedEmojisList} = this.state
const isEmojiPresent = clickedEmojisList.includes(id)
const clickedEmojisLength = clickedEmojisList.length
if (isEmojiPresent) {
this.finishGameAndSetTopScore(clickedEmojisLength)
} else {
if (emojisList.length - 1 === clickedEmojisLength) {
this.finishGameAndSetTopScore(emojisList.length)
}
this.setState(previousState => ({
clickedEmojisList: [...previousState.clickedEmojisList, id],
}))
}
}
getShuffledEmojisList = () => {
const {emojisList} = this.props
renderEmojisList = () => {
const shuffledEmojisList = this.getShuffledEmojisList()
return (
<ul className="emojis-list-container">
{shuffledEmojisList.map(emojiObject => (
<EmojiCard
key={emojiObject.id}
emojiDetails={emojiObject}
clickEmoji={this.clickEmoji}
/>
))}
</ul>
)
}
render() {
const {clickedEmojisList, isGameInProgress, topScore} = this.state
return (
<div className="app-container">
<NavBar
currentScore={clickedEmojisList.length}
isGameInProgress={isGameInProgress}
topScore={topScore}
/>
<div className="emoji-game-body">
{isGameInProgress ? this.renderEmojisList() :
this.renderScoreCard()}
</div>
</div>
)
}
}
.app-container {
display: flex;
flex-direction: column;
background-image: linear-gradient(
to bottom right,
#9796f0 0.5%,
#fbc7d4 150%
);
min-height: 100vh;
}
.emoji-game-body {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.emojis-list-container {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
width: 80%;
margin: auto;
padding: 0px;
max-width: 1000px;
}
Navbar:
import './index.css'
const NavBar = props => {
const {currentScore, isGameInProgress, topScore} = props
return (
<nav className="nav-bar-container">
<div className="title-with-score-container">
<div className="logo-and-title-container">
<img
className="emoji-logo"
src="https://assets.ccbp.in/frontend/react-js/game-logo-img.png"
alt="emoji logo"
/>
<h1 className="title">Emoji Game</h1>
</div>
{isGameInProgress && (
<div className="scores-container">
<p className="score">Score: {currentScore}</p>
<p className="score">Top Score: {topScore}</p>
</div>
)}
</div>
</nav>
)
}
.nav-bar-container {
display: flex;
justify-content: center;
background-color: #ffffff33;
padding: 8px;
}
.title-with-score-container {
display: flex;
justify-content: space-between;
flex-basis: 80%;
max-width: 1000px;
}
.logo-and-title-container {
display: flex;
align-items: center;
}
.emoji-logo {
width: 24px;
}
.title {
color: #ffffff;
font-family: 'Roboto';
font-size: 16px;
font-weight: bold;
}
@media screen and (min-width: 768px) {
.title {
font-size: 24px;
}
}
.scores-container {
display: flex;
}
.score {
color: #ffffff;
font-family: 'Roboto';
font-size: 16px;
font-weight: 500;
margin-left: 16px;
}
import './index.css'
return (
<div className="win-or-lose-card">
<div className="details-section">
<h1 className="game-status">{gameStatus}</h1>
<p className="current-score-label">{scoreLabel}</p>
<p className="current-score-value">{score}/12</p>
<button
type="button"
className="play-again-button"
onClick={onClickPlayAgain}
>
Play Again
</button>
</div>
<div className="image-section">
<img className="win-or-lose-image" src={imageUrl} alt="win or
lose" />
</div>
</div>
)
}
.details-section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
order: 1;
}
.game-status {
color: #ffffff;
font-family: 'Roboto';
font-size: 32px;
font-weight: bold;
}
.current-score-label {
text-align: center;
color: #ffffff;
font-family: 'Roboto';
font-size: 18px;
font-weight: bold;
margin: 0px;
}
.current-score-value {
text-align: center;
color: #6a59ff;
font-family: 'Roboto';
font-size: 32px;
font-weight: bold;
margin: 0px;
}
.play-again-button {
color: #3d3d3d;
background-color: #ffce27;
font-family: 'Roboto';
font-size: 16px;
font-weight: bold;
border: none;
border-radius: 20px;
margin: 16px;
padding: 16px;
padding-right: 24px;
padding-left: 24px;
cursor: pointer;
outline: none;
}
.image-section {
display: flex;
justify-content: center;
order: 0;
}
.win-or-lose-image {
width: 150px;
height: 200px;
}
import './index.css'
const initialState = {
isTimerRunning: false,
timeElapsedInSeconds: 0,
timerLimitInMinutes: 25,
}
componentWillUnmount() {
this.clearTimerInterval()
}
onDecreaseTimerLimitInMinutes = () => {
const {timerLimitInMinutes} = this.state
if (timerLimitInMinutes > 1) {
this.setState(prevState => ({
timerLimitInMinutes: prevState.timerLimitInMinutes - 1,
}))
}
}
onIncreaseTimerLimitInMinutes = () =>
this.setState(prevState => ({
timerLimitInMinutes: prevState.timerLimitInMinutes + 1,
}))
renderTimerLimitController = () => {
const {timerLimitInMinutes, timeElapsedInSeconds} = this.state
const isButtonsDisabled = timeElapsedInSeconds > 0
return (
<div className="timer-limit-controller-container">
<p className="limit-label">Set Timer limit</p>
<div className="timer-limit-controller">
<button
className="limit-controller-button"
disabled={isButtonsDisabled}
onClick={this.onDecreaseTimerLimitInMinutes}
type="button"
>
-
</button>
<div className="limit-label-and-value-container">
<p className="limit-value">{timerLimitInMinutes}</p>
</div>
<button
className="limit-controller-button"
disabled={isButtonsDisabled}
onClick={this.onIncreaseTimerLimitInMinutes}
type="button"
>
+
</button>
</div>
</div>
)
}
onResetTimer = () => {
this.clearTimerInterval()
this.setState(initialState)
}
incrementTimeElapsedInSeconds = () => {
const {timerLimitInMinutes, timeElapsedInSeconds} = this.state
const isTimerCompleted = timeElapsedInSeconds ===
timerLimitInMinutes * 60
if (isTimerCompleted) {
this.clearTimerInterval()
this.setState({isTimerRunning: false})
} else {
this.setState(prevState => ({
timeElapsedInSeconds: prevState.timeElapsedInSeconds + 1,
}))
}
}
onStartOrPauseTimer = () => {
const {isTimerRunning, timeElapsedInSeconds, timerLimitInMinutes} =
this.state
const isTimerCompleted = timeElapsedInSeconds ===
timerLimitInMinutes * 60
if (isTimerCompleted) {
this.setState({timeElapsedInSeconds: 0})
}
if (isTimerRunning) {
this.clearTimerInterval()
} else {
this.intervalId = setInterval(this.incrementTimeElapsedInSeconds,
1000)
}
this.setState(prevState => ({isTimerRunning: !
prevState.isTimerRunning}))
}
renderTimerController = () => {
const {isTimerRunning} = this.state
const startOrPauseImageUrl = isTimerRunning
? 'https://assets.ccbp.in/frontend/react-js/pause-icon-img.png'
: 'https://assets.ccbp.in/frontend/react-js/play-icon-img.png'
const startOrPauseAltText = isTimerRunning ? 'pause icon' : 'play icon'
return (
<div className="timer-controller-container">
<button
className="timer-controller-btn"
onClick={this.onStartOrPauseTimer}
type="button"
>
<img
alt={startOrPauseAltText}
className="timer-controller-icon"
src={startOrPauseImageUrl}
/>
<p className="timer-controller-label">
{isTimerRunning ? 'Pause' : 'Start'}
</p>
</button>
<button
className="timer-controller-btn"
onClick={this.onResetTimer}
type="button"
>
<img
alt="reset icon"
className="timer-controller-icon"
src="https://assets.ccbp.in/frontend/react-js/reset-icon-img.png"
/>
<p className="timer-controller-label">Reset</p>
</button>
</div>
)
}
getElapsedSecondsInTimeFormat = () => {
const {timerLimitInMinutes, timeElapsedInSeconds} = this.state
const totalRemainingSeconds =
timerLimitInMinutes * 60 - timeElapsedInSeconds
const minutes = Math.floor(totalRemainingSeconds / 60)
const seconds = Math.floor(totalRemainingSeconds % 60)
const stringifiedMinutes = minutes > 9 ? minutes : `0${minutes}`
const stringifiedSeconds = seconds > 9 ? seconds : `0${seconds}`
return `${stringifiedMinutes}:${stringifiedSeconds}`
}
render() {
const {isTimerRunning} = this.state
const labelText = isTimerRunning ? 'Running' : 'Paused'
return (
<div className="app-container">
<h1 className="heading">Digital Timer</h1>
<div className="digital-timer-container">
<div className="timer-display-container">
<div className="elapsed-time-container">
<h1 className="elapsed-time">
{this.getElapsedSecondsInTimeFormat()}
</h1>
<p className="timer-state">{labelText}</p>
</div>
</div>
<div className="controls-container">
{this.renderTimerController()}
{this.renderTimerLimitController()}
</div>
</div>
</div>
)
}
}
.app-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
background-image: linear-gradient(
to bottom,
#ffffff,
#cffcf1,
#cffcf1,
#defafe
);
}
.heading {
color: #0f172a;
font-family: 'Roboto';
font-size: 32px;
font-weight: 500;
}
.digital-timer-container {
display: flex;
flex-direction: column;
align-items: center;
width: 90%;
}
.timer-display-container {
display: flex;
justify-content: center;
align-items: center;
width: 320px;
height: 250px;
background-image: url('https://assets.ccbp.in/frontend/react-js/digital-
timer-elapsed-bg.png');
background-size: cover;
background-position: center;
}
.elapsed-time-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #ffffff;
width: 100px;
height: 100px;
border-radius: 100%;
}
.elapsed-time {
color: #00d9f5;
font-family: 'Roboto';
font-size: 28px;
font-weight: bold;
margin: 0px;
}
.timer-state {
color: #1e293b;
font-family: 'Roboto';
font-size: 14px;
font-weight: bold;
margin: 0px;
}
.controls-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-grow: 1;
}
.timer-controller-container {
display: flex;
align-items: center;
}
.timer-controller-btn {
display: flex;
align-items: center;
background-color: transparent;
border: none;
margin-right: 16px;
margin-left: 16px;
padding: 0px;
cursor: pointer;
outline: none;
}
.timer-controller-icon {
width: 24px;
height: 24px;
margin-right: 8px;
}
.timer-controller-label {
color: #1e293b;
font-family: 'Roboto';
font-size: 24px;
font-weight: 500;
}
.timer-limit-controller-container {
display: flex;
flex-direction: column;
align-items: center;
}
.limit-label {
color: #1e293b;
font-family: 'Roboto';
font-size: 16px;
font-weight: 500;
}
.timer-limit-controller {
display: flex;
align-items: center;
}
.limit-controller-button {
color: #1e293b;
background-color: transparent;
font-family: 'Roboto';
font-size: 40px;
font-weight: 500;
border: none;
margin: 8px;
cursor: pointer;
outline: none;
}
.limit-label-and-value-container {
display: flex;
flex-direction: column;
align-items: center;
margin: 8px;
}
.limit-value {
text-align: center;
color: #1e293b;
background-image: linear-gradient(to right, #00f5a0, #00d9f5);
font-family: 'Roboto';
font-size: 32px;
font-weight: 500;
width: 100px;
border-radius: 8px;
margin: 0px;
padding: 8px;
}