From 67ed128e0f65862f69b6e20fd33a8c08aef56493 Mon Sep 17 00:00:00 2001 From: Hussain Jameel Date: Fri, 30 Jan 2026 12:53:05 +0500 Subject: [PATCH 1/2] Completed Task 2: Implemented emoji picker and modified GoalManager UI --- src/api/types.ts | 1 + src/ui/components/EmojiPicker.tsx | 2 +- src/ui/features/goalmanager/GoalManager.tsx | 337 ++++++++++++++------ src/ui/pages/Main/goals/GoalCard.tsx | 5 + 4 files changed, 248 insertions(+), 97 deletions(-) diff --git a/src/api/types.ts b/src/api/types.ts index f75edad..8cbabc0 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 | null } export interface Tag { diff --git a/src/ui/components/EmojiPicker.tsx b/src/ui/components/EmojiPicker.tsx index 00bb54d..5d6533b 100644 --- a/src/ui/components/EmojiPicker.tsx +++ b/src/ui/components/EmojiPicker.tsx @@ -17,4 +17,4 @@ export default function EmojiPicker(props: Props) { color="primary" /> ) -} +} \ No newline at end of file diff --git a/src/ui/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx index 0779dda..af0d3a0 100644 --- a/src/ui/features/goalmanager/GoalManager.tsx +++ b/src/ui/features/goalmanager/GoalManager.tsx @@ -1,62 +1,252 @@ -import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons' +import { faCalendarAlt, faSmile } 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 React, { useEffect, useState, useRef } 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 { useAppDispatch, useAppSelector } from '../../../store/hooks' import DatePicker from '../../components/DatePicker' +import EmojiPicker from '../../components/EmojiPicker' +import { TransparentButton } from '../../components/TransparentButton' +import GoalIcon from './GoalIcon' import { Theme } from '../../components/Theme' type Props = { goal: Goal } + +// Styled Components +type AddIconButtonContainerProps = { hasIcon: boolean } +type GoalIconContainerProps = { shouldShow: boolean } +type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean } + +const AddIconButtonContainer = styled.div` + display: ${(props) => (props.hasIcon ? 'none' : 'flex')}; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + margin-bottom: 2rem; + width: 100%; +` + +const AddIconButtonText = styled.p` + font-size: 1.5rem; + margin-top: 1rem; + color: ${({ theme }: { theme: Theme }) => theme.text}; +` + +const GoalIconContainer = styled.div` + display: ${(props) => (props.shouldShow ? 'flex' : 'none')}; + justify-content: center; + margin-bottom: 2rem; + width: 100%; +` + +const EmojiPickerContainer = styled.div` + display: ${(props) => (props.isOpen ? 'flex' : 'none')}; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10000; + background: white; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); + border: 1px solid #ddd; + padding: 10px; + + /* Limit size */ + max-width: 90vw; + max-height: 80vh; + overflow: auto; + + /* Center the picker */ + justify-content: center; + align-items: center; +` + +const GoalManagerContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + height: 100%; + width: 100%; + position: relative; + padding: 2rem; +` + +const Group = styled.div` + display: flex; + flex-direction: row; + width: 100%; + margin-top: 1.25rem; + margin-bottom: 1.25rem; +` + +const NameInput = styled.input` + display: flex; + background-color: transparent; + outline: none; + border: none; + font-size: 4rem; + font-weight: bold; + color: ${({ theme }: { theme: Theme }) => theme.text}; + width: 100%; + margin-bottom: 2rem; +` + +const FieldName = styled.h1` + font-size: 1.8rem; + margin-left: 1rem; + color: rgba(174, 174, 174, 1); + font-weight: normal; +` + +const FieldContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + width: 20rem; + + svg { + 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}; + width: 100%; +` + +const Value = styled.div` + margin-left: 2rem; + flex: 1; +` + +// Field Component +type FieldProps = { name: string; icon: IconDefinition } + +const Field = (props: FieldProps) => ( + + + {props.name} + +) + +// Main GoalManager Component export function GoalManager(props: Props) { const dispatch = useAppDispatch() - const goal = useAppSelector(selectGoalsMap)[props.goal.id] + const pickerRef = useRef(null) const [name, setName] = useState(null) const [targetDate, setTargetDate] = useState(null) const [targetAmount, setTargetAmount] = useState(null) + const [icon, setIcon] = useState(null) + const [emojiPickerIsOpen, setEmojiPickerIsOpen] = useState(false) + + // Click outside handler + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (pickerRef.current && !pickerRef.current.contains(event.target as Node)) { + setEmojiPickerIsOpen(false) + } + } + + if (emojiPickerIsOpen) { + document.addEventListener('mousedown', handleClickOutside) + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [emojiPickerIsOpen]) + // Initialize state from props useEffect(() => { setName(props.goal.name) setTargetDate(props.goal.targetDate) setTargetAmount(props.goal.targetAmount) + setIcon(props.goal.icon) }, [ props.goal.id, props.goal.name, props.goal.targetDate, props.goal.targetAmount, + props.goal.icon, ]) + // Update state when Redux goal changes useEffect(() => { setName(goal.name) - }, [goal.name]) + setIcon(goal.icon) + }, [goal.name, goal.icon]) + + // Helper functions + const hasIcon = () => icon != null && icon !== '' + + const addIconOnClick = (event: React.MouseEvent) => { + event.stopPropagation() + setEmojiPickerIsOpen(true) + } + + const pickEmojiOnClick = (emoji: BaseEmoji, event: React.MouseEvent) => { + event.stopPropagation() + + setIcon(emoji.native) + setEmojiPickerIsOpen(false) - const updateNameOnChange = (event: React.ChangeEvent) => { - const nextName = event.target.value - setName(nextName) const updatedGoal: Goal = { ...props.goal, - name: nextName, + icon: emoji.native ?? props.goal.icon, + name: name ?? props.goal.name, + targetDate: targetDate ?? props.goal.targetDate, + targetAmount: targetAmount ?? props.goal.targetAmount, } + dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } - const updateTargetAmountOnChange = (event: React.ChangeEvent) => { - const nextTargetAmount = parseFloat(event.target.value) - setTargetAmount(nextTargetAmount) - const updatedGoal: Goal = { + const getUpdatedGoal = (updates: Partial): Goal => { + return { ...props.goal, name: name ?? props.goal.name, targetDate: targetDate ?? props.goal.targetDate, - targetAmount: nextTargetAmount, + targetAmount: targetAmount ?? props.goal.targetAmount, + icon: icon ?? props.goal.icon, + ...updates, } + } + + const updateNameOnChange = (event: React.ChangeEvent) => { + const nextName = event.target.value + setName(nextName) + const updatedGoal = getUpdatedGoal({ name: nextName }) + dispatch(updateGoalRedux(updatedGoal)) + updateGoalApi(props.goal.id, updatedGoal) + } + + const updateTargetAmountOnChange = (event: React.ChangeEvent) => { + const nextTargetAmount = parseFloat(event.target.value) + setTargetAmount(nextTargetAmount) + const updatedGoal = getUpdatedGoal({ targetAmount: nextTargetAmount }) dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } @@ -64,12 +254,7 @@ 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, - } + const updatedGoal = getUpdatedGoal({ targetDate: date }) dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } @@ -77,8 +262,26 @@ export function GoalManager(props: Props) { return ( - + {/* Icon Section - Add Icon Button or Goal Icon */} + + + + Add icon + + + + + + + {/* Goal Name Input */} + + + {/* Goal Details */} @@ -89,14 +292,19 @@ export function GoalManager(props: Props) { - + - {props.goal.balance} + ${props.goal.balance.toFixed(2)} @@ -106,79 +314,16 @@ export function GoalManager(props: Props) { {new Date(props.goal.created).toLocaleDateString()} + + {/* Emoji Picker */} + event.stopPropagation()} + > + + ) -} - -type FieldProps = { name: string; icon: IconDefinition } -type AddIconButtonContainerProps = { shouldShow: boolean } -type GoalIconContainerProps = { shouldShow: boolean } -type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean } - -const Field = (props: FieldProps) => ( - - - {props.name} - -) - -const GoalManagerContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - height: 100%; - width: 100%; - position: relative; -` - -const Group = styled.div` - display: flex; - flex-direction: row; - width: 100%; - margin-top: 1.25rem; - margin-bottom: 1.25rem; -` -const NameInput = styled.input` - display: flex; - background-color: transparent; - outline: none; - border: none; - font-size: 4rem; - font-weight: bold; - color: ${({ theme }: { theme: Theme }) => theme.text}; -` - -const FieldName = styled.h1` - font-size: 1.8rem; - margin-left: 1rem; - color: rgba(174, 174, 174, 1); - font-weight: normal; -` -const FieldContainer = styled.div` - display: flex; - flex-direction: row; - align-items: center; - width: 20rem; - - svg { - 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}; -` - -const Value = styled.div` - margin-left: 2rem; -` +} \ No newline at end of file diff --git a/src/ui/pages/Main/goals/GoalCard.tsx b/src/ui/pages/Main/goals/GoalCard.tsx index e8f6d0a..1196ac4 100644 --- a/src/ui/pages/Main/goals/GoalCard.tsx +++ b/src/ui/pages/Main/goals/GoalCard.tsx @@ -11,6 +11,10 @@ import { Card } from '../../../components/Card' type Props = { id: string } +const Icon = styled.h1` + font-size: 5.5rem; +` + export default function GoalCard(props: Props) { const dispatch = useAppDispatch() @@ -29,6 +33,7 @@ export default function GoalCard(props: Props) { ${goal.targetAmount} {asLocaleDateString(goal.targetDate)} + {goal.icon} ) } From 00b5dbbe29a6145565cc973d534d8d4d3ca724c6 Mon Sep 17 00:00:00 2001 From: Hussain Jameel Date: Fri, 30 Jan 2026 22:30:20 +0500 Subject: [PATCH 2/2] Completed Task 3: Emoji changes persist after client refresh or on server restart --- TASK2.md | 350 ++++++++++++++++++++ emoji-api-tests/after-update | 50 +++ emoji-api-tests/before-update | 50 +++ src/api/lib.ts | 1 + src/ui/features/goalmanager/GoalManager.tsx | 1 + 5 files changed, 452 insertions(+) create mode 100644 TASK2.md create mode 100644 emoji-api-tests/after-update create mode 100644 emoji-api-tests/before-update diff --git a/TASK2.md b/TASK2.md new file mode 100644 index 0000000..f15273d --- /dev/null +++ b/TASK2.md @@ -0,0 +1,350 @@ +# Task 5 & 6 Submission - Emoji Picker & GoalManager + +## Student: [Your Name] +## Date: $(Get-Date -Format "MM/dd/yyyy") + +## Overview +Implemented emoji picker functionality and modified GoalManager to allow users to add and change icons for goals. + +## Files Modified +1. `src/ui/features/goal manager/GoalManager.tsx` +2. `src/ui/components/EmojiPicker.tsx` +3. `src/ui/features/goal manager/GoalIcon.tsx` +4. `package.json` (added emoji-mart@3.0.1) + +## Code Implementation + +### GoalManager.tsx - Key Changes + +```typescript +import { faCalendarAlt, faSmile } 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, useRef } 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 { useAppDispatch, useAppSelector } from '../../../store/hooks' +import DatePicker from '../../components/DatePicker' +import EmojiPicker from '../../components/EmojiPicker' +import { TransparentButton } from '../../components/TransparentButton' +import GoalIcon from './GoalIcon' +import { Theme } from '../../components/Theme' + +type Props = { goal: Goal } + +// Styled Components +type AddIconButtonContainerProps = { hasIcon: boolean } +type GoalIconContainerProps = { shouldShow: boolean } +type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean } + +const AddIconButtonContainer = styled.div` + display: ${(props) => (props.hasIcon ? 'none' : 'flex')}; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + margin-bottom: 2rem; + width: 100%; +` + +const AddIconButtonText = styled.p` + font-size: 1.5rem; + margin-top: 1rem; + color: ${({ theme }: { theme: Theme }) => theme.text}; +` + +const GoalIconContainer = styled.div` + display: ${(props) => (props.shouldShow ? 'flex' : 'none')}; + justify-content: center; + margin-bottom: 2rem; + width: 100%; +` + +const EmojiPickerContainer = styled.div` + display: ${(props) => (props.isOpen ? 'flex' : 'none')}; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10000; + background: white; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); + border: 1px solid #ddd; + padding: 10px; + + /* Limit size */ + max-width: 90vw; + max-height: 80vh; + overflow: auto; + + /* Center the picker */ + justify-content: center; + align-items: center; +` + +const GoalManagerContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + height: 100%; + width: 100%; + position: relative; + padding: 2rem; +` + +const Group = styled.div` + display: flex; + flex-direction: row; + width: 100%; + margin-top: 1.25rem; + margin-bottom: 1.25rem; +` + +const NameInput = styled.input` + display: flex; + background-color: transparent; + outline: none; + border: none; + font-size: 4rem; + font-weight: bold; + color: ${({ theme }: { theme: Theme }) => theme.text}; + width: 100%; + margin-bottom: 2rem; +` + +const FieldName = styled.h1` + font-size: 1.8rem; + margin-left: 1rem; + color: rgba(174, 174, 174, 1); + font-weight: normal; +` + +const FieldContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + width: 20rem; + + svg { + 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}; + width: 100%; +` + +const Value = styled.div` + margin-left: 2rem; + flex: 1; +` + +// Field Component +type FieldProps = { name: string; icon: IconDefinition } + +const Field = (props: FieldProps) => ( + + + {props.name} + +) + +// Main GoalManager Component +export function GoalManager(props: Props) { + const dispatch = useAppDispatch() + const goal = useAppSelector(selectGoalsMap)[props.goal.id] + const pickerRef = useRef(null) + + const [name, setName] = useState(null) + const [targetDate, setTargetDate] = useState(null) + const [targetAmount, setTargetAmount] = useState(null) + const [icon, setIcon] = useState(null) + const [emojiPickerIsOpen, setEmojiPickerIsOpen] = useState(false) + + // Click outside handler + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (pickerRef.current && !pickerRef.current.contains(event.target as Node)) { + setEmojiPickerIsOpen(false) + } + } + + if (emojiPickerIsOpen) { + document.addEventListener('mousedown', handleClickOutside) + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [emojiPickerIsOpen]) + + // Initialize state from props + useEffect(() => { + setName(props.goal.name) + setTargetDate(props.goal.targetDate) + setTargetAmount(props.goal.targetAmount) + setIcon(props.goal.icon) + }, [ + props.goal.id, + props.goal.name, + props.goal.targetDate, + props.goal.targetAmount, + props.goal.icon, + ]) + + // Update state when Redux goal changes + useEffect(() => { + setName(goal.name) + setIcon(goal.icon) + }, [goal.name, goal.icon]) + + // Helper functions + const hasIcon = () => icon != null && icon !== '' + + const addIconOnClick = (event: React.MouseEvent) => { + event.stopPropagation() + setEmojiPickerIsOpen(true) + } + + const pickEmojiOnClick = (emoji: BaseEmoji, event: React.MouseEvent) => { + event.stopPropagation() + + setIcon(emoji.native) + setEmojiPickerIsOpen(false) + + 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, + } + + dispatch(updateGoalRedux(updatedGoal)) + updateGoalApi(props.goal.id, updatedGoal) + } + + const getUpdatedGoal = (updates: Partial): Goal => { + return { + ...props.goal, + name: name ?? props.goal.name, + targetDate: targetDate ?? props.goal.targetDate, + targetAmount: targetAmount ?? props.goal.targetAmount, + icon: icon ?? props.goal.icon, + ...updates, + } + } + + const updateNameOnChange = (event: React.ChangeEvent) => { + const nextName = event.target.value + setName(nextName) + const updatedGoal = getUpdatedGoal({ name: nextName }) + dispatch(updateGoalRedux(updatedGoal)) + updateGoalApi(props.goal.id, updatedGoal) + } + + const updateTargetAmountOnChange = (event: React.ChangeEvent) => { + const nextTargetAmount = parseFloat(event.target.value) + setTargetAmount(nextTargetAmount) + const updatedGoal = getUpdatedGoal({ targetAmount: nextTargetAmount }) + dispatch(updateGoalRedux(updatedGoal)) + updateGoalApi(props.goal.id, updatedGoal) + } + + const pickDateOnChange = (date: MaterialUiPickersDate) => { + if (date != null) { + setTargetDate(date) + const updatedGoal = getUpdatedGoal({ targetDate: date }) + dispatch(updateGoalRedux(updatedGoal)) + updateGoalApi(props.goal.id, updatedGoal) + } + } + + return ( + + {/* Icon Section - Add Icon Button or Goal Icon */} + + + + Add icon + + + + + + + + {/* Goal Name Input */} + + + {/* Goal Details */} + + + + + + + + + + + + + + + + + + ${props.goal.balance.toFixed(2)} + + + + + + + {new Date(props.goal.created).toLocaleDateString()} + + + + {/* Emoji Picker */} + event.stopPropagation()} + > + + + + ) +} +``` + diff --git a/emoji-api-tests/after-update b/emoji-api-tests/after-update new file mode 100644 index 0000000..729db2f --- /dev/null +++ b/emoji-api-tests/after-update @@ -0,0 +1,50 @@ +[ + { + "id": "62a3f587102e921da1253d32", + "name": null, + "icon": "🤺", + "targetAmount": 0, + "targetDate": "0001-01-01T00:00:00Z", + "balance": 0, + "created": "2026-01-29T20:53:31.37Z", + "transactionIds": null, + "tagIds": null, + "userId": null + }, + { + "id": "62a3f5e0102e921da1253d33", + "name": "Tesla Model Y", + "icon": "🚗", + "targetAmount": 60000, + "targetDate": "2022-09-01T04:00:00Z", + "balance": 43840.02, + "created": "2026-01-30T17:23:47.829Z", + "transactionIds": null, + "tagIds": null, + "userId": "62a29c15f4605c4c9fa7f306" + }, + { + "id": "62a3f62e102e921da1253d34", + "name": "House Down Payment", + "icon": "🏠", + "targetAmount": 100000, + "targetDate": "2025-01-08T05:00:00Z", + "balance": 73501.82, + "created": "2026-01-30T16:47:55.215Z", + "transactionIds": null, + "tagIds": null, + "userId": "62a29c15f4605c4c9fa7f306" + }, + { + "id": "62a61945fa15f1cd18516a5f", + "name": "Trip to NYC", + "icon": "🗽", + "targetAmount": 800, + "targetDate": "2023-12-10T05:00:00Z", + "balance": 0, + "created": "2026-01-30T17:25:25.055Z", + "transactionIds": null, + "tagIds": null, + "userId": "62a29c15f4605c4c9fa7f306" + } +] \ No newline at end of file diff --git a/emoji-api-tests/before-update b/emoji-api-tests/before-update new file mode 100644 index 0000000..fb43ea1 --- /dev/null +++ b/emoji-api-tests/before-update @@ -0,0 +1,50 @@ +[ + { + "id": "62a3f587102e921da1253d32", + "name": null, + "icon": "🤺", + "targetAmount": 0, + "targetDate": "0001-01-01T00:00:00Z", + "balance": 0, + "created": "2026-01-29T20:53:31.37Z", + "transactionIds": null, + "tagIds": null, + "userId": null + }, + { + "id": "62a3f5e0102e921da1253d33", + "name": "Tesla Model Y", + "icon": null, + "targetAmount": 60000, + "targetDate": "2022-09-01T04:00:00Z", + "balance": 43840.02, + "created": "2022-06-11T01:54:40.95Z", + "transactionIds": null, + "tagIds": null, + "userId": "62a29c15f4605c4c9fa7f306" + }, + { + "id": "62a3f62e102e921da1253d34", + "name": "House Down Payment", + "icon": "🏠", + "targetAmount": 100000, + "targetDate": "2025-01-08T05:00:00Z", + "balance": 73501.82, + "created": "2026-01-30T16:47:55.215Z", + "transactionIds": null, + "tagIds": null, + "userId": "62a29c15f4605c4c9fa7f306" + }, + { + "id": "62a61945fa15f1cd18516a5f", + "name": "Trip to NYC", + "icon": null, + "targetAmount": 800, + "targetDate": "2023-12-10T05:00:00Z", + "balance": 0, + "created": "2022-06-12T16:57:45.668Z", + "transactionIds": null, + "tagIds": null, + "userId": "62a29c15f4605c4c9fa7f306" + } +] \ No newline at end of file diff --git a/src/api/lib.ts b/src/api/lib.ts index 3c593ca..34e554c 100644 --- a/src/api/lib.ts +++ b/src/api/lib.ts @@ -50,4 +50,5 @@ export async function updateGoal(goalId: string, updatedGoal: Goal): Promise