Skip to content

Support icons#118

Open
leahmarymathew wants to merge 1 commit intofencer-so:mainfrom
leahmarymathew:icons
Open

Support icons#118
leahmarymathew wants to merge 1 commit intofencer-so:mainfrom
leahmarymathew:icons

Conversation

@leahmarymathew
Copy link
Copy Markdown

Changes Made

  • Added optional Icon field to Goal model
  • Fixed MongoDB ObjectId deserialization issue
  • Implemented PUT /api/Goal/{id} endpoint to update icon
  • Connected frontend to backend using updateGoalIcon API
  • Completed pickEmojiOnClick handler
  • Verified icon persists after refresh

Testing

  • GET request returns goal with icon field
  • PUT request updates icon successfully
  • Emoji selection updates icon in UI
  • Icon persists after page reload

Copilot AI review requested due to automatic review settings March 21, 2026 13:04
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds per-goal icon/emoji support across the UI and API typings, enabling users to select an emoji for a goal and persist it via the existing Goal update API.

Changes:

  • Display a goal icon on cards (with a default fallback).
  • Add emoji picker flow in GoalManager to set/update a goal’s icon.
  • Extend Goal TypeScript model with an icon field (and update dependencies/lockfile metadata).

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/ui/pages/Main/goals/GoalCard.tsx Renders goal icon (fallback 🎯) and adds icon styling.
src/ui/features/goalmanager/GoalManager.tsx Adds emoji picker + icon update flow and related UI containers/state.
src/ui/features/goalmanager/GoalIcon.tsx Minor sizing tweak for rendered goal icon.
src/api/types.ts Extends Goal interface with icon.
package-lock.json Adds license metadata for emoji-mart entry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

accountId: string
transactionIds: string[]
tagIds: string[]
icon: string | null
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says the Icon field is optional, but the Goal interface makes icon a required property. If the backend may omit this field for existing goals, this should be modeled as optional (e.g., icon?: string | null) or the API layer should normalize missing icons to null so the type is accurate.

Suggested change
icon: string | null
icon?: string | null

Copilot uses AI. Check for mistakes.
font-size: 1rem;
`

const Icon = styled.h1`
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icon is rendered/styled as an h1, which is a heading element and can be confusing for screen readers and document outline. Consider using a non-heading element (e.g., span/div) and add appropriate accessibility semantics (role="img" + aria-label, or aria-hidden if decorative).

Suggested change
const Icon = styled.h1`
const Icon = styled.div.attrs({ 'aria-hidden': 'true' })`

Copilot uses AI. Check for mistakes.
</AddIconButtonContainer>

<GoalIconContainer shouldShow={hasIcon()}>
<GoalIcon icon={goal.icon} onClick={addIconOnClick} />
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GoalIconContainer visibility is driven by hasIcon() (local icon state), but the rendered icon is sourced from goal.icon (Redux). After selecting an emoji there can be a render where hasIcon() is true while goal.icon is still null/old, causing the icon area to show blank. Use a single source of truth (derive hasIcon from the same value you render, or render from icon state consistently).

Suggested change
<GoalIcon icon={goal.icon} onClick={addIconOnClick} />
<GoalIcon icon={icon} onClick={addIconOnClick} />

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +64
setIcon(emoji.native)
setEmojiPickerIsOpen(false)

const updatedGoal: Goal = {
...props.goal,
icon: emoji.native ?? props.goal.icon,
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local state is updated with setIcon(emoji.native), but the value you persist is updatedGoal.icon (which falls back to props.goal.icon). To avoid local/UI state drifting from the persisted value, set the local icon state from the same value you assign into updatedGoal.icon (and normalize to null when absent).

Suggested change
setIcon(emoji.native)
setEmojiPickerIsOpen(false)
const updatedGoal: Goal = {
...props.goal,
icon: emoji.native ?? props.goal.icon,
const nextIcon = emoji.native ?? props.goal.icon ?? null
setIcon(nextIcon)
setEmojiPickerIsOpen(false)
const updatedGoal: Goal = {
...props.goal,
icon: nextIcon,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants