diff --git a/src/ui/components/GoalIcon.tsx b/src/ui/components/GoalIcon.tsx
new file mode 100644
index 0000000..5a8c368
--- /dev/null
+++ b/src/ui/components/GoalIcon.tsx
@@ -0,0 +1,23 @@
+import React from 'react'
+import styled from 'styled-components'
+// Corrected the import path to be one level higher
+import { TransparentButton } from './TransparentButton'
+
+type Props = {
+ icon: string | null | undefined
+ onClick: (event: React.MouseEvent) => void
+}
+
+const Icon = styled.h1`
+ font-size: 5.5rem;
+ cursor: pointer;
+ margin: 0;
+`
+
+export default function GoalIcon(props: Props) {
+ return (
+
+ {props.icon}
+
+ )
+}
\ No newline at end of file
diff --git a/src/ui/features/goalmanager/GoalIcon.tsx b/src/ui/features/goalmanager/GoalIcon.tsx
index b5a0d75..b94a676 100644
--- a/src/ui/features/goalmanager/GoalIcon.tsx
+++ b/src/ui/features/goalmanager/GoalIcon.tsx
@@ -14,6 +14,7 @@ export default function GoalIcon(props: Props) {
}
const Icon = styled.h1`
- font-size: 6rem;
+ font-size: 5.5rem;
cursor: pointer;
+ margin: 0;
`
diff --git a/src/ui/features/goalmanager/GoalManager.tsx b/src/ui/features/goalmanager/GoalManager.tsx
index 0779dda..1a01bd4 100644
--- a/src/ui/features/goalmanager/GoalManager.tsx
+++ b/src/ui/features/goalmanager/GoalManager.tsx
@@ -1,62 +1,54 @@
-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 'date-fns'
+import { BaseEmoji } from 'emoji-mart'
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 { TransparentButton } from '../../components/TransparentButton'
import DatePicker from '../../components/DatePicker'
+import EmojiPicker from '../../components/EmojiPicker'
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)
+ 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,
- ])
-
- useEffect(() => {
- setName(goal.name)
- }, [goal.name])
+ setIcon(props.goal.icon)
+ }, [props.goal])
const updateNameOnChange = (event: React.ChangeEvent) => {
const nextName = event.target.value
setName(nextName)
- const updatedGoal: Goal = {
- ...props.goal,
- name: nextName,
- }
+ const updatedGoal: Goal = { ...props.goal, name: nextName }
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
}
- const updateTargetAmountOnChange = (event: React.ChangeEvent) => {
+ 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 updatedGoal: Goal = { ...props.goal, targetAmount: nextTargetAmount }
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
}
@@ -64,19 +56,66 @@ 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: Goal = { ...props.goal, targetDate: date }
dispatch(updateGoalRedux(updatedGoal))
updateGoalApi(props.goal.id, updatedGoal)
}
}
+ const hasIcon = () => icon != null
+
+ const addIconOnClick = (event: React.MouseEvent) => {
+ event.stopPropagation()
+ setEmojiPickerIsOpen(true)
+ }
+
+ // src/ui/features/goalmanager/GoalManager.tsx
+
+ const pickEmojiOnClick = (emoji: BaseEmoji, event: React.MouseEvent) => {
+ event.stopPropagation();
+ const nativeEmoji = emoji.native;
+
+ // This keeps the local UI state updated
+ setIcon(nativeEmoji);
+ setEmojiPickerIsOpen(false);
+
+ // This creates the updated goal object with ALL recent changes
+ const updatedGoal: Goal = {
+ ...props.goal,
+ icon: nativeEmoji, // The new icon
+ name: name ?? props.goal.name, // The updated name from state
+ targetDate: targetDate ?? props.goal.targetDate, // The updated date from state
+ targetAmount: targetAmount ?? props.goal.targetAmount, // The updated amount from state
+ };
+
+ // This updates the Redux store so the whole app is aware of the change
+ dispatch(updateGoalRedux(updatedGoal));
+
+ // This calls the API to save the changes to the backend
+ updateGoalApi(props.goal.id, updatedGoal);
+ };
+
return (
+ event.stopPropagation()}
+ >
+
+
+
+
+
+
+
+
+
+
+ Add icon
+
+
+
@@ -89,7 +128,10 @@ export function GoalManager(props: Props) {
-
+
@@ -103,19 +145,19 @@ export function GoalManager(props: Props) {
- {new Date(props.goal.created).toLocaleDateString()}
+
+ {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 }
+// --- STYLED COMPONENTS ---
+// These were missing from the previous version
-const Field = (props: FieldProps) => (
+const Field = (props: { name: string; icon: IconDefinition }) => (
{props.name}
@@ -139,6 +181,7 @@ const Group = styled.div`
margin-top: 1.25rem;
margin-bottom: 1.25rem;
`
+
const NameInput = styled.input`
display: flex;
background-color: transparent;
@@ -147,38 +190,77 @@ const NameInput = styled.input`
font-size: 4rem;
font-weight: bold;
color: ${({ theme }: { theme: Theme }) => theme.text};
+ width: 100%;
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+
+ &::placeholder {
+ color: ${({ theme }: { theme: Theme }) => theme.textSecondary};
+ }
`
-const FieldName = styled.h1`
- font-size: 1.8rem;
- margin-left: 1rem;
- color: rgba(174, 174, 174, 1);
- font-weight: normal;
+type EmojiPickerContainerProps = { isOpen: boolean; hasIcon: boolean }
+
+const EmojiPickerContainer = styled.div`
+ display: ${(props) => (props.isOpen ? 'flex' : 'none')};
+ position: absolute;
+ top: ${(props) => (props.hasIcon ? '6rem' : '8rem')};
+ left: 0;
+ z-index: 1000;
`
-const FieldContainer = styled.div`
- display: flex;
+
+type GoalIconContainerProps = { shouldShow: boolean }
+
+const GoalIconContainer = styled.div`
+ display: ${(props) => (props.shouldShow ? 'flex' : 'none')};
+ margin-bottom: 1rem;
+`
+
+type AddIconButtonContainerProps = { hasIcon: boolean }
+
+const AddIconButtonContainer = styled.div`
+ display: ${(props) => (props.hasIcon ? 'none' : 'flex')};
flex-direction: row;
align-items: center;
- width: 20rem;
+ margin-bottom: 1rem;
+`
- svg {
- color: rgba(174, 174, 174, 1);
- }
+const AddIconButtonText = styled.span`
+ margin-left: 0.6rem;
+ font-size: 1.5rem;
+ color: rgba(174, 174, 174, 1);
`
-const StringValue = styled.h1`
- font-size: 1.8rem;
- font-weight: bold;
+
+const Value = styled.div`
+ display: flex;
+ flex: 1;
+ margin-left: 1rem;
`
+
const StringInput = styled.input`
- display: flex;
background-color: transparent;
outline: none;
border: none;
- font-size: 1.8rem;
- font-weight: bold;
+ font-size: 2rem;
color: ${({ theme }: { theme: Theme }) => theme.text};
+ width: 100%;
`
-const Value = styled.div`
- margin-left: 2rem;
+const StringValue = styled.span`
+ font-size: 2rem;
+ color: ${({ theme }: { theme: Theme }) => theme.text};
`
+
+const FieldContainer = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ min-width: 12rem;
+`
+
+const FieldName = styled.span`
+ margin-left: 0.6rem;
+ font-size: 2rem;
+ font-weight: bold;
+ color: ${({ theme }: { theme: Theme }) => theme.text};
+`
\ No newline at end of file