From 34e4a41bf2b519aa3e30b947cc3803869af70d7d Mon Sep 17 00:00:00 2001 From: Aashka Shah Date: Wed, 7 Jan 2026 09:22:35 +0400 Subject: [PATCH 1/2] Complete Task 2: Add emoji icon functionality --- src/ui/features/goalmanager/GoalManager.tsx | 82 ++++++- .../features/goalmanager/GoalManager.tsx.save | 220 ++++++++++++++++++ 2 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 src/ui/features/goalmanager/GoalManager.tsx.save diff --git a/src/ui/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx index 0779dda..f81f361 100644 --- a/src/ui/features/goalmanager/GoalManager.tsx +++ b/src/ui/features/goalmanager/GoalManager.tsx @@ -1,6 +1,7 @@ import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons' import { faDollarSign, IconDefinition } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faSmile } from '@fortawesome/free-regular-svg-icons' import { MaterialUiPickersDate } from '@material-ui/pickers/typings/date' import 'date-fns' import React, { useEffect, useState } from 'react' @@ -11,6 +12,10 @@ 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 } from 'emoji-mart' +import EmojiPicker from '../../components/EmojiPicker' +import { TransparentButton } from '../../components/TransparentButton' +import GoalIcon from './GoalIcon' type Props = { goal: Goal } export function GoalManager(props: Props) { @@ -21,21 +26,29 @@ 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 [emojiPickerIsOpen, setEmojiPickerIsOpen] = useState(false) 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, ]) useEffect(() => { setName(goal.name) }, [goal.name]) + + useEffect(() => { + setIcon(props.goal.icon) + }, [props.goal.id, props.goal.icon]) const updateNameOnChange = (event: React.ChangeEvent) => { const nextName = event.target.value @@ -60,8 +73,13 @@ export function GoalManager(props: Props) { dispatch(updateGoalRedux(updatedGoal)) updateGoalApi(props.goal.id, updatedGoal) } - - const pickDateOnChange = (date: MaterialUiPickersDate) => { + + const hasIcon = () => icon != null + const addIconOnClick = (event: React.MouseEvent) => { + event.stopPropagation() + setEmojiPickerIsOpen(true) + } + const pickDateOnChange = (date: MaterialUiPickersDate) => { if (date != null) { setTargetDate(date) const updatedGoal: Goal = { @@ -74,11 +92,47 @@ export function GoalManager(props: Props) { updateGoalApi(props.goal.id, updatedGoal) } } + 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) + } return ( - + event.stopPropagation()} + > + + + + + + + Add icon + + + + + + + + @@ -182,3 +236,25 @@ const StringInput = styled.input` const Value = styled.div` margin-left: 2rem; ` +const EmojiPickerContainer = styled.div` + display: ${(props) => (props.isOpen ? 'flex' : 'none')}; + position: absolute; + top: ${(props) => (props.hasIcon ? '10rem' : '2rem')}; + left: 0; +` + +const AddIconButtonContainer = styled.div` + display: ${(props) => (props.shouldShow ? 'flex' : 'none')}; + flex-direction: row; + align-items: center; +` + +const AddIconButtonText = styled.h1` + font-size: 1.8rem; + margin-left: 1rem; + font-weight: normal; +` + +const GoalIconContainer = styled.div` + display: ${(props) => (props.shouldShow ? 'flex' : 'none')}; +` diff --git a/src/ui/features/goalmanager/GoalManager.tsx.save b/src/ui/features/goalmanager/GoalManager.tsx.save new file mode 100644 index 0000000..c6393e4 --- /dev/null +++ b/src/ui/features/goalmanager/GoalManager.tsx.save @@ -0,0 +1,220 @@ +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 '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 { useAppDispatch, useAppSelector } from '../../../store/hooks' +import DatePicker from '../../components/DatePicker' +import { Theme } from '../../components/Theme' +import { BaseEmoji } from 'emoji-mart' +import EmojiPicker from '../../components/EmojiPicker' + +type Props = { goal: Goal } +export function GoalManager(props: Props) { + const dispatch = useAppDispatch() + + const goal = useAppSelector(selectGoalsMap)[props.goal.id] + + 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) + + 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, + props.goal.icon, + ]) + + useEffect(() => { + setName(goal.name) + }, [goal.name]) + + 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 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) + } + + const hasIcon = () => icon != null + 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 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) + } + + return ( + + event.stopPropagation()} + > + + + + + + + + + + + + + + + + + + + + + + {props.goal.balance} + + + + + + + {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 } + +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; +` +const EmojiPickerContainer = styled.div` + display: ${(props) => (props.isOpen ? 'flex' : 'none')}; + position: absolute; + top: ${(props) => (props.hasIcon ? '10rem' : '2rem')}; + left: 0; +` From d85c12bf3d6cc846f3b4e329858aff4ad971434a Mon Sep 17 00:00:00 2001 From: Aashka Shah Date: Wed, 7 Jan 2026 10:31:22 +0400 Subject: [PATCH 2/2] Add emoji icon picker and display functionality --- package-lock.json | 75 ++++++++++++++++++++++++++-- src/api/lib.ts | 2 + src/api/types.ts | 1 + src/ui/pages/Main/goals/GoalCard.tsx | 4 ++ 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 240aecf..ecdbd6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,6 +75,7 @@ "version": "7.18.2", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.2.tgz", "integrity": "sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -894,6 +895,7 @@ "version": "7.17.12", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.17.12.tgz", "integrity": "sha512-B8QIgBvkIG6G2jgsOHQUist7Sm0EBLDCx8sen072IwqNuzMegZNXrYnSv77cYzA8mLDZAfQYqsLIhimiP1s2HQ==", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.17.12" }, @@ -1456,6 +1458,7 @@ "version": "7.17.12", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz", "integrity": "sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ==", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", @@ -3856,6 +3859,7 @@ "version": "16.14.26", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.26.tgz", "integrity": "sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==", + "peer": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3998,6 +4002,7 @@ "version": "5.27.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.1.tgz", "integrity": "sha512-6dM5NKT57ZduNnJfpY81Phe9nc9wolnMCnknb1im6brWi1RYv84nbMS3olJa27B6+irUVV1X/Wb+Am0FjJdGFw==", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.27.1", "@typescript-eslint/type-utils": "5.27.1", @@ -4048,6 +4053,7 @@ "version": "5.27.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.27.1.tgz", "integrity": "sha512-7Va2ZOkHi5NP+AZwb5ReLgNF6nWLGTeUJfxdkVUAPPSaAdbWNnFZzLZ4EGGmmiCTg+AwlbE1KyUYTBglosSLHQ==", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.27.1", "@typescript-eslint/types": "5.27.1", @@ -4370,6 +4376,7 @@ "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4477,6 +4484,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5241,6 +5249,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001349", "electron-to-chromium": "^1.4.147", @@ -5914,6 +5923,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6228,6 +6238,7 @@ "version": "2.28.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "peer": true, "engines": { "node": ">=0.11" }, @@ -6611,6 +6622,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-3.0.1.tgz", "integrity": "sha512-sxpmMKxqLvcscu6mFn9ITHeZNkGzIvD0BSNFE/LJESPbCA8s1jM6bCDPjWbV31xHq7JXaxgpHxLB54RCbBZSlg==", + "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "^7.0.0", "prop-types": "^15.6.0" @@ -6848,6 +6860,7 @@ "version": "8.17.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.17.0.tgz", "integrity": "sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==", + "peer": true, "dependencies": { "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", @@ -9264,6 +9277,7 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -11692,6 +11706,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -12428,6 +12443,7 @@ "url": "https://tidelift.com/funding/github/npm/postcss" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", @@ -13454,6 +13470,7 @@ "version": "6.0.10", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13649,6 +13666,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -13807,6 +13825,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -13952,6 +13971,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -13975,6 +13995,7 @@ "version": "7.2.8", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.8.tgz", "integrity": "sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==", + "peer": true, "dependencies": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", @@ -14004,6 +14025,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -14153,6 +14175,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "peer": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -14457,6 +14480,7 @@ "version": "2.75.6", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.75.6.tgz", "integrity": "sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -14562,6 +14586,7 @@ "version": "1.52.3", "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.3.tgz", "integrity": "sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA==", + "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -15209,6 +15234,7 @@ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz", "integrity": "sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==", "hasInstallScript": true, + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -15729,6 +15755,7 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "peer": true, "engines": { "node": ">=10" }, @@ -15760,6 +15787,7 @@ "version": "4.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15997,6 +16025,7 @@ "version": "5.73.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz", "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", @@ -16065,6 +16094,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -16114,6 +16144,7 @@ "version": "4.9.2", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.2.tgz", "integrity": "sha512-H95Ns95dP24ZsEzO6G9iT+PNw4Q7ltll1GfJHV4fKphuHWgKFzGHWi4alTlTnpk1SPPk41X+l2RB7rLfIhnB9Q==", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -16164,6 +16195,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -16476,6 +16508,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -16871,6 +16904,7 @@ "version": "7.18.2", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.2.tgz", "integrity": "sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==", + "peer": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -17434,6 +17468,7 @@ "version": "7.17.12", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.17.12.tgz", "integrity": "sha512-B8QIgBvkIG6G2jgsOHQUist7Sm0EBLDCx8sen072IwqNuzMegZNXrYnSv77cYzA8mLDZAfQYqsLIhimiP1s2HQ==", + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.17.12" } @@ -17780,6 +17815,7 @@ "version": "7.17.12", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz", "integrity": "sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ==", + "peer": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", @@ -19465,6 +19501,7 @@ "version": "16.14.26", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.26.tgz", "integrity": "sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==", + "peer": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -19611,6 +19648,7 @@ "version": "5.27.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.1.tgz", "integrity": "sha512-6dM5NKT57ZduNnJfpY81Phe9nc9wolnMCnknb1im6brWi1RYv84nbMS3olJa27B6+irUVV1X/Wb+Am0FjJdGFw==", + "peer": true, "requires": { "@typescript-eslint/scope-manager": "5.27.1", "@typescript-eslint/type-utils": "5.27.1", @@ -19635,6 +19673,7 @@ "version": "5.27.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.27.1.tgz", "integrity": "sha512-7Va2ZOkHi5NP+AZwb5ReLgNF6nWLGTeUJfxdkVUAPPSaAdbWNnFZzLZ4EGGmmiCTg+AwlbE1KyUYTBglosSLHQ==", + "peer": true, "requires": { "@typescript-eslint/scope-manager": "5.27.1", "@typescript-eslint/types": "5.27.1", @@ -19876,7 +19915,8 @@ "acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==" + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "peer": true }, "acorn-globals": { "version": "6.0.0", @@ -19954,6 +19994,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -20525,6 +20566,7 @@ "version": "4.20.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz", "integrity": "sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==", + "peer": true, "requires": { "caniuse-lite": "^1.0.30001349", "electron-to-chromium": "^1.4.147", @@ -20996,6 +21038,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -21231,7 +21274,8 @@ "date-fns": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==" + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "peer": true }, "debug": { "version": "4.3.4", @@ -21696,6 +21740,7 @@ "version": "8.17.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.17.0.tgz", "integrity": "sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw==", + "peer": true, "requires": { "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", @@ -23419,6 +23464,7 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "peer": true, "requires": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -25221,6 +25267,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -25744,6 +25791,7 @@ "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "peer": true, "requires": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", @@ -26322,6 +26370,7 @@ "version": "6.0.10", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "peer": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -26461,6 +26510,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "peer": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -26574,6 +26624,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "peer": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -26682,6 +26733,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "peer": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -26702,6 +26754,7 @@ "version": "7.2.8", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.8.tgz", "integrity": "sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==", + "peer": true, "requires": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", @@ -26721,7 +26774,8 @@ "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", - "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "peer": true }, "react-scripts": { "version": "5.0.1", @@ -26837,6 +26891,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "peer": true, "requires": { "@babel/runtime": "^7.9.2" } @@ -27054,6 +27109,7 @@ "version": "2.75.6", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.75.6.tgz", "integrity": "sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==", + "peer": true, "requires": { "fsevents": "~2.3.2" } @@ -27129,6 +27185,7 @@ "version": "1.52.3", "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.3.tgz", "integrity": "sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA==", + "peer": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -27616,6 +27673,7 @@ "version": "5.3.5", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz", "integrity": "sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==", + "peer": true, "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -28004,7 +28062,8 @@ "type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "peer": true }, "type-is": { "version": "1.6.18", @@ -28026,7 +28085,8 @@ "typescript": { "version": "4.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==" + "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "peer": true }, "unbox-primitive": { "version": "1.0.2", @@ -28205,6 +28265,7 @@ "version": "5.73.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz", "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==", + "peer": true, "requires": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", @@ -28264,6 +28325,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -28301,6 +28363,7 @@ "version": "4.9.2", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.2.tgz", "integrity": "sha512-H95Ns95dP24ZsEzO6G9iT+PNw4Q7ltll1GfJHV4fKphuHWgKFzGHWi4alTlTnpk1SPPk41X+l2RB7rLfIhnB9Q==", + "peer": true, "requires": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -28337,6 +28400,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -28559,6 +28623,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", diff --git a/src/api/lib.ts b/src/api/lib.ts index 3c593ca..d196ad3 100644 --- a/src/api/lib.ts +++ b/src/api/lib.ts @@ -51,3 +51,5 @@ export async function updateGoal(goalId: string, updatedGoal: Goal): Promise + {goal.icon} ${goal.targetAmount} {asLocaleDateString(goal.targetDate)} @@ -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; +`