diff --git a/.gitignore b/.gitignore index 4d29575..98087f4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +.vs/VSWorkspaceState.json +.vs/CommBank-Web/v17/.wsuo +.vs/CommBank-Web/v17/DocumentLayout.json diff --git a/my_patch.patch b/my_patch.patch new file mode 100644 index 0000000..8936999 --- /dev/null +++ b/my_patch.patch @@ -0,0 +1,206 @@ +diff --git a/.gitignore b/.gitignore +index 4d29575..98087f4 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -21,3 +21,6 @@ + npm-debug.log* + yarn-debug.log* + yarn-error.log* ++.vs/VSWorkspaceState.json ++.vs/CommBank-Web/v17/.wsuo ++.vs/CommBank-Web/v17/DocumentLayout.json +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/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx +index 0779dda..d6d0fbd 100644 +--- a/src/ui/features/goalmanager/GoalManager.tsx ++++ b/src/ui/features/goalmanager/GoalManager.tsx +@@ -11,12 +11,41 @@ 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 EmojiPicker from '../../../ui/pages/EmojiPicker' ++import { BaseEmoji, Picker } from 'emoji-mart' ++import { TransparentButton } from '../../components/TransparentButton'; ++import GoalIcon from './GoalIcon'; ++ ++import { faSmile } from '@fortawesome/free-solid-svg-icons'; ++ + + type Props = { goal: Goal } + export function GoalManager(props: Props) { +- const dispatch = useAppDispatch() +- ++ const [emojiPickerIsOpen, setEmojiPickerIsOpen] = useState(false) ++ const [icon, setIcon] = useState(null) ++ const hasIcon = () => icon != null + const goal = useAppSelector(selectGoalsMap)[props.goal.id] ++ useEffect(() => { ++ setIcon(props.goal.icon) ++ }, [props.goal.id, props.goal.icon]) ++ ++ const addIconOnClick = (event: React.MouseEvent) => { ++ event.stopPropagation() ++ setEmojiPickerIsOpen(true) ++ } ++ ++ const pickEmojiOnClick = (emoji: BaseEmoji) => { ++ setIcon(emoji.native) ++ setEmojiPickerIsOpen(false) ++ const updatedGoal: Goal = { ++ ...props.goal, ++ icon: emoji.native, ++ } ++ dispatch(updateGoalRedux(updatedGoal)) ++ updateGoalApi(props.goal.id, updatedGoal) ++ } ++ ++ const dispatch = useAppDispatch() + + const [name, setName] = useState(null) + const [targetDate, setTargetDate] = useState(null) +@@ -61,18 +90,16 @@ export function GoalManager(props: Props) { + 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: 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 ( +@@ -92,6 +119,15 @@ export function GoalManager(props: Props) { + + + ++ ++ ++ ++ Add icon ++ ++ ++ ++ ++ + + + +@@ -106,14 +142,20 @@ export function GoalManager(props: Props) { + {new Date(props.goal.created).toLocaleDateString()} + + ++ ++ ++ ++ + + ) + } + +-type FieldProps = { name: string; icon: IconDefinition } +-type AddIconButtonContainerProps = { shouldShow: boolean } ++type FieldProps = { name: string; icon: any } ++type AddIconButtonContainerProps = { hasIcon?: boolean } + type GoalIconContainerProps = { shouldShow: boolean } +-type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean } ++ ++ ++type EmojiPickerContainerProps = { isOpen: boolean } + + const Field = (props: FieldProps) => ( + +@@ -122,6 +164,28 @@ const Field = (props: FieldProps) => ( + + ) + ++const AddIconButtonContainer = styled.div` ++ display: ${(props) => (props.hasIcon ? 'none' : 'flex')}; ++ align-items: center; ++` ++ ++const AddIconButtonText = styled.span` ++ margin-left: 0.5rem; ++` ++ ++const GoalIconContainer = styled.div` ++ display: ${(props) => (props.shouldShow ? 'flex' : 'none')}; ++` ++ ++const Icon = styled.h1` ++ font-size: 5.5rem; ++ cursor: pointer; ++` ++ ++const EmojiPickerContainer = styled.div` ++ display: ${(props) => (props.isOpen ? 'flex' : 'none')}; ++` ++ + const GoalManagerContainer = styled.div` + display: flex; + flex-direction: column; +@@ -139,6 +203,7 @@ const Group = styled.div` + margin-top: 1.25rem; + margin-bottom: 1.25rem; + ` ++ + const NameInput = styled.input` + display: flex; + background-color: transparent; +diff --git a/src/ui/pages/Main/goals/GoalCard.tsx b/src/ui/pages/Main/goals/GoalCard.tsx +index e8f6d0a..10d9e6b 100644 +--- a/src/ui/pages/Main/goals/GoalCard.tsx ++++ b/src/ui/pages/Main/goals/GoalCard.tsx +@@ -8,13 +8,16 @@ import { + setType as setTypeRedux + } from '../../../../store/modalSlice' + import { Card } from '../../../components/Card' +- ++const Icon = styled.h1` ++ font-size: 5.5rem; ++` + type Props = { id: string } + + export default function GoalCard(props: Props) { + const dispatch = useAppDispatch() + + const goal = useAppSelector(selectGoalsMap)[props.id] ++ console.log(goal) + + const onClick = (event: React.MouseEvent) => { + event.stopPropagation() +@@ -29,6 +32,7 @@ export default function GoalCard(props: Props) { + + ${goal.targetAmount} + {asLocaleDateString(goal.targetDate)} ++ {goal.icon} + + ) + } diff --git a/my_patch_file.patch b/my_patch_file.patch new file mode 100644 index 0000000..9def944 --- /dev/null +++ b/my_patch_file.patch @@ -0,0 +1,209 @@ +diff --git a/.gitignore b/.gitignore +index 4d29575..98087f4 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -21,3 +21,6 @@ + npm-debug.log* + yarn-debug.log* + yarn-error.log* ++.vs/VSWorkspaceState.json ++.vs/CommBank-Web/v17/.wsuo ++.vs/CommBank-Web/v17/DocumentLayout.json +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/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx +index 0779dda..7a19628 100644 +--- a/src/ui/features/goalmanager/GoalManager.tsx ++++ b/src/ui/features/goalmanager/GoalManager.tsx +@@ -11,12 +11,44 @@ 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 EmojiPicker from '../../../ui/pages/EmojiPicker' ++import { BaseEmoji, Picker } from 'emoji-mart' ++import { TransparentButton } from '../../components/TransparentButton'; ++import GoalIcon from './GoalIcon'; ++ ++import { faSmile } from '@fortawesome/free-solid-svg-icons'; ++ + + type Props = { goal: Goal } + export function GoalManager(props: Props) { +- const dispatch = useAppDispatch() +- ++ const [emojiPickerIsOpen, setEmojiPickerIsOpen] = useState(false) ++ const [icon, setIcon] = useState(null) ++ const hasIcon = () => icon != null + const goal = useAppSelector(selectGoalsMap)[props.goal.id] ++ useEffect(() => { ++ setIcon(props.goal.icon) ++ }, [props.goal.id, props.goal.icon]) ++ ++ const addIconOnClick = (event: React.MouseEvent) => { ++ event.stopPropagation() ++ setEmojiPickerIsOpen(true) ++ } ++ ++ const pickEmojiOnClick = (emoji: BaseEmoji) => { ++ 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 dispatch = useAppDispatch() + + const [name, setName] = useState(null) + const [targetDate, setTargetDate] = useState(null) +@@ -61,18 +93,16 @@ export function GoalManager(props: Props) { + 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: 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 ( +@@ -92,6 +122,15 @@ export function GoalManager(props: Props) { + + + ++ ++ ++ ++ Add icon ++ ++ ++ ++ ++ + + + +@@ -106,14 +145,20 @@ export function GoalManager(props: Props) { + {new Date(props.goal.created).toLocaleDateString()} + + ++ ++ ++ ++ + + ) + } + +-type FieldProps = { name: string; icon: IconDefinition } +-type AddIconButtonContainerProps = { shouldShow: boolean } ++type FieldProps = { name: string; icon: any } ++type AddIconButtonContainerProps = { hasIcon?: boolean } + type GoalIconContainerProps = { shouldShow: boolean } +-type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean } ++ ++ ++type EmojiPickerContainerProps = { isOpen: boolean } + + const Field = (props: FieldProps) => ( + +@@ -122,6 +167,28 @@ const Field = (props: FieldProps) => ( + + ) + ++const AddIconButtonContainer = styled.div` ++ display: ${(props) => (props.hasIcon ? 'none' : 'flex')}; ++ align-items: center; ++` ++ ++const AddIconButtonText = styled.span` ++ margin-left: 0.5rem; ++` ++ ++const GoalIconContainer = styled.div` ++ display: ${(props) => (props.shouldShow ? 'flex' : 'none')}; ++` ++ ++const Icon = styled.h1` ++ font-size: 5.5rem; ++ cursor: pointer; ++` ++ ++const EmojiPickerContainer = styled.div` ++ display: ${(props) => (props.isOpen ? 'flex' : 'none')}; ++` ++ + const GoalManagerContainer = styled.div` + display: flex; + flex-direction: column; +@@ -139,6 +206,7 @@ const Group = styled.div` + margin-top: 1.25rem; + margin-bottom: 1.25rem; + ` ++ + const NameInput = styled.input` + display: flex; + background-color: transparent; +diff --git a/src/ui/pages/Main/goals/GoalCard.tsx b/src/ui/pages/Main/goals/GoalCard.tsx +index e8f6d0a..10d9e6b 100644 +--- a/src/ui/pages/Main/goals/GoalCard.tsx ++++ b/src/ui/pages/Main/goals/GoalCard.tsx +@@ -8,13 +8,16 @@ import { + setType as setTypeRedux + } from '../../../../store/modalSlice' + import { Card } from '../../../components/Card' +- ++const Icon = styled.h1` ++ font-size: 5.5rem; ++` + type Props = { id: string } + + export default function GoalCard(props: Props) { + const dispatch = useAppDispatch() + + const goal = useAppSelector(selectGoalsMap)[props.id] ++ console.log(goal) + + const onClick = (event: React.MouseEvent) => { + event.stopPropagation() +@@ -29,6 +32,7 @@ export default function GoalCard(props: Props) { + + ${goal.targetAmount} + {asLocaleDateString(goal.targetDate)} ++ {goal.icon} + + ) + } 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/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx index 0779dda..7a19628 100644 --- a/src/ui/features/goalmanager/GoalManager.tsx +++ b/src/ui/features/goalmanager/GoalManager.tsx @@ -11,12 +11,44 @@ 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 EmojiPicker from '../../../ui/pages/EmojiPicker' +import { BaseEmoji, Picker } from 'emoji-mart' +import { TransparentButton } from '../../components/TransparentButton'; +import GoalIcon from './GoalIcon'; + +import { faSmile } from '@fortawesome/free-solid-svg-icons'; + type Props = { goal: Goal } export function GoalManager(props: Props) { - const dispatch = useAppDispatch() - + const [emojiPickerIsOpen, setEmojiPickerIsOpen] = useState(false) + const [icon, setIcon] = useState(null) + const hasIcon = () => icon != null const goal = useAppSelector(selectGoalsMap)[props.goal.id] + useEffect(() => { + setIcon(props.goal.icon) + }, [props.goal.id, props.goal.icon]) + + const addIconOnClick = (event: React.MouseEvent) => { + event.stopPropagation() + setEmojiPickerIsOpen(true) + } + + const pickEmojiOnClick = (emoji: BaseEmoji) => { + 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 dispatch = useAppDispatch() const [name, setName] = useState(null) const [targetDate, setTargetDate] = useState(null) @@ -61,18 +93,16 @@ export function GoalManager(props: Props) { 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: 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 ( @@ -92,6 +122,15 @@ export function GoalManager(props: Props) { + + + + Add icon + + + + + @@ -106,14 +145,20 @@ export function GoalManager(props: Props) { {new Date(props.goal.created).toLocaleDateString()} + + + + ) } -type FieldProps = { name: string; icon: IconDefinition } -type AddIconButtonContainerProps = { shouldShow: boolean } +type FieldProps = { name: string; icon: any } +type AddIconButtonContainerProps = { hasIcon?: boolean } type GoalIconContainerProps = { shouldShow: boolean } -type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean } + + +type EmojiPickerContainerProps = { isOpen: boolean } const Field = (props: FieldProps) => ( @@ -122,6 +167,28 @@ const Field = (props: FieldProps) => ( ) +const AddIconButtonContainer = styled.div` + display: ${(props) => (props.hasIcon ? 'none' : 'flex')}; + align-items: center; +` + +const AddIconButtonText = styled.span` + margin-left: 0.5rem; +` + +const GoalIconContainer = styled.div` + display: ${(props) => (props.shouldShow ? 'flex' : 'none')}; +` + +const Icon = styled.h1` + font-size: 5.5rem; + cursor: pointer; +` + +const EmojiPickerContainer = styled.div` + display: ${(props) => (props.isOpen ? 'flex' : 'none')}; +` + const GoalManagerContainer = styled.div` display: flex; flex-direction: column; @@ -139,6 +206,7 @@ const Group = styled.div` margin-top: 1.25rem; margin-bottom: 1.25rem; ` + const NameInput = styled.input` display: flex; background-color: transparent; diff --git a/src/ui/pages/EmojiPicker.tsx b/src/ui/pages/EmojiPicker.tsx new file mode 100644 index 0000000..42a19ff --- /dev/null +++ b/src/ui/pages/EmojiPicker.tsx @@ -0,0 +1,20 @@ +import { BaseEmoji, Picker } from 'emoji-mart' +import 'emoji-mart/css/emoji-mart.css' +import { useAppSelector } from '../../store/hooks' +import { selectMode } from '../../store/themeSlice' + +type Props = { onClick: (emoji: BaseEmoji, event: React.MouseEvent) => void } + +export default function EmojiPicker(props: Props) { + const theme = useAppSelector(selectMode) + + return ( + + ) +} \ 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..10d9e6b 100644 --- a/src/ui/pages/Main/goals/GoalCard.tsx +++ b/src/ui/pages/Main/goals/GoalCard.tsx @@ -8,13 +8,16 @@ import { setType as setTypeRedux } from '../../../../store/modalSlice' import { Card } from '../../../components/Card' - +const Icon = styled.h1` + font-size: 5.5rem; +` type Props = { id: string } export default function GoalCard(props: Props) { const dispatch = useAppDispatch() const goal = useAppSelector(selectGoalsMap)[props.id] + console.log(goal) const onClick = (event: React.MouseEvent) => { event.stopPropagation() @@ -29,6 +32,7 @@ export default function GoalCard(props: Props) { ${goal.targetAmount} {asLocaleDateString(goal.targetDate)} + {goal.icon} ) }