0% found this document useful (0 votes)
8 views

Import

The document contains a React component for a Money Manager application, which allows users to track their income and expenses. It includes components for displaying financial details, adding transactions, and listing transaction history. The application utilizes state management to handle transactions and calculate balance, income, and expenses dynamically.

Uploaded by

Lakshmi Bhargavi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

Import

The document contains a React component for a Money Manager application, which allows users to track their income and expenses. It includes components for displaying financial details, adding transactions, and listing transaction history. The application utilizes state management to handle transactions and calculate balance, income, and expenses dynamically.

Uploaded by

Lakshmi Bhargavi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 53

import './index.

css'

const MoneyDetails = props => {

const {balanceAmount, incomeAmount, expensesAmount} = props

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>

<p className="details-text">Your Balance</p>

<p className="details-money" data-testid="balanceAmount">

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>

<p className="details-text">Your Income</p>

<p className="details-money" data-testid="incomeAmount">

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>

<p className="details-text">Your Expenses</p>

<p className="details-money" data-testid="expensesAmount">

Rs {expensesAmount}

</p>

</div>

</div>

</div>

export default MoneyDetails


.money-details-container {

display: flex;

flex-direction: column;

justify-content: center;

align-items: center;

width: 100%;

margin-bottom: 32px;

margin-top: 32px;

@media screen and (min-width: 768px) {

.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: 1px solid #84cc16;

border-radius: 16px;

margin-bottom: 16px;

@media screen and (min-width: 768px) {


.balance-container {

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;

@media screen and (min-width: 768px) {

.details-text {

font-size: 14px;

.details-money {

color: #475569;
font-size: 18px;

font-family: 'Roboto';

line-height: 1.3;

margin: 0px;

@media screen and (min-width: 768px) {

.details-money {

font-size: 24px;

.income-container {

display: flex;

justify-content: flex-start;

align-items: center;

background-color: #cffafe;

width: 100%;

border: 1px solid #06b6d4;

border-radius: 16px;

margin-bottom: 16px;

@media screen and (min-width: 768px) {

.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: 1px solid #7c3aed;

border-radius: 16px;

margin-bottom: 16px;

@media screen and (min-width: 768px) {

.expenses-container {

width: 30%;

margin-right: 0px;

import {Component} from 'react'

import {v4} from 'uuid'


import TransactionItem from '../TransactionItem'

import MoneyDetails from '../MoneyDetails'

import './index.css'

const transactionTypeOptions = [

optionId: 'INCOME',

displayText: 'Income',

},

optionId: 'EXPENSES',

displayText: 'Expenses',

},

class MoneyManager extends Component {

state = {

transactionsList: [],

titleInput: '',

amountInput: '',

optionId: transactionTypeOptions[0].optionId,

deleteTransaction = id => {

const {transactionsList} = this.state

const updatedTransactionList = transactionsList.filter(

eachTransaction => id !== eachTransaction.id,

)
this.setState({

transactionsList: updatedTransactionList,

})

onAddTransaction = event => {

event.preventDefault()

const {titleInput, amountInput, optionId} = this.state

const typeOption = transactionTypeOptions.find(

eachTransaction => eachTransaction.optionId === optionId,

const {displayText} = typeOption

const newTransaction = {

id: v4(),

title: titleInput,

amount: parseInt(amountInput),

type: displayText,

this.setState(prevState => ({

transactionsList: [...prevState.transactionsList, newTransaction],

titleInput: '',

amountInput: '',

optionId: transactionTypeOptions[0].optionId,

}))

onChangeOptionId = event => {


this.setState({optionId: event.target.value})

onChangeAmountInput = event => {

this.setState({amountInput: event.target.value})

onChangeTitleInput = event => {

this.setState({titleInput: event.target.value})

getExpenses = () => {

const {transactionsList} = this.state

let expensesAmount = 0

transactionsList.forEach(eachTransaction => {

if (eachTransaction.type === transactionTypeOptions[1].displayText) {

expensesAmount += eachTransaction.amount

})

return expensesAmount

getIncome = () => {

const {transactionsList} = this.state

let incomeAmount = 0

transactionsList.forEach(eachTransaction => {

if (eachTransaction.type === transactionTypeOptions[0].displayText) {


incomeAmount += eachTransaction.amount

})

return incomeAmount

getBalance = () => {

const {transactionsList} = this.state

let balanceAmount = 0

let incomeAmount = 0

let expensesAmount = 0

transactionsList.forEach(eachTransaction => {

if (eachTransaction.type === transactionTypeOptions[0].displayText) {

incomeAmount += eachTransaction.amount

} else {

expensesAmount += eachTransaction.amount

})

balanceAmount = incomeAmount - expensesAmount

return balanceAmount

render() {

const {titleInput, amountInput, optionId, transactionsList} = this.state

const balanceAmount = this.getBalance()


const incomeAmount = this.getIncome()

const expensesAmount = this.getExpenses()

return (

<div className="app-container">

<div className="responsive-container">

<div className="header-container">

<h1 className="heading">Hi, Richard</h1>

<p className="header-content">

Welcome back to your

<span className="money-manager-text"> Money


Manager</span>

</p>

</div>

<MoneyDetails

balanceAmount={balanceAmount}

incomeAmount={incomeAmount}

expensesAmount={expensesAmount}

/>

<div className="transaction-details">

<form className="transaction-form"
onSubmit={this.onAddTransaction}>

<h1 className="transaction-header">Add Transaction</h1>

<label className="input-label" htmlFor="title">

TITLE

</label>

<input

type="text"

id="title"

value={titleInput}
onChange={this.onChangeTitleInput}

className="input"

placeholder="TITLE"

/>

<label className="input-label" htmlFor="amount">

AMOUNT

</label>

<input

type="text"

id="amount"

className="input"

value={amountInput}

onChange={this.onChangeAmountInput}

placeholder="AMOUNT"

/>

<label className="input-label" htmlFor="select">

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>

<button type="submit" className="button">

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>

)
}

export default MoneyManager

.app-container {

display: flex;

justify-content: center;

min-height: 100vh;

.responsive-container {

width: 80%;

max-width: 550px;

@media screen and (min-width: 768px) {

.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;

@media screen and (min-width: 768px) {

.header-container {

align-items: flex-start;

padding: 48px;

.heading {

color: #475569;

font-family: 'Roboto';

font-size: 24px;

font-weight: 500;

margin-bottom: 16px;

@media screen and (min-width: 768px) {

.heading {

font-size: 30px;

}
.header-content {

color: #475569;

font-family: 'Roboto';

font-size: 14px;

margin: 0px;

@media screen and (min-width: 768px) {

.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;

@media screen and (min-width: 768px) {

.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: 1px solid #cbd5e1;

border-radius: 12px;

padding-top: 24px;

padding-right: 24px;

padding-left: 24px;

padding-bottom: 32px;

@media screen and (min-width: 768px) {

.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;

@media screen and (min-width: 768px) {

.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: 1px solid #d7dfe9;

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: 1px solid #cbd5e1;

border-radius: 12px;
padding-left: 24px;

padding-top: 12px;

padding-bottom: 12px;

padding-right: 24px;

margin-top: 32px;

@media screen and (min-width: 768px) {

.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;

border: 1px solid #cbd5e1;

padding-left: 10px;

padding-right: 10px;

@media screen and (min-width: 768px) {

.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;

@media screen and (min-width: 768px) {


.table-header-cell {

font-size: 16px;

import './index.css'

const TransactionItem = props => {

const {transactionDetails, deleteTransaction} = props

const {id, title, amount, type} = transactionDetails

const onDeleteTransaction = () => {

deleteTransaction(id)

return (

<li className="table-row">

<p className="transaction-text">{title}</p>

<p className="transaction-text">Rs {amount}</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>

export default TransactionItem

.table-row {

display: flex;

justify-content: space-between;

list-style-type: none;

border-bottom: 1px solid #cbd5e1;

border-right: 1px solid #cbd5e1;

border-left: 1px solid #cbd5e1;

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%;

@media screen and (min-width: 768px) {

.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;

@media screen and (min-width: 768px) {

.delete-img {

margin-right: 24px;

Emoji game:

Emoji card: import './index.css'

const EmojiCard = props => {


const {emojiDetails, clickEmoji} = props
const {id, emojiName, emojiUrl} = emojiDetails

const onClickEmojiCard = () => {


clickEmoji(id)
}
return (
<li className="emoji-item">
<button type="button" className="emoji-btn"
onClick={onClickEmojiCard}>
<img className="emoji-icon" src={emojiUrl} alt={emojiName} />
</button>
</li>
)
}

export default EmojiCard

.emoji-item {
width: 32%;
list-style-type: none;
margin-top: 8px;
margin-bottom: 8px;
}

@media screen and (min-width: 992px) {


.emoji-item {
width: 24%;
}
}

.emoji-btn {
background-color: #ffffff33;
cursor: pointer;
outline: none;
width: 100%;
height: 100px;
border: 3px solid #ffffff30;
border-radius: 24px;
}

@media screen and (min-width: 768px) {


.emoji-btn {
height: 200px;
}
}

.emoji-icon {
width: 50px;
}

@media screen and (min-width: 768px) {


.emoji-icon {
width: 100px;
}
}

Emoji Game:

import {Component} from 'react'

import EmojiCard from '../EmojiCard'


import NavBar from '../NavBar'
import WinOrLoseCard from '../WinOrLoseCard'

import './index.css'

class EmojiGame extends Component {


state = {
clickedEmojisList: [],
isGameInProgress: true,
topScore: 0,
}

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}
/>
)
}

finishGameAndSetTopScore = currentScore => {


const {topScore} = this.state
let newTopScore = topScore

if (currentScore > topScore) {


newTopScore = currentScore
}

this.setState({topScore: newTopScore, isGameInProgress: false})


}

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

return emojisList.sort(() => Math.random() - 0.5)


}

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>
)
}
}

export default EmojiGame

.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>
)
}

export default NavBar

.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;
}

@media screen and (min-width: 768px) {


.emoji-logo {
width: 48px;
}
}

.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;
}

@media screen and (min-width: 768px) {


.score {
font-size: 24px;
margin-left: 32px;
}
}

Win or lose card:

import './index.css'

const LOSE_IMAGE = 'https://assets.ccbp.in/frontend/react-js/lose-game-


img.png'
const WON_IMAGE = 'https://assets.ccbp.in/frontend/react-js/won-game-
img.png'

const WinOrLoseCard = props => {


const {isWon, onClickPlayAgain, score} = props
const imageUrl = isWon ? WON_IMAGE : LOSE_IMAGE
const gameStatus = isWon ? 'You Won' : 'You Lose'
const scoreLabel = isWon ? 'Best Score' : 'Score'

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>
)
}

export default WinOrLoseCard


.win-or-lose-card {
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: #ffffff33;
width: 85%;
border: 3px solid #ffffff30;
border-radius: 24px;
margin: auto;
padding: 16px;
}

@media screen and (min-width: 768px) {


.win-or-lose-card {
width: 80%;
padding: 32px;
}
}

@media screen and (min-width: 992px) {


.win-or-lose-card {
flex-direction: row;
width: 70%;
}
}

@media screen and (min-width: 1200px) {


.win-or-lose-card {
width: 60%;
}
}

.details-section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
order: 1;
}

@media screen and (min-width: 992px) {


.details-section {
order: 0;
}
}

.game-status {
color: #ffffff;
font-family: 'Roboto';
font-size: 32px;
font-weight: bold;
}

@media screen and (min-width: 992px) {


.game-status {
font-size: 64px;
}
}

.current-score-label {
text-align: center;
color: #ffffff;
font-family: 'Roboto';
font-size: 18px;
font-weight: bold;
margin: 0px;
}

@media screen and (min-width: 992px) {


.current-score-label {
font-size: 36px;
}
}

.current-score-value {
text-align: center;
color: #6a59ff;
font-family: 'Roboto';
font-size: 32px;
font-weight: bold;
margin: 0px;
}

@media screen and (min-width: 768px) {


.current-score-value {
font-size: 48px;
}
}

.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;
}

@media screen and (min-width: 992px) {


.image-section {
order: 1;
}
}

.win-or-lose-image {
width: 150px;
height: 200px;
}

@media screen and (min-width: 992px) {


.win-or-lose-image {
width: 350px;
height: 450px;
}
}

import {Component} from 'react'

import './index.css'

const initialState = {
isTimerRunning: false,
timeElapsedInSeconds: 0,
timerLimitInMinutes: 25,
}

class DigitalTimer extends Component {


state = initialState

componentWillUnmount() {
this.clearTimerInterval()
}

clearTimerInterval = () => clearInterval(this.intervalId)

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>
)
}
}

export default DigitalTimer

.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
);
}

@media screen and (min-width: 768px) {


.app-container {
background-image: linear-gradient(
to right,
#ffffff,
#cffcf1,
#cffcf1,
#defafe
);
}
}

.heading {
color: #0f172a;
font-family: 'Roboto';
font-size: 32px;
font-weight: 500;
}

@media screen and (min-width: 768px) {


.heading {
font-size: 48px;
}
}

.digital-timer-container {
display: flex;
flex-direction: column;
align-items: center;
width: 90%;
}

@media screen and (min-width: 768px) {


.digital-timer-container {
flex-direction: row;
max-width: 1140px;
}
}

.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;
}

@media (min-width: 576px) {


.timer-display-container {
width: 450px;
height: 320px;
}
}

@media screen and (min-width: 768px) {


.timer-display-container {
width: 600px;
height: 450px;
}
}

@media (min-width: 992px) {


.timer-display-container {
width: 600px;
height: 600px;
}
}

.elapsed-time-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #ffffff;
width: 100px;
height: 100px;
border-radius: 100%;
}

@media (min-width: 576px) {


.elapsed-time-container {
width: 150px;
height: 150px;
}
}

@media screen and (min-width: 768px) {


.elapsed-time-container {
width: 200px;
height: 200px;
}
}

.elapsed-time {
color: #00d9f5;
font-family: 'Roboto';
font-size: 28px;
font-weight: bold;
margin: 0px;
}

@media screen and (min-width: 768px) {


.elapsed-time {
font-size: 48px;
}
}

.timer-state {
color: #1e293b;
font-family: 'Roboto';
font-size: 14px;
font-weight: bold;
margin: 0px;
}

@media screen and (min-width: 768px) {


.timer-state {
font-size: 24px;
}
}

.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;
}

@media (min-width: 576px) {


.timer-controller-icon {
width: 36px;
height: 36px;
margin-right: 12px;
}
}

.timer-controller-label {
color: #1e293b;
font-family: 'Roboto';
font-size: 24px;
font-weight: 500;
}

@media (min-width: 576px) {


.timer-controller-label {
font-size: 32px;
}
}

.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;
}

You might also like