From d4486ab5cadf320e2385c47960f02a995d970e9e Mon Sep 17 00:00:00 2001 From: Arif Khan Date: Thu, 25 Jun 2026 23:21:14 +0500 Subject: [PATCH] Added goal icon support and emoji picker --- src/api/types.ts | 1 + src/ui/features/goalmanager/GoalManager.tsx | 48 +++++++++++-- src/ui/pages/Main/goals/GoalCard.tsx | 77 ++++++++++++--------- 3 files changed, 89 insertions(+), 37 deletions(-) diff --git a/src/api/types.ts b/src/api/types.ts index f75edad..6193f2a 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -27,6 +27,7 @@ export interface Goal { accountId: string transactionIds: string[] tagIds: string[] + icon?: string } export interface Tag { diff --git a/src/ui/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx index 0779dda..42b72e4 100644 --- a/src/ui/features/goalmanager/GoalManager.tsx +++ b/src/ui/features/goalmanager/GoalManager.tsx @@ -11,6 +11,9 @@ 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 GoalIcon from './GoalIcon' +import { Picker } from 'emoji-mart' +import 'emoji-mart/css/emoji-mart.css' type Props = { goal: Goal } export function GoalManager(props: Props) { @@ -21,29 +24,37 @@ export function GoalManager(props: Props) { const [name, setName] = useState(null) const [targetDate, setTargetDate] = useState(null) const [targetAmount, setTargetAmount] = useState(null) + const [icon, setIcon] = useState(null) + const [isEmojiPickerOpen, setIsEmojiPickerOpen] = 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 nextName = event.target.value setName(nextName) + const updatedGoal: Goal = { ...props.goal, name: nextName, + icon: icon ?? props.goal.icon, } + dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } @@ -51,12 +62,15 @@ export function GoalManager(props: Props) { 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 ?? props.goal.icon, } + dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } @@ -64,19 +78,48 @@ export function GoalManager(props: Props) { 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, + icon: icon ?? props.goal.icon, } + dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } } + const pickEmojiOnClick = (emoji: any) => { + const selectedIcon = emoji.native + + setIcon(selectedIcon) + setIsEmojiPickerOpen(false) + + const updatedGoal: Goal = { + ...props.goal, + icon: selectedIcon, + } + + dispatch(updateGoalRedux(updatedGoal)) + updateGoalApi(props.goal.id, updatedGoal) + } + return ( + setIsEmojiPickerOpen(!isEmojiPickerOpen)} + /> + + {isEmojiPickerOpen && ( + + )} + @@ -111,9 +154,6 @@ export function GoalManager(props: Props) { } type FieldProps = { name: string; icon: IconDefinition } -type AddIconButtonContainerProps = { shouldShow: boolean } -type GoalIconContainerProps = { shouldShow: boolean } -type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean } const Field = (props: FieldProps) => ( diff --git a/src/ui/pages/Main/goals/GoalCard.tsx b/src/ui/pages/Main/goals/GoalCard.tsx index e8f6d0a..5c7f318 100644 --- a/src/ui/pages/Main/goals/GoalCard.tsx +++ b/src/ui/pages/Main/goals/GoalCard.tsx @@ -3,54 +3,65 @@ import styled from 'styled-components' import { selectGoalsMap } from '../../../../store/goalsSlice' import { useAppDispatch, useAppSelector } from '../../../../store/hooks' import { - setContent as setContentRedux, - setIsOpen as setIsOpenRedux, - setType as setTypeRedux + setContent as setContentRedux, + setIsOpen as setIsOpenRedux, + setType as setTypeRedux } from '../../../../store/modalSlice' import { Card } from '../../../components/Card' type Props = { id: string } export default function GoalCard(props: Props) { - const dispatch = useAppDispatch() + const dispatch = useAppDispatch() - const goal = useAppSelector(selectGoalsMap)[props.id] + const goal = useAppSelector(selectGoalsMap)[props.id] - const onClick = (event: React.MouseEvent) => { - event.stopPropagation() - dispatch(setContentRedux(goal)) - dispatch(setTypeRedux('Goal')) - dispatch(setIsOpenRedux(true)) - } + const onClick = (event: React.MouseEvent) => { + event.stopPropagation() + dispatch(setContentRedux(goal)) + dispatch(setTypeRedux('Goal')) + dispatch(setIsOpenRedux(true)) + } - const asLocaleDateString = (date: Date) => new Date(date).toLocaleDateString() + const asLocaleDateString = (date: Date) => + new Date(date).toLocaleDateString() - return ( - - ${goal.targetAmount} - {asLocaleDateString(goal.targetDate)} - - ) + return ( + + {goal.icon ?? '🎯'} + + ${goal.targetAmount} + + + {asLocaleDateString(goal.targetDate)} + + + ) } const Container = styled(Card)` - display: flex; - flex-direction: column; - min-height: 140px; - min-width: 140px; - width: 33%; - cursor: pointer; - margin-left: 2rem; - margin-right: 2rem; - border-radius: 2rem; - - align-items: center; + display: flex; + flex-direction: column; + min-height: 140px; + min-width: 140px; + width: 33%; + cursor: pointer; + margin-left: 2rem; + margin-right: 2rem; + border-radius: 2rem; + align-items: center; +` + +const GoalEmoji = styled.h1` + font-size: 3rem; + margin-bottom: 1rem; ` + const TargetAmount = styled.h2` - font-size: 2rem; + font-size: 2rem; ` const TargetDate = styled.h4` - color: rgba(174, 174, 174, 1); - font-size: 1rem; -` + color: rgba(174, 174, 174, 1); + font-size: 1rem; +` \ No newline at end of file