Skip to content

Commit

Permalink
feat: Adds functionality for the button
Browse files Browse the repository at this point in the history
  • Loading branch information
KajiyamaVK committed Jul 11, 2023
1 parent b884eca commit ae8b992
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 137 deletions.
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@hookform/resolvers": "^3.1.1",
"@types/styled-components": "^5.1.26",
"date-fns": "^2.30.0",
"phosphor-react": "^1.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
52 changes: 52 additions & 0 deletions src/components/CountDown/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useState, useEffect } from 'react'
import { CountdownContainer, Separator } from './styles'
import { differenceInSeconds } from 'date-fns'

export default function CountDown() {
const [amountSecondsPassed, setAmountSecondsPassed] = useState<number>(0)
const totalSeconds = activeCycle ? activeCycle.minutesAmount * 60 : 0

useEffect(() => {
let interval: number

if (activeCycle) {
interval = setInterval(() => {
const secondsDifference = differenceInSeconds(
new Date(),
activeCycle.startDate,
)

if (secondsDifference >= totalSeconds) {
setCycles((state) =>
state.map((cycle) => {
if (cycle.id === activeCycle.id) {
const now = new Date()
return { ...cycle, finishedDate: now }
} else {
return cycle
}
}),
)
setAmountSecondsPassed(totalSeconds)
clearInterval(interval)
} else {
setAmountSecondsPassed(secondsDifference)
}
}, 1000)
}

return () => {
clearInterval(interval)
}
}, [activeCycle, totalSeconds, activeCycleId])

return (
<CountdownContainer>
<span>{minutes[0]}</span>
<span>{minutes[1]}</span>
<Separator>:</Separator>
<span>{seconds[0]}</span>
<span>{seconds[1]}</span>
</CountdownContainer>
)
}
24 changes: 24 additions & 0 deletions src/components/CountDown/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { styled } from 'styled-components'

export const CountdownContainer = styled.div`
font-family: 'Roboto-Mono', monospace;
font-size: 10rem;
line-height: 8rem;
color: ${(props) => props.theme['gray-100']};
display: flex;
gap: 1rem;
span {
background: ${(props) => props.theme['gray-700']};
padding: 2rem 1rem;
border-radius: 8px;
}
`

export const Separator = styled.div`
padding: 2rem 0;
color: ${(props) => props.theme['green-500']};
width: 4rem;
overflow: hidden;
display: flex;
justify-content: center;
`
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ export const newCycleFormValidationSchema = zod.object({
task: zod.string().min(1, validationsMessages.task),
minutesAmount: zod
.number()
.min(5, validationsMessages.minutesAmount.min)
.min(1, validationsMessages.minutesAmount.min)
.max(60, validationsMessages.minutesAmount.max),
})
54 changes: 54 additions & 0 deletions src/components/NewCyclesForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { FormContainer, TaskInput, MinutesAmountInput } from './styles'
import { useForm } from 'react-hook-form'
import { ICycle } from '../../pages/Home/interfaces'
import * as zod from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { newCycleFormValidationSchema } from './formValidations'

interface INewCyclesForm {
activeCycle: ICycle | undefined
}

type formType = zod.infer<typeof newCycleFormValidationSchema>

export default function NewCyclesForm({ activeCycle }: INewCyclesForm) {
const { register, handleSubmit, formState, reset, watch } = useForm<formType>(
{
resolver: zodResolver(newCycleFormValidationSchema),
defaultValues: {
minutesAmount: 0,
task: '',
},
},
)
return (
<FormContainer>
<label htmlFor="task">Going to work with</label>
<TaskInput
type="text"
id="task"
placeholder="Give a name for the task"
list="task-suggestions"
{...register('task')}
disabled={!!activeCycle}
/>
<datalist id="task-suggestions">
<option value="Projeto 1" />
<option value="Projeto 2" />
<option value="Projeto 3" />
<option value="Projeto 4" />
</datalist>
<label htmlFor="minutesAmount">for</label>
<MinutesAmountInput
type="number"
id="minutesAmount"
placeholder="00"
step={1}
required
disabled={!!activeCycle}
{...register('minutesAmount', { valueAsNumber: true })}
/>
<span>minutes</span>
</FormContainer>
)
}
44 changes: 44 additions & 0 deletions src/components/NewCyclesForm/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { styled } from 'styled-components'

export const FormContainer = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
color: ${(props) => props.theme['gray-100']};
font-size: 1.125rem;
font-weight: bold;
flex-wrap: wrap;
`

const BaseInput = styled.input`
background: transparent;
height: 2.5rem;
border: 0;
border-bottom: 2px solid ${(props) => props.theme['gray-500']};
font-weight: bold;
font-size: 1.125rem;
padding: 0 0.5%;
color: ${(props) => props.theme['gray-100']};
&:focus {
box-shadow: none;
border-color: ${(props) => props.theme['green-500']};
}
&::placeholder {
color: ${(props) => props.theme['gray-500']};
}
`

export const TaskInput = styled(BaseInput)`
flex: 1;
&::-webkit-calendar-picker-indicator {
display: none !important;
}
`

export const MinutesAmountInput = styled(BaseInput)`
width: 4rem;
`
121 changes: 54 additions & 67 deletions src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,100 +1,87 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { Play } from 'phosphor-react'
import { differenceInSeconds } from 'date-fns'
import { HandPalm, Play } from 'phosphor-react'
import {
CountdownContainer,
FormContainer,
HomeContainer,
MinutesAmountInput,
Separator,
StartCountDownButton,
TaskInput,
StopCountDownButton,
} from './styles'
import { useForm } from 'react-hook-form'
import * as zod from 'zod'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { ICycle } from './interfaces'
import { newCycleFormValidationSchema } from './formValidations'
import { showError } from './functions'

type formType = zod.infer<typeof newCycleFormValidationSchema>
import NewCyclesForm from '../../components/NewCyclesForm'
import CountDown from '../../components/CountDown'

export default function Home() {
const teste: formType = {
minutesAmount: 1,
task: 'asd',
}
console.log(teste)
const [cycles, setCycles] = useState<ICycle[]>([])
const [activeCycleId, setActiveCycleId] = useState<number>(0)
const [amountSecondsPassed, setAmountSecondsPassed] = useState<number>(0)

const activeCycle = cycles.find((cycle) => cycle.id === activeCycleId)
const { register, handleSubmit, formState, reset } = useForm<formType>({
resolver: zodResolver(newCycleFormValidationSchema),
defaultValues: {
minutesAmount: 0,
task: '',
},
})
const totalSeconds = activeCycle ? activeCycle.minutesAmount * 60 : 0

const currentSeconds = activeCycle ? totalSeconds - amountSecondsPassed : 0
const taskInput = watch('task')
const minutesInput = watch('minutesAmount')
const isSubmitDisabled = !(taskInput && minutesInput)
const minutesAmount: number = Math.floor(currentSeconds / 60)
const secondsAmount: number = currentSeconds % 60
const minutes = String(minutesAmount).padStart(2, '0')
const seconds = String(secondsAmount).padStart(2, '0')

function saveFormData(data: formType) {
const newCycle: ICycle = {
id: new Date().getTime(),
task: data.task,
minutesAmount: data.minutesAmount,
startDate: new Date(),
}
setCycles([...cycles, newCycle])
setActiveCycleId(newCycle.id)
setAmountSecondsPassed(0)
}

useEffect(() => {
if (activeCycle) {
document.title = `${minutes}:${seconds}`
}
}, [minutes, seconds])

function handleInterruptCycle() {
if (activeCycle) {
setCycles((state) =>
state.map((cycle) => {
if (cycle.id === activeCycle.id) {
const now = new Date()

return { ...cycle, interruptedDate: now }
} else {
return cycle
}
}),
)
}
setActiveCycleId(0)
reset()
}

return (
<HomeContainer>
<form onSubmit={handleSubmit(saveFormData)}>
<FormContainer>
<label htmlFor="task">Going to work with</label>
<TaskInput
type="text"
id="task"
placeholder="Give a name for the task"
list="task-suggestions"
{...register('task')}
/>
<datalist id="task-suggestions">
<option value="Projeto 1" />
<option value="Projeto 2" />
<option value="Projeto 3" />
<option value="Projeto 4" />
</datalist>
<label htmlFor="minutesAmount">for</label>
<MinutesAmountInput
type="number"
id="minutesAmount"
placeholder="00"
step={5}
required
{...register('minutesAmount', { valueAsNumber: true })}
/>
<span>minutes</span>
</FormContainer>
<CountdownContainer>
<span>{minutes[0]}</span>
<span>{minutes[1]}</span>
<Separator>:</Separator>
<span>{seconds[0]}</span>
<span>{seconds[1]}</span>
</CountdownContainer>
<StartCountDownButton
type="submit"
onClick={() => showError(formState.errors)}
>
<Play size={24} />
Begin
</StartCountDownButton>
<NewCyclesForm activeCycle={activeCycle} />
<CountDown />
{activeCycle ? (
<StopCountDownButton type="button" onClick={handleInterruptCycle}>
<HandPalm size={24} />
Stop
</StopCountDownButton>
) : (
<StartCountDownButton
type="submit"
onClick={() => showError(formState.errors)}
disabled={isSubmitDisabled}
>
<Play size={24} />
Begin
</StartCountDownButton>
)}
</form>
</HomeContainer>
)
Expand Down
4 changes: 3 additions & 1 deletion src/pages/Home/interfaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ export interface ICycle {
id: number
task: string
minutesAmount: number
startDate: Date
finishedDate?: Date
interruptedCycle?: Date
}

Loading

0 comments on commit ae8b992

Please sign in to comment.