Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Icons #38

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface Goal {
accountId: string
transactionIds: string[]
tagIds: string[]
icon: string | null
}

export interface Tag {
Expand Down
2 changes: 1 addition & 1 deletion src/ui/features/goalmanager/GoalIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ export default function GoalIcon(props: Props) {
}

const Icon = styled.h1`
font-size: 6rem;
font-size: 5.5rem;
cursor: pointer;
`
267 changes: 172 additions & 95 deletions src/ui/features/goalmanager/GoalManager.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons'
import { faDollarSign, IconDefinition } from '@fortawesome/free-solid-svg-icons'
import { faDollarSign,faSmile, IconDefinition } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { MaterialUiPickersDate } from '@material-ui/pickers/typings/date'
import 'date-fns'
Expand All @@ -11,115 +11,175 @@ import { selectGoalsMap, updateGoal as updateGoalRedux } from '../../../store/go
import { useAppDispatch, useAppSelector } from '../../../store/hooks'
import DatePicker from '../../components/DatePicker'
import { Theme } from '../../components/Theme'

import { BaseEmoji, Picker } from 'emoji-mart'
import 'emoji-mart/css/emoji-mart.css'
import { TransparentButton } from '../../components/TransparentButton'
import GoalIcon from './GoalIcon'
type Props = { goal: Goal }
export function GoalManager(props: Props) {
const dispatch = useAppDispatch()

const goal = useAppSelector(selectGoalsMap)[props.goal.id]

const [name, setName] = useState<string | null>(null)
const [targetDate, setTargetDate] = useState<Date | null>(null)
const [targetAmount, setTargetAmount] = useState<number | null>(null)

useEffect(() => {
setName(props.goal.name)
setTargetDate(props.goal.targetDate)
setTargetAmount(props.goal.targetAmount)
}, [
props.goal.id,
props.goal.name,
props.goal.targetDate,
props.goal.targetAmount,
])

useEffect(() => {
setName(goal.name)
}, [goal.name])

const updateNameOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const nextName = event.target.value
setName(nextName)
const updatedGoal: Goal = {
...props.goal,
name: nextName,
const dispatch = useAppDispatch()

const goal = useAppSelector(selectGoalsMap)[props.goal.id]

const [name, setName] = useState<string | null>(null)
const [targetDate, setTargetDate] = useState<Date | null>(null)
const [targetAmount, setTargetAmount] = useState<number | null>(null)
const [icon, setIcon] = useState<string | null>(null)
const [emojiPickerIsOpen, setEmojiPickerIsOpen] = useState(false)
const [selectedEmoji, setSelectedEmoji] = useState<BaseEmoji | null>(null)

useEffect(() => {
setName(props.goal.name)
setTargetDate(props.goal.targetDate)
setTargetAmount(props.goal.targetAmount)
}, [
props.goal.id,
props.goal.name,
props.goal.targetDate,
props.goal.targetAmount,
])

useEffect(() => {
setName(goal.name)
}, [goal.name])

useEffect(() => {
setIcon(props.goal.icon)
}, [props.goal.id, props.goal.icon])

const updateNameOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const nextName = event.target.value
setName(nextName)
const updatedGoal: Goal = {
...props.goal,
name: nextName,
}
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
}
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
}

const updateTargetAmountOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
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,
const updateTargetAmountOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
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,
}
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
}
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
}

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,
}
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
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,
}
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
}
}
}
const hasIcon = () => icon != null

const addIconOnClick = (event: React.MouseEvent) => {
event.stopPropagation()
setEmojiPickerIsOpen(true)
}
const pickEmojiOnClick = (emoji: BaseEmoji, event: React.MouseEvent) => {
event.stopPropagation()

// Set selected emoji
setSelectedEmoji(emoji)

// Close emoji picker
setEmojiPickerIsOpen(false)

// Update goal locally
const updatedGoal: Goal = {
...props.goal,
icon: emoji.native ?? props.goal.icon,
name: name ?? props.goal.name,
targetDate: targetDate ?? props.goal.targetDate,
targetAmount: targetAmount ?? props.goal.targetAmount,
}

// Update Redux store
dispatch(updateGoalRedux(updatedGoal))

return (
<GoalManagerContainer>
<NameInput value={name ?? ''} onChange={updateNameOnChange} />

<Group>
<Field name="Target Date" icon={faCalendarAlt} />
<Value>
<DatePicker value={targetDate} onChange={pickDateOnChange} />
</Value>
</Group>

<Group>
<Field name="Target Amount" icon={faDollarSign} />
<Value>
<StringInput value={targetAmount ?? ''} onChange={updateTargetAmountOnChange} />
</Value>
</Group>

<Group>
<Field name="Balance" icon={faDollarSign} />
<Value>
<StringValue>{props.goal.balance}</StringValue>
</Value>
</Group>

<Group>
<Field name="Date Created" icon={faCalendarAlt} />
<Value>
<StringValue>{new Date(props.goal.created).toLocaleDateString()}</StringValue>
</Value>
</Group>
</GoalManagerContainer>
)
// Update database (if necessary)
updateGoalApi(props.goal.id, updatedGoal)
}


return (
<GoalManagerContainer>
<NameInput value={name ?? ''} onChange={updateNameOnChange} />

<Group>
<Field name="Target Date" icon={faCalendarAlt} />
<Value>
<DatePicker value={targetDate} onChange={pickDateOnChange} />
</Value>
</Group>

<Group>
<Field name="Target Amount" icon={faDollarSign} />
<Value>
<StringInput value={targetAmount ?? ''} onChange={updateTargetAmountOnChange} />
</Value>
</Group>

<Group>
<Field name="Balance" icon={faDollarSign} />
<Value>
<StringValue>{props.goal.balance}</StringValue>
</Value>
</Group>

<Group>
<Field name="Date Created" icon={faCalendarAlt} />
<Value>
<StringValue>{new Date(props.goal.created).toLocaleDateString()}</StringValue>
</Value>
</Group>
<Group>
<Field name="Icon" icon={faDollarSign} />
<Value>
<AddIconButtonContainer hasIcon={hasIcon()}>
<GoalIconContainer shouldShow={hasIcon()}>
<GoalIcon icon={goal.icon} onClick={addIconOnClick} />
</GoalIconContainer>
</AddIconButtonContainer>
</Value>
</Group>
<EmojiPickerContainer
isOpen={emojiPickerIsOpen}
hasIcon={selectedEmoji !=null}
onClick={(event) => event.stopPropagation()}
>
<Picker onClick={pickEmojiOnClick} />
</EmojiPickerContainer>

</GoalManagerContainer>
)
}

type FieldProps = { name: string; icon: IconDefinition }
type AddIconButtonContainerProps = { shouldShow: boolean }
type AddIconButtonContainerProps = { hasIcon: boolean }
type GoalIconContainerProps = { shouldShow: boolean }
type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean }


const Field = (props: FieldProps) => (
<FieldContainer>
<FontAwesomeIcon icon={props.icon} size="2x" />
<FieldName>{props.name}</FieldName>
</FieldContainer>
<FieldContainer>
<FontAwesomeIcon icon={props.icon} size="2x" />
<FieldName>{props.name}</FieldName>
</FieldContainer>
)

const GoalManagerContainer = styled.div`
Expand Down Expand Up @@ -182,3 +242,20 @@ const StringInput = styled.input`
const Value = styled.div`
margin-left: 2rem;
`
const EmojiPickerContainer = styled.div<EmojiPickerContainerProps>`
display: ${(props) => (props.isOpen ? 'flex' : 'none')};
position: absolute;
top: ${(props) => (props.hasIcon ? '10rem' : '2rem')};
left: 0;
`
const AddIconButtonContainer = styled.div<AddIconButtonContainerProps>`
display: ${(props) => (props.hasIcon ? 'flex' : 'none')};
`
const AddIconButtonText = styled.h1`
font-size: 1.8rem;
font-weight: bold;
`

const GoalIconContainer = styled.div<GoalIconContainerProps>`
display: ${(props) => (props.shouldShow ? 'flex' : 'none')};
`
6 changes: 5 additions & 1 deletion src/ui/pages/Main/goals/GoalCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export default function GoalCard(props: Props) {

return (
<Container key={goal.id} onClick={onClick}>
<TargetAmount>${goal.targetAmount}</TargetAmount>
<TargetAmount>${goal.targetAmount}</TargetAmount>
<Icon>{goal.icon}</Icon>
<TargetDate>{asLocaleDateString(goal.targetDate)}</TargetDate>
</Container>
)
Expand All @@ -54,3 +55,6 @@ const TargetDate = styled.h4`
color: rgba(174, 174, 174, 1);
font-size: 1rem;
`
const Icon = styled.h1`
font-size: 5.5rem;
`