@@ -5,12 +5,16 @@ import {
55 ModalHeader ,
66 ModalBody ,
77 ModalFooter ,
8- Listbox ,
9- ListboxSection ,
108 ListboxItem ,
9+ Textarea ,
10+ Select ,
11+ SelectItem ,
12+ SelectSection ,
1113} from '@nextui-org/react' ;
12- import { useHabitsStore , useOccurrencesStore } from '@stores' ;
14+ import { ArrowsClockwise } from '@phosphor-icons/react' ;
15+ import { useHabitsStore , useNotesStore , useOccurrencesStore } from '@stores' ;
1316import { useUser } from '@supabase/auth-helpers-react' ;
17+ import { getHabitIconUrl } from '@utils' ;
1418import { format } from 'date-fns' ;
1519import React , { type MouseEventHandler } from 'react' ;
1620import { Link } from 'react-router-dom' ;
@@ -29,7 +33,9 @@ const AddOccurrenceDialog = ({
2933 const { habits } = useHabitsStore ( ) ;
3034 const user = useUser ( ) ;
3135 const { addOccurrence, addingOccurrence } = useOccurrencesStore ( ) ;
32- const [ selectedHabitIds , setSelectedHabitIds ] = React . useState < string [ ] > ( [ ] ) ;
36+ const [ selectedHabitId , setSelectedHabitId ] = React . useState ( '' ) ;
37+ const [ note , setNote ] = React . useState ( '' ) ;
38+ const { addNote, addingNote } = useNotesStore ( ) ;
3339
3440 const habitsByTraitName = React . useMemo ( ( ) => {
3541 return Object . groupBy ( habits , ( habit ) => habit . trait ?. name || 'Unknown' ) ;
@@ -44,36 +50,37 @@ const AddOccurrenceDialog = ({
4450 const handleSubmit : MouseEventHandler < HTMLButtonElement > = async ( event ) => {
4551 event . preventDefault ( ) ;
4652
47- if ( ! user || ! hasHabits ) {
53+ if ( ! user || ! hasHabits || ! selectedHabitId ) {
4854 return null ;
4955 }
5056
51- const addOccurrences = selectedHabitIds . map ( ( id ) => {
52- return addOccurrence ( {
53- day : date . toISOString ( ) . split ( 'T' ) [ 0 ] ,
54- timestamp : + date ,
55- habitId : + id ,
56- userId : user ?. id as string ,
57- time : null , // TODO: Add time picker
58- } ) ;
57+ const newOccurrence = await addOccurrence ( {
58+ day : date . toISOString ( ) . split ( 'T' ) [ 0 ] ,
59+ timestamp : + date ,
60+ habitId : + selectedHabitId ,
61+ userId : user ?. id as string ,
62+ time : null , // TODO: Add time picker
5963 } ) ;
6064
61- await Promise . all ( addOccurrences ) ;
65+ if ( note ) {
66+ await addNote ( {
67+ content : note ,
68+ occurrenceId : newOccurrence . id ,
69+ userId : user . id ,
70+ } ) ;
71+ }
6272
6373 handleClose ( ) ;
6474 } ;
6575
6676 const handleClose = ( ) => {
67- setSelectedHabitIds ( [ ] ) ;
77+ setSelectedHabitId ( '' ) ;
78+ setNote ( '' ) ;
6879 onClose ( ) ;
6980 } ;
7081
7182 const handleHabitSelect = ( habitId : string ) => {
72- if ( selectedHabitIds . includes ( habitId ) ) {
73- setSelectedHabitIds ( selectedHabitIds . filter ( ( id ) => id !== habitId ) ) ;
74- } else {
75- setSelectedHabitIds ( [ ...selectedHabitIds , habitId ] ) ;
76- }
83+ setSelectedHabitId ( habitId ) ;
7784 } ;
7885
7986 return (
@@ -88,39 +95,57 @@ const AddOccurrenceDialog = ({
8895 Add habit entries for { format ( date , 'iii, LLL d, y' ) }
8996 </ ModalHeader >
9097 < ModalBody >
91- < Listbox
98+ < Select
99+ disableSelectorIconRotation
92100 variant = "flat"
93- color = "primary"
94- selectionMode = "multiple"
95- selectedKeys = { selectedHabitIds }
96- disabledKeys = { [ 'none' ] }
97- emptyContent = "No habits yet. Create a habit to get started."
98- className = "max-h-80 overflow-auto rounded border border-neutral-200 p-2 dark:border-neutral-800"
101+ selectedKeys = { selectedHabitId }
102+ label = {
103+ hasHabits
104+ ? 'Habits'
105+ : 'No habits yet. Create a habit to get started.'
106+ }
107+ description = "Select from your habits"
108+ data-testid = "habit-select"
109+ selectorIcon = { < ArrowsClockwise /> }
99110 >
100111 { Object . keys ( habitsByTraitName ) . map ( ( traitName ) => (
101- < ListboxSection key = { traitName } showDivider title = { traitName } >
112+ < SelectSection key = { traitName } title = { traitName } showDivider >
102113 { habitsByTraitName [ traitName ] ? (
103- habitsByTraitName [ traitName ] . map ( ( habit ) => (
104- < ListboxItem
105- key = { habit . id . toString ( ) }
106- onClick = { ( ) => handleHabitSelect ( habit . id . toString ( ) ) }
107- >
108- { habit . name }
109- </ ListboxItem >
110- ) )
114+ habitsByTraitName [ traitName ] . map ( ( habit ) => {
115+ const iconUrl = getHabitIconUrl ( habit . iconPath ) ;
116+
117+ return (
118+ < SelectItem
119+ key = { habit . id . toString ( ) }
120+ textValue = { habit . name }
121+ onClick = { ( ) => handleHabitSelect ( habit . id . toString ( ) ) }
122+ >
123+ < div className = "flex items-center gap-2" >
124+ < img
125+ src = { iconUrl }
126+ alt = { habit . name }
127+ role = "habit-icon"
128+ className = "h-4 w-4"
129+ />
130+ < span > { habit . name } </ span >
131+ </ div >
132+ </ SelectItem >
133+ ) ;
134+ } )
111135 ) : (
112136 < ListboxItem key = "none" > No habits</ ListboxItem >
113137 ) }
114- </ ListboxSection >
138+ </ SelectSection >
115139 ) ) }
116- </ Listbox >
140+ </ Select >
141+ < Textarea onValueChange = { setNote } value = { note } placeholder = "Note" />
117142 </ ModalBody >
118143 < ModalFooter >
119144 < Button
120145 as = { hasHabits ? Button : Link }
121146 type = "submit"
122147 color = "primary"
123- isLoading = { addingOccurrence }
148+ isLoading = { addingOccurrence || addingNote }
124149 onClick = { hasHabits ? handleSubmit : undefined }
125150 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
126151 // @ts -ignore
0 commit comments