diff --git a/.vs/CommBank-Web/FileContentIndex/98809b40-6728-4ae8-8f96-0c8e581cb769.vsidx b/.vs/CommBank-Web/FileContentIndex/98809b40-6728-4ae8-8f96-0c8e581cb769.vsidx new file mode 100644 index 0000000..bb2f9da Binary files /dev/null and b/.vs/CommBank-Web/FileContentIndex/98809b40-6728-4ae8-8f96-0c8e581cb769.vsidx differ diff --git a/.vs/CommBank-Web/v17/.wsuo b/.vs/CommBank-Web/v17/.wsuo new file mode 100644 index 0000000..8d6fcc6 Binary files /dev/null and b/.vs/CommBank-Web/v17/.wsuo differ diff --git a/.vs/CommBank-Web/v17/DocumentLayout.json b/.vs/CommBank-Web/v17/DocumentLayout.json new file mode 100644 index 0000000..1ff4a1e --- /dev/null +++ b/.vs/CommBank-Web/v17/DocumentLayout.json @@ -0,0 +1,117 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\features\\goalmanager\\GoalManager.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\ui\\features\\goalmanager\\GoalManager.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\api\\lib.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\api\\lib.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\features\\goalmanager\\GoalIcon.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\ui\\features\\goalmanager\\GoalIcon.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\pages\\Main\\goals\\GoalCard.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\ui\\pages\\Main\\goals\\GoalCard.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\api\\types.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\api\\types.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\store\\goalsSlice.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\store\\goalsSlice.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 1, + "Children": [ + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "lib.ts", + "DocumentMoniker": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\api\\lib.ts", + "RelativeDocumentMoniker": "src\\api\\lib.ts", + "ToolTip": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\api\\lib.ts", + "RelativeToolTip": "src\\api\\lib.ts", + "ViewState": "AQIAAAAAAAAAAAAAAAAAAAQAAAAwAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2026-04-07T01:30:32.276Z" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "GoalManager.tsx", + "DocumentMoniker": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\features\\goalmanager\\GoalManager.tsx", + "RelativeDocumentMoniker": "src\\ui\\features\\goalmanager\\GoalManager.tsx", + "ToolTip": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\features\\goalmanager\\GoalManager.tsx", + "RelativeToolTip": "src\\ui\\features\\goalmanager\\GoalManager.tsx", + "ViewState": "AQIAALIAAAAAAAAAAAAwwL4AAAAQAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2026-04-07T01:21:34.391Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "GoalIcon.tsx", + "DocumentMoniker": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\features\\goalmanager\\GoalIcon.tsx", + "RelativeDocumentMoniker": "src\\ui\\features\\goalmanager\\GoalIcon.tsx", + "ToolTip": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\features\\goalmanager\\GoalIcon.tsx", + "RelativeToolTip": "src\\ui\\features\\goalmanager\\GoalIcon.tsx", + "ViewState": "AQIAAAAAAAAAAAAAAAAAABMAAAAAAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2026-04-07T01:21:16.275Z" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "GoalCard.tsx", + "DocumentMoniker": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\pages\\Main\\goals\\GoalCard.tsx", + "RelativeDocumentMoniker": "src\\ui\\pages\\Main\\goals\\GoalCard.tsx", + "ToolTip": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\ui\\pages\\Main\\goals\\GoalCard.tsx", + "RelativeToolTip": "src\\ui\\pages\\Main\\goals\\GoalCard.tsx", + "ViewState": "AQIAAAAAAAAAAAAAAAAAADIAAAABAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2026-04-07T01:17:31.156Z" + }, + { + "$type": "Document", + "DocumentIndex": 4, + "Title": "types.ts", + "DocumentMoniker": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\api\\types.ts", + "RelativeDocumentMoniker": "src\\api\\types.ts", + "ToolTip": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\api\\types.ts", + "RelativeToolTip": "src\\api\\types.ts", + "ViewState": "AQIAAA0AAAAAAAAAAAAswBcAAAARAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2026-04-07T01:16:14.544Z" + }, + { + "$type": "Document", + "DocumentIndex": 5, + "Title": "goalsSlice.ts", + "DocumentMoniker": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\store\\goalsSlice.ts", + "RelativeDocumentMoniker": "src\\store\\goalsSlice.ts", + "ToolTip": "C:\\Users\\aiell\\Documents\\GitHub\\CommBank-Web\\src\\store\\goalsSlice.ts", + "RelativeToolTip": "src\\store\\goalsSlice.ts", + "ViewState": "AQIAAAsAAAAAAAAAAAAwwCcAAAAAAAAA", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2026-04-07T01:15:58.001Z" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..d0e42e1 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,15 @@ +{ + "ExpandedNodes": [ + "", + "\\src", + "\\src\\assets", + "\\src\\assets\\images", + "\\src\\data", + "\\src\\store", + "\\src\\ui", + "\\src\\ui\\components", + "\\src\\ui\\features", + "\\src\\ui\\features\\goalmanager" + ], + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..9de77d5 Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/package-lock.json b/package-lock.json index 240aecf..94349f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6611,6 +6611,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" diff --git a/src/api/lib.ts b/src/api/lib.ts index 3c593ca..1094f67 100644 --- a/src/api/lib.ts +++ b/src/api/lib.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { user } from '../data/user' import { Goal, Transaction, User } from './types' -export const API_ROOT = 'https://fencer-commbank.azurewebsites.net' +export const API_ROOT = 'http://localhost:11366' export async function getUser(): Promise { try { diff --git a/src/api/types.ts b/src/api/types.ts index f75edad..1bf1c7c 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 // add this line } export interface Tag { diff --git a/src/ui/features/goalmanager/GoalIcon.tsx b/src/ui/features/goalmanager/GoalIcon.tsx index b5a0d75..595cf24 100644 --- a/src/ui/features/goalmanager/GoalIcon.tsx +++ b/src/ui/features/goalmanager/GoalIcon.tsx @@ -1,19 +1,16 @@ -import 'date-fns' +import 'date-fns' import React from 'react' import styled from 'styled-components' import { TransparentButton } from '../../components/TransparentButton' - type Props = { icon: string | null; onClick: (e: React.MouseEvent) => void } - export default function GoalIcon(props: Props) { - return ( - - {props.icon} - - ) + return ( + + {props.icon ?? '🏦'} + + ) } - const Icon = styled.h1` font-size: 6rem; cursor: pointer; -` +` \ No newline at end of file diff --git a/src/ui/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx index 0779dda..ad69cee 100644 --- a/src/ui/features/goalmanager/GoalManager.tsx +++ b/src/ui/features/goalmanager/GoalManager.tsx @@ -3,6 +3,8 @@ 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 { Picker } from 'emoji-mart' +import 'emoji-mart/css/emoji-mart.css' import React, { useEffect, useState } from 'react' import styled from 'styled-components' import { updateGoal as updateGoalApi } from '../../../api/lib' @@ -11,115 +13,134 @@ 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' 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) - - 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) => { - 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(null) + 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]) + + 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) } - 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, + 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) } - 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) + } } - } - return ( - - - - - - - - - - - - - - - - - - - - - {props.goal.balance} - - - - - - - {new Date(props.goal.created).toLocaleDateString()} - - - - ) + const onEmojiSelect = (emoji: any) => { + const nextIcon = emoji.native + setIcon(nextIcon) + setIsPickerOpen(false) + const updatedGoal: Goal = { ...props.goal, icon: nextIcon } + dispatch(updateGoalRedux(updatedGoal)) + updateGoalApi(props.goal.id, updatedGoal) + } + + const onIconClick = (e: React.MouseEvent) => { + e.stopPropagation() + setIsPickerOpen(!isPickerOpen) + } + + return ( + + + {isPickerOpen && ( + + + + )} + + + + + + + + + + + + + + + + + + + + + {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} - + + + {props.name} + ) const GoalManagerContainer = styled.div` @@ -131,7 +152,6 @@ const GoalManagerContainer = styled.div` width: 100%; position: relative; ` - const Group = styled.div` display: flex; flex-direction: row; @@ -148,7 +168,6 @@ const NameInput = styled.input` font-weight: bold; color: ${({ theme }: { theme: Theme }) => theme.text}; ` - const FieldName = styled.h1` font-size: 1.8rem; margin-left: 1rem; @@ -160,7 +179,6 @@ const FieldContainer = styled.div` flex-direction: row; align-items: center; width: 20rem; - svg { color: rgba(174, 174, 174, 1); } @@ -178,7 +196,12 @@ const StringInput = styled.input` font-weight: bold; color: ${({ theme }: { theme: Theme }) => theme.text}; ` - const Value = styled.div` margin-left: 2rem; ` +const EmojiPickerContainer = styled.div` + position: absolute; + top: 8rem; + left: 0; + z-index: 100; +` \ 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..6fed265 100644 --- a/src/ui/pages/Main/goals/GoalCard.tsx +++ b/src/ui/pages/Main/goals/GoalCard.tsx @@ -3,36 +3,30 @@ 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 goal = useAppSelector(selectGoalsMap)[props.id] - - const onClick = (event: React.MouseEvent) => { - event.stopPropagation() - dispatch(setContentRedux(goal)) - dispatch(setTypeRedux('Goal')) - dispatch(setIsOpenRedux(true)) - } - - const asLocaleDateString = (date: Date) => new Date(date).toLocaleDateString() - - return ( - - ${goal.targetAmount} - {asLocaleDateString(goal.targetDate)} - - ) + const dispatch = useAppDispatch() + const goal = useAppSelector(selectGoalsMap)[props.id] + const onClick = (event: React.MouseEvent) => { + event.stopPropagation() + dispatch(setContentRedux(goal)) + dispatch(setTypeRedux('Goal')) + dispatch(setIsOpenRedux(true)) + } + const asLocaleDateString = (date: Date) => new Date(date).toLocaleDateString() + return ( + + {goal.icon && {goal.icon}} + ${goal.targetAmount} + {asLocaleDateString(goal.targetDate)} + + ) } - const Container = styled(Card)` display: flex; flex-direction: column; @@ -43,14 +37,15 @@ const Container = styled(Card)` margin-left: 2rem; margin-right: 2rem; border-radius: 2rem; - align-items: center; ` const TargetAmount = styled.h2` font-size: 2rem; ` - const TargetDate = styled.h4` color: rgba(174, 174, 174, 1); font-size: 1rem; ` +const Icon = styled.span` + font-size: 2rem; +` \ No newline at end of file