@@ -7,6 +7,7 @@ import { useQuery } from '@wasp/queries';
77import getAllTasksByUser from '@wasp/queries/getAllTasksByUser' ;
88import { Task } from '@wasp/entities' ;
99import { CgSpinner } from 'react-icons/cg' ;
10+ import { XSquare } from 'lucide-react' ;
1011
1112export default function DemoAppPage ( ) {
1213 return (
@@ -35,12 +36,20 @@ export default function DemoAppPage() {
3536type TodoProps = Pick < Task , 'id' | 'isDone' | 'description' | 'time' > ;
3637
3738function Todo ( { id, isDone, description, time } : TodoProps ) {
38- const handleCheckboxChange = async ( e : React . ChangeEvent < HTMLInputElement > ) => {
39- await updateTask ( { id, isDone : e . currentTarget . checked } ) ;
39+ const handleCheckboxChange = async (
40+ e : React . ChangeEvent < HTMLInputElement >
41+ ) => {
42+ await updateTask ( {
43+ id,
44+ isDone : e . currentTarget . checked ,
45+ } ) ;
4046 } ;
4147
4248 const handleTimeChange = async ( e : React . ChangeEvent < HTMLInputElement > ) => {
43- await updateTask ( { id, time : e . currentTarget . value } ) ;
49+ await updateTask ( {
50+ id,
51+ time : e . currentTarget . value ,
52+ } ) ;
4453 } ;
4554
4655 const handleDeleteClick = async ( ) => {
@@ -53,43 +62,60 @@ function Todo({ id, isDone, description, time }: TodoProps) {
5362 < div className = 'flex items-center gap-3' >
5463 < input
5564 type = 'checkbox'
56- className = 'ml-1 form-checkbox bg-purple-500 checked:bg-purple-300 rounded border-purple-600 duration-200 ease-in-out hover:bg-purple-600 hover:checked:bg-purple-600 focus:ring focus:ring-purple-300 focus:checked:bg-purple-400 focus:ring-opacity-50'
65+ className = 'ml-1 form-checkbox bg-purple-300 checked:bg-purple-300 rounded border-purple-400 duration-200 ease-in-out hover:bg-purple-400 hover:checked:bg-purple-600 focus:ring focus:ring-purple-300 focus:checked:bg-purple-400 focus:ring-opacity-50 text-black '
5766 checked = { isDone }
5867 onChange = { handleCheckboxChange }
5968 />
60- < span className = { `text-slate-600 ${ isDone ? 'line-through text-slate-500' : '' } ` } > { description } </ span >
69+ < span
70+ className = { `text-slate-600 ${
71+ isDone ? 'line-through text-slate-500' : ''
72+ } `}
73+ >
74+ { description }
75+ </ span >
6176 </ div >
6277 < div className = 'flex items-center gap-2' >
6378 < input
6479 id = 'time'
6580 type = 'number'
6681 min = { 0.5 }
6782 step = { 0.5 }
68- className = { `w-15 h-8 text-center text-slate-600 text-xs rounded border border-gray-200 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-purple-300 focus:ring-opacity-50 ${
83+ className = { `w-18 h-8 text-center text-slate-600 text-xs rounded border border-gray-200 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-purple-300 focus:ring-opacity-50 ${
6984 isDone && 'pointer-events-none opacity-50'
7085 } `}
7186 value = { time }
7287 onChange = { handleTimeChange }
7388 />
74- < span className = { `italic text-slate-600 text-xs ${ isDone ? 'text-slate-500' : '' } ` } > hrs</ span >
89+ < span
90+ className = { `italic text-slate-600 text-xs ${
91+ isDone ? 'text-slate-500' : ''
92+ } `}
93+ >
94+ hrs
95+ </ span >
7596 </ div >
7697 </ div >
7798 < div className = 'flex items-center justify-end w-15' >
78- < button className = { ` p-1 ${ ! isDone ? 'hidden' : '' } ` } onClick = { handleDeleteClick } >
79- ❌
99+ < button className = ' p-1' onClick = { handleDeleteClick } title = 'Remove task' >
100+ < XSquare size = '20' className = 'text-red-600 hover:text-red-700' />
80101 </ button >
81102 </ div >
82103 </ div >
83104 ) ;
84105}
85106
86- function NewTaskForm ( { handleCreateTask } : { handleCreateTask : typeof createTask } ) {
107+ function NewTaskForm ( {
108+ handleCreateTask,
109+ } : {
110+ handleCreateTask : typeof createTask ;
111+ } ) {
87112 const [ description , setDescription ] = useState < string > ( '' ) ;
88113 const [ todaysHours , setTodaysHours ] = useState < string > ( '8' ) ;
89114 const [ response , setResponse ] = useState < any > ( null ) ;
90115 const [ isPlanGenerating , setIsPlanGenerating ] = useState < boolean > ( false ) ;
91116
92- const { data : tasks , isLoading : isTasksLoading } = useQuery ( getAllTasksByUser ) ;
117+ const { data : tasks , isLoading : isTasksLoading } =
118+ useQuery ( getAllTasksByUser ) ;
93119
94120 useEffect ( ( ) => {
95121 console . log ( 'response' , response ) ;
@@ -107,7 +133,9 @@ function NewTaskForm({ handleCreateTask }: { handleCreateTask: typeof createTask
107133 const handleGeneratePlan = async ( ) => {
108134 try {
109135 setIsPlanGenerating ( true ) ;
110- const response = await generateGptResponse ( { hours : todaysHours } ) ;
136+ const response = await generateGptResponse ( {
137+ hours : todaysHours ,
138+ } ) ;
111139 if ( response ) {
112140 console . log ( 'response' , response ) ;
113141 setResponse ( JSON . parse ( response ) ) ;
@@ -130,6 +158,11 @@ function NewTaskForm({ handleCreateTask }: { handleCreateTask: typeof createTask
130158 placeholder = 'Enter task description'
131159 value = { description }
132160 onChange = { ( e ) => setDescription ( e . currentTarget . value ) }
161+ onKeyDown = { ( e ) => {
162+ if ( e . key === 'Enter' ) {
163+ handleSubmit ( ) ;
164+ }
165+ } }
133166 />
134167 < button
135168 type = 'button'
@@ -146,50 +179,61 @@ function NewTaskForm({ handleCreateTask }: { handleCreateTask: typeof createTask
146179 { tasks ! ! && tasks . length > 0 ? (
147180 < div className = 'space-y-4' >
148181 { tasks . map ( ( task : Task ) => (
149- < Todo key = { task . id } id = { task . id } isDone = { task . isDone } description = { task . description } time = { task . time } />
182+ < Todo
183+ key = { task . id }
184+ id = { task . id }
185+ isDone = { task . isDone }
186+ description = { task . description }
187+ time = { task . time }
188+ />
150189 ) ) }
151- < div className = 'flex flex-col gap-3' >
152- < div className = 'flex items-center justify-between gap-3' >
153- < label htmlFor = 'time' className = 'text-sm text-gray-600 text-nowrap font-semibold' >
154- How many hours will you work today?
155- </ label >
156- < input
157- type = 'number'
158- id = 'time'
159- step = { 0.5 }
160- min = { 1 }
161- max = { 24 }
162- className = 'min-w-[7rem] text-gray-800/90 text-center font-medium rounded-md border border-gray-200 bg-yellow-50 hover:bg-yellow-100 shadow-md focus:outline-none focus:border-transparent focus:shadow-none duration-200 ease-in-out hover:shadow-none'
163- value = { todaysHours }
164- onChange = { ( e ) => setTodaysHours ( e . currentTarget . value ) }
165- />
166- </ div >
167- </ div >
190+ < div className = 'flex flex-col gap-3' >
191+ < div className = 'flex items-center justify-between gap-3' >
192+ < label
193+ htmlFor = 'time'
194+ className = 'text-sm text-gray-600 dark:text-gray-300 text-nowrap font-semibold'
195+ >
196+ How many hours will you work today?
197+ </ label >
198+ < input
199+ type = 'number'
200+ id = 'time'
201+ step = { 0.5 }
202+ min = { 1 }
203+ max = { 24 }
204+ className = 'min-w-[7rem] text-gray-800/90 text-center font-medium rounded-md border border-gray-200 bg-yellow-50 hover:bg-yellow-100 shadow-md focus:outline-none focus:border-transparent focus:shadow-none duration-200 ease-in-out hover:shadow-none'
205+ value = { todaysHours }
206+ onChange = { ( e ) => setTodaysHours ( e . currentTarget . value ) }
207+ />
208+ </ div >
209+ </ div >
168210 </ div >
169211 ) : (
170212 < div className = 'text-gray-600 text-center' > Add tasks to begin</ div >
171213 ) }
172214 </ div >
173215
174-
175216 < button
176217 type = 'button'
177- disabled = { isPlanGenerating || tasks ?. length === 0 }
218+ disabled = { isPlanGenerating || tasks ?. length === 0 }
178219 onClick = { ( ) => handleGeneratePlan ( ) }
179220 className = 'flex items-center justify-center min-w-[7rem] font-medium text-gray-800/90 bg-yellow-50 shadow-md ring-1 ring-inset ring-slate-200 py-2 px-4 rounded-md hover:bg-yellow-100 duration-200 ease-in-out focus:outline-none focus:shadow-none hover:shadow-none disabled:opacity-70 disabled:cursor-not-allowed'
180221 >
181- { isPlanGenerating ?
182- < >
183- < CgSpinner className = 'inline-block mr-2 animate-spin' />
184- Generating...
185- </ >
186- :
187- 'Generate Schedule' }
222+ { isPlanGenerating ? (
223+ < >
224+ < CgSpinner className = 'inline-block mr-2 animate-spin' />
225+ Generating...
226+ </ >
227+ ) : (
228+ 'Generate Schedule'
229+ ) }
188230 </ button >
189231
190232 { ! ! response && (
191233 < div className = 'flex flex-col' >
192- < h3 className = 'text-lg font-semibold text-gray-900 dark:text-white' > Today's Schedule</ h3 >
234+ < h3 className = 'text-lg font-semibold text-gray-900 dark:text-white' >
235+ Today's Schedule
236+ </ h3 >
193237
194238 < TaskTable schedule = { response . schedule } />
195239 </ div >
@@ -210,11 +254,18 @@ function TaskTable({ schedule }: { schedule: any[] }) {
210254 < tr >
211255 < th
212256 className = { `flex items-center justify-between gap-5 py-4 px-3 text-slate-800 border rounded-md border-slate-200 ${
213- task . priority === 'high' ? 'bg-red-50' : task . priority === 'low' ? 'bg-green-50' : 'bg-yellow-50'
257+ task . priority === 'high'
258+ ? 'bg-red-50'
259+ : task . priority === 'low'
260+ ? 'bg-green-50'
261+ : 'bg-yellow-50'
214262 } `}
215263 >
216264 < span > { task . name } </ span >
217- < span className = 'opacity-70 text-xs font-medium italic' > { task . priority } priority</ span >
265+ < span className = 'opacity-70 text-xs font-medium italic' >
266+ { ' ' }
267+ { task . priority } priority
268+ </ span >
218269 </ th >
219270 </ tr >
220271 </ thead >
@@ -224,7 +275,10 @@ function TaskTable({ schedule }: { schedule: any[] }) {
224275 < td
225276 className = { `flex items-center justify-between py-2 px-3 text-slate-600 border rounded-md border-purple-100 bg-purple-50` }
226277 >
227- < Subtask description = { subtask . description } time = { subtask . time } />
278+ < Subtask
279+ description = { subtask . description }
280+ time = { subtask . time }
281+ />
228282 </ td >
229283 </ tr >
230284 ) ) }
@@ -234,7 +288,10 @@ function TaskTable({ schedule }: { schedule: any[] }) {
234288 < td
235289 className = { `flex items-center justify-between py-2 px-3 text-slate-600 border rounded-md border-purple-100 bg-purple-50` }
236290 >
237- < Subtask description = { breakItem . description } time = { breakItem . time } />
291+ < Subtask
292+ description = { breakItem . description }
293+ time = { breakItem . time }
294+ />
238295 </ td >
239296 </ tr >
240297 ) ) }
@@ -252,8 +309,10 @@ function Subtask({ description, time }: { description: string; time: number }) {
252309 if ( time === 0 ) return 0 ;
253310 const hours = Math . floor ( time ) ;
254311 const minutes = Math . round ( ( time - hours ) * 60 ) ;
255- return `${ hours > 0 ? hours + 'hr' : '' } ${ minutes > 0 ? minutes + 'min' : '' } ` ;
256- }
312+ return `${ hours > 0 ? hours + 'hr' : '' } ${
313+ minutes > 0 ? minutes + 'min' : ''
314+ } `;
315+ } ;
257316
258317 const minutes = useMemo ( ( ) => convertHrsToMinutes ( time ) , [ time ] ) ;
259318
@@ -265,8 +324,20 @@ function Subtask({ description, time }: { description: string; time: number }) {
265324 checked = { isDone }
266325 onChange = { ( e ) => setIsDone ( e . currentTarget . checked ) }
267326 />
268- < span className = { `text-slate-600 ${ isDone ? 'line-through text-slate-500 opacity-50' : '' } ` } > { description } </ span >
269- < span className = { `text-slate-600 ${ isDone ? 'line-through text-slate-500 opacity-50' : '' } ` } > { minutes } </ span >
327+ < span
328+ className = { `text-slate-600 ${
329+ isDone ? 'line-through text-slate-500 opacity-50' : ''
330+ } `}
331+ >
332+ { description }
333+ </ span >
334+ < span
335+ className = { `text-slate-600 ${
336+ isDone ? 'line-through text-slate-500 opacity-50' : ''
337+ } `}
338+ >
339+ { minutes }
340+ </ span >
270341 </ >
271342 ) ;
272343}
0 commit comments