@@ -14,6 +14,7 @@ import (
1414 "github.com/ignavan39/mood-diary/internal/infrastructure/i18n"
1515 "github.com/ignavan39/mood-diary/internal/presentation/styles"
1616 "github.com/ignavan39/mood-diary/internal/presentation/tui/components"
17+ "github.com/ignavan39/mood-diary/internal/presentation/tui/constants"
1718 "github.com/ignavan39/mood-diary/internal/presentation/tui/formatters"
1819 "github.com/ignavan39/mood-diary/internal/presentation/tui/state"
1920)
@@ -32,8 +33,17 @@ type CalendarScreen struct {
3233 cursorRow int
3334 cursorCol int
3435 confirmDlg * components.ConfirmationDialog
36+
37+ viewMode calendarViewMode
3538}
3639
40+ type calendarViewMode string
41+
42+ const (
43+ viewEmoji calendarViewMode = "emoji"
44+ viewHeatmap calendarViewMode = "heatmap"
45+ )
46+
3747func NewCalendarScreen (ctx context.Context , service * usecase.MoodService , translator i18n.Translator ) * CalendarScreen {
3848 now := time .Now ()
3949 return & CalendarScreen {
@@ -43,6 +53,7 @@ func NewCalendarScreen(ctx context.Context, service *usecase.MoodService, transl
4353 currentMonth : time .Date (now .Year (), now .Month (), 1 , 0 , 0 , 0 , 0 , time .UTC ),
4454 selectedDate : time .Date (now .Year (), now .Month (), now .Day (), 0 , 0 , 0 , 0 , time .UTC ),
4555 moodData : make (map [time.Time ]* entity.MoodEntry ),
56+ viewMode : viewEmoji ,
4657 }
4758}
4859
@@ -123,6 +134,13 @@ func (s *CalendarScreen) handleKeyMsg(msg tea.KeyMsg) (state.Screen, tea.Cmd) {
123134 return s , nil
124135 }
125136
137+ case "m" :
138+ if s .viewMode == viewEmoji {
139+ s .viewMode = viewHeatmap
140+ } else {
141+ s .viewMode = viewEmoji
142+ }
143+
126144 case "esc" , "q" :
127145 return s , state .NavigateBack ()
128146 }
@@ -211,7 +229,7 @@ func (s *CalendarScreen) View() string {
211229 var b strings.Builder
212230
213231 monthName := s .t (fmt .Sprintf ("date.month.%d" , s .currentMonth .Month ()))
214- header := styles .HeaderStyle .Render (fmt .Sprintf ("📅 %s %d" , monthName , s .currentMonth .Year ()))
232+ header := styles .HeaderStyle .Render (fmt .Sprintf ("%s %s %d" , constants . Emoji . Calendar , monthName , s .currentMonth .Year ()))
215233 b .WriteString (header )
216234 b .WriteString ("\n \n " )
217235
@@ -221,7 +239,7 @@ func (s *CalendarScreen) View() string {
221239 }
222240
223241 if s .Error != nil {
224- b .WriteString (styles .ErrorStyle . Render (s .t (i18n .CommonErrorPrefixKey ) + s .Error .Error ()))
242+ b .WriteString (styles .ErrorStyle (s .t (i18n .CommonErrorPrefixKey ) + s .Error .Error ()))
225243 b .WriteString ("\n \n " )
226244 }
227245
@@ -244,19 +262,25 @@ func (s *CalendarScreen) View() string {
244262 b .WriteString (strings .Repeat ("─" , 42 ) + "\n " )
245263
246264 rows := s .buildCalendarGrid ()
265+ if s .viewMode == viewHeatmap {
266+ rows = s .buildHeatmapGrid ()
267+ }
247268 for i , row := range rows {
248269 var line strings.Builder
249270 for j , cell := range row {
250271 style := lipgloss .NewStyle ().Width (6 ).Align (lipgloss .Center )
251272
252273 if i == s .cursorRow && j == s .cursorCol {
253274 style = style .Background (styles .PastelDarkSlateBlue ).Foreground (styles .TextLight ).Bold (true )
275+ } else if cell .bgColor != "" {
276+ style = style .Background (cell .bgColor )
277+ if cell .date .Month () != s .currentMonth .Month () {
278+ style = style .Foreground (styles .TextMuted )
279+ } else {
280+ style = style .Foreground (styles .TextLight )
281+ }
254282 } else if entry , ok := s .moodData [cell .date ]; ok {
255-
256283 style = style .Foreground (styles .GetMoodColor (int (entry .Level )))
257- } else if cell .date .Month () != s .currentMonth .Month () {
258-
259- style = style .Foreground (styles .TextMuted )
260284 }
261285
262286 line .WriteString (style .Render (cell .text ))
@@ -272,11 +296,20 @@ func (s *CalendarScreen) View() string {
272296}
273297
274298type calendarCell struct {
275- date time.Time
276- text string
299+ date time.Time
300+ text string
301+ bgColor lipgloss.Color
277302}
278303
279304func (s * CalendarScreen ) buildCalendarGrid () [][]calendarCell {
305+ return s .buildGrid (viewEmoji )
306+ }
307+
308+ func (s * CalendarScreen ) buildHeatmapGrid () [][]calendarCell {
309+ return s .buildGrid (viewHeatmap )
310+ }
311+
312+ func (s * CalendarScreen ) buildGrid (mode calendarViewMode ) [][]calendarCell {
280313 year , month := s .currentMonth .Year (), s .currentMonth .Month ()
281314 firstDay := time .Date (year , month , 1 , 0 , 0 , 0 , 0 , time .UTC )
282315
@@ -295,7 +328,17 @@ func (s *CalendarScreen) buildCalendarGrid() [][]calendarCell {
295328 cell := calendarCell {date : current }
296329
297330 if current .Month () != month {
298- cell .text = lipgloss .NewStyle ().Foreground (styles .TextMuted ).Render (" " )
331+ cell .bgColor = lipgloss .Color ("#F0F0F0" )
332+ cell .text = " "
333+ } else if mode == viewHeatmap {
334+ entry , ok := s .moodData [current ]
335+ if ok {
336+ cell .bgColor = styles .GetMoodColor (int (entry .Level ))
337+ cell .text = fmt .Sprintf ("%2d" , current .Day ())
338+ } else {
339+ cell .bgColor = styles .PastelGray
340+ cell .text = fmt .Sprintf ("%2d" , current .Day ())
341+ }
299342 } else {
300343 dayNum := fmt .Sprintf ("%2d" , current .Day ())
301344 if entry , ok := s .moodData [current ]; ok {
@@ -313,9 +356,13 @@ func (s *CalendarScreen) buildCalendarGrid() [][]calendarCell {
313356
314357func (s * CalendarScreen ) renderFooter () string {
315358 dateStr := formatters .FormatDate (s .selectedDate )
359+ modeIcon := constants .Emoji .Calendar
360+ if s .viewMode == viewHeatmap {
361+ modeIcon = constants .Emoji .Heatmap
362+ }
316363 if entry , ok := s .moodData [s .selectedDate ]; ok {
317364 note := formatters .TruncateNote (entry .Note , 25 )
318- return fmt .Sprintf ("📍 %s | %s %d/10 | %s" , dateStr , entry .Level .Emoji (), entry .Level .Int (), note )
365+ return fmt .Sprintf ("%s %s | %s %d/10 | %s" , modeIcon , dateStr , entry .Level .Emoji (), entry .Level .Int (), note )
319366 }
320- return fmt .Sprintf ("📍 %s | %s" , dateStr , s .t (i18n .CalendarNoEntryKey ))
367+ return fmt .Sprintf ("%s %s | %s" , modeIcon , dateStr , s .t (i18n .CalendarNoEntryKey ))
321368}
0 commit comments