diff --git a/src/api/types.ts b/src/api/types.ts index f75edad..474a3a3 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -27,6 +27,9 @@ export interface Goal { accountId: string transactionIds: string[] tagIds: string[] + + // Task 2 + icon?: string } export interface Tag { @@ -65,4 +68,4 @@ export enum ApplicationStatus { } export type ModalContent = Goal -export type ModalType = 'Goal' +export type ModalType = 'Goal' \ No newline at end of file diff --git a/src/ui/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx index 0779dda..698e035 100644 --- a/src/ui/features/goalmanager/GoalManager.tsx +++ b/src/ui/features/goalmanager/GoalManager.tsx @@ -2,17 +2,27 @@ import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons' import { faDollarSign, IconDefinition } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { MaterialUiPickersDate } from '@material-ui/pickers/typings/date' +import { BaseEmoji } from 'emoji-mart' import 'date-fns' import React, { useEffect, useState } from 'react' import styled from 'styled-components' + import { updateGoal as updateGoalApi } from '../../../api/lib' import { Goal } from '../../../api/types' -import { selectGoalsMap, updateGoal as updateGoalRedux } from '../../../store/goalsSlice' +import { + selectGoalsMap, + updateGoal as updateGoalRedux, +} from '../../../store/goalsSlice' import { useAppDispatch, useAppSelector } from '../../../store/hooks' + import DatePicker from '../../components/DatePicker' +import EmojiPicker from '../../components/EmojiPicker' import { Theme } from '../../components/Theme' +import GoalIcon from './GoalIcon' + type Props = { goal: Goal } + export function GoalManager(props: Props) { const dispatch = useAppDispatch() @@ -22,104 +32,255 @@ export function GoalManager(props: Props) { const [targetDate, setTargetDate] = useState(null) const [targetAmount, setTargetAmount] = useState(null) + const [icon, setIcon] = useState(null) + + const [isPickerOpen, setIsPickerOpen] = useState(false) + useEffect(() => { setName(props.goal.name) setTargetDate(props.goal.targetDate) setTargetAmount(props.goal.targetAmount) + + setIcon(props.goal.icon ?? null) }, [ props.goal.id, props.goal.name, props.goal.targetDate, props.goal.targetAmount, + props.goal.icon, ]) useEffect(() => { setName(goal.name) - }, [goal.name]) + setIcon(goal.icon ?? null) + }, [goal.name, goal.icon]) - const updateNameOnChange = (event: React.ChangeEvent) => { + const updateNameOnChange = ( + event: React.ChangeEvent + ) => { const nextName = event.target.value + setName(nextName) + const updatedGoal: Goal = { ...props.goal, name: nextName, } + dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } - const updateTargetAmountOnChange = (event: React.ChangeEvent) => { + const updateTargetAmountOnChange = ( + event: React.ChangeEvent + ) => { const nextTargetAmount = parseFloat(event.target.value) + setTargetAmount(nextTargetAmount) + const updatedGoal: Goal = { ...props.goal, name: name ?? props.goal.name, targetDate: targetDate ?? props.goal.targetDate, targetAmount: nextTargetAmount, + icon: icon ?? undefined, } + dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } - const pickDateOnChange = (date: MaterialUiPickersDate) => { + const pickDateOnChange = ( + date: MaterialUiPickersDate + ) => { if (date != null) { + setTargetDate(date) + const updatedGoal: Goal = { ...props.goal, + name: name ?? props.goal.name, - targetDate: date ?? props.goal.targetDate, - targetAmount: targetAmount ?? props.goal.targetAmount, + + targetDate: + date ?? props.goal.targetDate, + + targetAmount: + targetAmount ?? + props.goal.targetAmount, + + icon: icon ?? undefined, } + dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } } + const updateIcon = ( + emoji: BaseEmoji + ) => { + + setIcon(emoji.native) + + const updatedGoal: Goal = { + + ...props.goal, + + name: name ?? props.goal.name, + + targetDate: + targetDate ?? + props.goal.targetDate, + + targetAmount: + targetAmount ?? + props.goal.targetAmount, + + icon: emoji.native, + } + + dispatch(updateGoalRedux(updatedGoal)) + + updateGoalApi( + props.goal.id, + updatedGoal + ) + + setIsPickerOpen(false) + } + return ( - + + + + + setIsPickerOpen( + !isPickerOpen + ) + } + /> + + {isPickerOpen && ( + + + + )} - + + + - + + + + - + + + - + + + + - + + + - {props.goal.balance} + + + + {props.goal.balance} + + + + - + + + - {new Date(props.goal.created).toLocaleDateString()} + + + + {new Date( + props.goal.created + ).toLocaleDateString()} + + + + + ) } -type FieldProps = { name: string; icon: IconDefinition } -type AddIconButtonContainerProps = { shouldShow: boolean } -type GoalIconContainerProps = { shouldShow: boolean } -type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean } +type FieldProps = { + name: string + icon: IconDefinition +} + +const Field = ( + props: FieldProps +) => ( -const Field = (props: FieldProps) => ( - - {props.name} + + + + + + {props.name} + + + + ) const GoalManagerContainer = styled.div` @@ -139,6 +300,7 @@ const Group = styled.div` margin-top: 1.25rem; margin-bottom: 1.25rem; ` + const NameInput = styled.input` display: flex; background-color: transparent; @@ -146,15 +308,18 @@ const NameInput = styled.input` border: none; font-size: 4rem; font-weight: bold; - color: ${({ theme }: { theme: Theme }) => theme.text}; + + color: ${({ theme }: { theme: Theme }) => + theme.text}; ` const FieldName = styled.h1` font-size: 1.8rem; margin-left: 1rem; - color: rgba(174, 174, 174, 1); + color: rgba(174,174,174,1); font-weight: normal; ` + const FieldContainer = styled.div` display: flex; flex-direction: row; @@ -162,23 +327,32 @@ const FieldContainer = styled.div` width: 20rem; svg { - color: rgba(174, 174, 174, 1); + color: rgba(174,174,174,1); } ` + const StringValue = styled.h1` font-size: 1.8rem; font-weight: bold; ` + const StringInput = styled.input` display: flex; + background-color: transparent; + outline: none; + border: none; + font-size: 1.8rem; + font-weight: bold; - color: ${({ theme }: { theme: Theme }) => theme.text}; + + color: ${({ theme }: { theme: Theme }) => + theme.text}; ` const Value = styled.div` margin-left: 2rem; -` +` \ No newline at end of file