@@ -8,18 +8,21 @@ import getAllTasksByUser from '@wasp/queries/getAllTasksByUser';
88import { Task } from '@wasp/entities' ;
99import { CgSpinner } from 'react-icons/cg' ;
1010import { TiDelete } from 'react-icons/ti' ;
11+ import { type GeneratedSchedule } from '../../shared/types' ;
12+ import { MainTask , Subtask } from '@wasp/shared/types' ;
1113
1214export default function DemoAppPage ( ) {
1315 return (
1416 < div className = 'py-10 lg:mt-10' >
1517 < div className = 'mx-auto max-w-7xl px-6 lg:px-8' >
1618 < div className = 'mx-auto max-w-4xl text-center' >
1719 < h2 className = 'mt-2 text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl dark:text-white' >
18- < span className = 'text-yellow-500' > AI</ span > Day Scheduler
20+ < span className = 'text-yellow-500' > AI</ span > Day Scheduler
1921 </ h2 >
2022 </ div >
2123 < p className = 'mx-auto mt-6 max-w-2xl text-center text-lg leading-8 text-gray-600 dark:text-white' >
22- This example app uses OpenAI's chat completions with function calling to return a structured JSON object. Try it out, enter your day's tasks, and let AI do the rest!
24+ This example app uses OpenAI's chat completions with function calling to return a structured JSON object. Try
25+ it out, enter your day's tasks, and let AI do the rest!
2326 </ p >
2427 { /* begin AI-powered Todo List */ }
2528 < div className = 'my-8 border rounded-3xl border-gray-900/10 dark:border-gray-100/10' >
@@ -36,9 +39,7 @@ export default function DemoAppPage() {
3639type TodoProps = Pick < Task , 'id' | 'isDone' | 'description' | 'time' > ;
3740
3841function Todo ( { id, isDone, description, time } : TodoProps ) {
39- const handleCheckboxChange = async (
40- e : React . ChangeEvent < HTMLInputElement >
41- ) => {
42+ const handleCheckboxChange = async ( e : React . ChangeEvent < HTMLInputElement > ) => {
4243 await updateTask ( {
4344 id,
4445 isDone : e . currentTarget . checked ,
@@ -66,13 +67,7 @@ function Todo({ id, isDone, description, time }: TodoProps) {
6667 checked = { isDone }
6768 onChange = { handleCheckboxChange }
6869 />
69- < span
70- className = { `text-slate-600 ${
71- isDone ? 'line-through text-slate-500' : ''
72- } `}
73- >
74- { description }
75- </ span >
70+ < span className = { `text-slate-600 ${ isDone ? 'line-through text-slate-500' : '' } ` } > { description } </ span >
7671 </ div >
7772 < div className = 'flex items-center gap-2' >
7873 < input
@@ -86,13 +81,7 @@ function Todo({ id, isDone, description, time }: TodoProps) {
8681 value = { time }
8782 onChange = { handleTimeChange }
8883 />
89- < span
90- className = { `italic text-slate-600 text-xs ${
91- isDone ? 'text-slate-500' : ''
92- } `}
93- >
94- hrs
95- </ span >
84+ < span className = { `italic text-slate-600 text-xs ${ isDone ? 'text-slate-500' : '' } ` } > hrs</ span >
9685 </ div >
9786 </ div >
9887 < div className = 'flex items-center justify-end w-15' >
@@ -104,22 +93,75 @@ function Todo({ id, isDone, description, time }: TodoProps) {
10493 ) ;
10594}
10695
107- function NewTaskForm ( {
108- handleCreateTask,
109- } : {
110- handleCreateTask : typeof createTask ;
111- } ) {
96+ function NewTaskForm ( { handleCreateTask } : { handleCreateTask : typeof createTask } ) {
11297 const [ description , setDescription ] = useState < string > ( '' ) ;
11398 const [ todaysHours , setTodaysHours ] = useState < string > ( '8' ) ;
114- const [ response , setResponse ] = useState < any > ( null ) ;
99+ const [ response , setResponse ] = useState < GeneratedSchedule | null > ( {
100+ mainTasks : [
101+ {
102+ name : 'Respond to emails' ,
103+ priority : 'high' ,
104+ } ,
105+ {
106+ name : 'Learn WASP' ,
107+ priority : 'low' ,
108+ } ,
109+ {
110+ name : 'Read a book' ,
111+ priority : 'medium' ,
112+ } ,
113+ ] ,
114+ subtasks : [
115+ {
116+ description : 'Read introduction and chapter 1' ,
117+ time : 0.5 ,
118+ mainTaskName : 'Read a book' ,
119+ } ,
120+ {
121+ description : 'Read chapter 2 and take notes' ,
122+ time : 0.3 ,
123+ mainTaskName : 'Read a book' ,
124+ } ,
125+ {
126+ description : 'Read chapter 3 and summarize key points' ,
127+ time : 0.2 ,
128+ mainTaskName : 'Read a book' ,
129+ } ,
130+ {
131+ description : 'Check and respond to important emails' ,
132+ time : 1 ,
133+ mainTaskName : 'Respond to emails' ,
134+ } ,
135+ {
136+ description : 'Organize and prioritize remaining emails' ,
137+ time : 0.5 ,
138+ mainTaskName : 'Respond to emails' ,
139+ } ,
140+ {
141+ description : 'Draft responses to urgent emails' ,
142+ time : 0.5 ,
143+ mainTaskName : 'Respond to emails' ,
144+ } ,
145+ {
146+ description : 'Watch tutorial video on WASP' ,
147+ time : 0.5 ,
148+ mainTaskName : 'Learn WASP' ,
149+ } ,
150+ {
151+ description : 'Complete online quiz on the basics of WASP' ,
152+ time : 1.5 ,
153+ mainTaskName : 'Learn WASP' ,
154+ } ,
155+ {
156+ description : 'Review quiz answers and clarify doubts' ,
157+ time : 1 ,
158+ mainTaskName : 'Learn WASP' ,
159+ } ,
160+ ] ,
161+ } ) ;
115162 const [ isPlanGenerating , setIsPlanGenerating ] = useState < boolean > ( false ) ;
116163
117- const { data : tasks , isLoading : isTasksLoading } =
118- useQuery ( getAllTasksByUser ) ;
119-
120- useEffect ( ( ) => {
121- console . log ( 'response' , response ) ;
122- } , [ response ] ) ;
164+ const { data : tasks , isLoading : isTasksLoading } = useQuery ( getAllTasksByUser ) ;
123165
124166 const handleSubmit = async ( ) => {
125167 try {
@@ -137,8 +179,7 @@ function NewTaskForm({
137179 hours : todaysHours ,
138180 } ) ;
139181 if ( response ) {
140- console . log ( 'response' , response ) ;
141- setResponse ( JSON . parse ( response ) ) ;
182+ setResponse ( response ) ;
142183 }
143184 } catch ( err : any ) {
144185 window . alert ( 'Error: ' + ( err . message || 'Something went wrong' ) ) ;
@@ -179,20 +220,11 @@ function NewTaskForm({
179220 { tasks ! ! && tasks . length > 0 ? (
180221 < div className = 'space-y-4' >
181222 { tasks . map ( ( task : Task ) => (
182- < Todo
183- key = { task . id }
184- id = { task . id }
185- isDone = { task . isDone }
186- description = { task . description }
187- time = { task . time }
188- />
223+ < Todo key = { task . id } id = { task . id } isDone = { task . isDone } description = { task . description } time = { task . time } />
189224 ) ) }
190225 < div className = 'flex flex-col gap-3' >
191226 < 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- >
227+ < label htmlFor = 'time' className = 'text-sm text-gray-600 dark:text-gray-300 text-nowrap font-semibold' >
196228 How many hours will you work today?
197229 </ label >
198230 < input
@@ -231,87 +263,98 @@ function NewTaskForm({
231263
232264 { ! ! response && (
233265 < div className = 'flex flex-col' >
234- < h3 className = 'text-lg font-semibold text-gray-900 dark:text-white' >
235- Today's Schedule
236- </ h3 >
266+ < h3 className = 'text-lg font-semibold text-gray-900 dark:text-white' > Today's Schedule</ h3 >
237267
238- < TaskTable schedule = { response . schedule } />
268+ < TaskTable schedule = { response } />
239269 </ div >
240270 ) }
241271 </ div >
242272 ) ;
243273}
244274
245- function TaskTable ( { schedule } : { schedule : any [ ] } ) {
275+ function TaskTable ( { schedule } : { schedule : GeneratedSchedule } ) {
246276 return (
247277 < div className = 'flex flex-col gap-6 py-6' >
248- { schedule . map ( ( task : any ) => (
249- < table
250- key = { task . name }
251- className = 'table-auto w-full border-separate border border-spacing-2 rounded-md border-slate-200 shadow-sm'
252- >
253- < thead >
254- < tr >
255- < th
256- className = { `flex items-center justify-between gap-5 py-4 px-3 text-slate-800 border rounded-md border-slate-200 ${
257- task . priority === 'high'
258- ? 'bg-red-50'
259- : task . priority === 'low'
260- ? 'bg-green-50'
261- : 'bg-yellow-50'
262- } `}
263- >
264- < span > { task . name } </ span >
265- < span className = 'opacity-70 text-xs font-medium italic' >
266- { ' ' }
267- { task . priority } priority
268- </ span >
269- </ th >
270- </ tr >
271- </ thead >
272- < tbody className = '' >
273- { task . subtasks . map ( ( subtask : { description : any ; time : any } ) => (
274- < tr >
275- < td
276- className = { `flex items-center justify-between py-2 px-3 text-slate-600 border rounded-md border-purple-100 bg-purple-50` }
277- >
278- < Subtask
279- description = { subtask . description }
280- time = { subtask . time }
281- />
282- </ td >
283- </ tr >
284- ) ) }
278+ < table className = 'table-auto w-full border-separate border border-spacing-2 rounded-md border-slate-200 shadow-sm' >
279+ { ! ! schedule . mainTasks ? (
280+ schedule . mainTasks
281+ . map ( ( mainTask ) => < MainTask key = { mainTask . name } mainTask = { mainTask } subtasks = { schedule . subtasks } /> )
282+ . sort ( ( a , b ) => {
283+ const priorityOrder = [ 'low' , 'medium' , 'high' ] ;
284+ if ( a . props . mainTask . priority && b . props . mainTask . priority ) {
285+ return (
286+ priorityOrder . indexOf ( b . props . mainTask . priority ) - priorityOrder . indexOf ( a . props . mainTask . priority )
287+ ) ;
288+ } else {
289+ return 0 ;
290+ }
291+ } )
292+ ) : (
293+ < div className = 'text-slate-600 text-center' > OpenAI didn't return any Main Tasks. Try again.</ div >
294+ ) }
295+ </ table >
285296
286- { task . breaks . map ( ( breakItem : { description : any ; time : any } ) => (
287- < tr key = { breakItem . description } >
288- < td
289- className = { `flex items-center justify-between py-2 px-3 text-slate-600 border rounded-md border-purple-100 bg-purple-50` }
290- >
291- < Subtask
292- description = { breakItem . description }
293- time = { breakItem . time }
294- />
295- </ td >
296- </ tr >
297- ) ) }
298- </ tbody >
299- </ table >
300- ) ) }
297+ { /* ))} */ }
301298 </ div >
302299 ) ;
303300}
304301
302+ function MainTask ( { mainTask, subtasks } : { mainTask : MainTask ; subtasks : Subtask [ ] } ) {
303+ return (
304+ < >
305+ < thead >
306+ < tr >
307+ < th
308+ className = { `flex items-center justify-between gap-5 py-4 px-3 text-slate-800 border rounded-md border-slate-200 bg-opacity-70 ${
309+ mainTask . priority === 'high'
310+ ? 'bg-red-100'
311+ : mainTask . priority === 'low'
312+ ? 'bg-green-100'
313+ : 'bg-yellow-100'
314+ } `}
315+ >
316+ < span > { mainTask . name } </ span >
317+ < span className = 'opacity-70 text-xs font-medium italic' > { mainTask . priority } priority</ span >
318+ </ th >
319+ </ tr >
320+ </ thead >
321+ { ! ! subtasks ? (
322+ subtasks . map ( ( subtask ) => {
323+ if ( subtask . mainTaskName === mainTask . name ) {
324+ return (
325+ < tbody key = { subtask . description } >
326+ < tr >
327+ < td
328+ className = { `flex items-center justify-between gap-4 py-2 px-3 text-slate-600 border rounded-md border-purple-100 bg-opacity-60 ${
329+ mainTask . priority === 'high'
330+ ? 'bg-red-50'
331+ : mainTask . priority === 'low'
332+ ? 'bg-green-50'
333+ : 'bg-yellow-50'
334+ } `}
335+ >
336+ < Subtask description = { subtask . description } time = { subtask . time } />
337+ </ td >
338+ </ tr >
339+ </ tbody >
340+ ) ;
341+ }
342+ } )
343+ ) : (
344+ < div className = 'text-slate-600 text-center' > OpenAI didn't return any Subtasks. Try again.</ div >
345+ ) }
346+ </ >
347+ ) ;
348+ }
349+
305350function Subtask ( { description, time } : { description : string ; time : number } ) {
306351 const [ isDone , setIsDone ] = useState < boolean > ( false ) ;
307352
308353 const convertHrsToMinutes = ( time : number ) => {
309354 if ( time === 0 ) return 0 ;
310355 const hours = Math . floor ( time ) ;
311356 const minutes = Math . round ( ( time - hours ) * 60 ) ;
312- return `${ hours > 0 ? hours + 'hr' : '' } ${
313- minutes > 0 ? minutes + 'min' : ''
314- } `;
357+ return `${ hours > 0 ? hours + 'hr' : '' } ${ minutes > 0 ? minutes + 'min' : '' } ` ;
315358 } ;
316359
317360 const minutes = useMemo ( ( ) => convertHrsToMinutes ( time ) , [ time ] ) ;
@@ -325,17 +368,13 @@ function Subtask({ description, time }: { description: string; time: number }) {
325368 onChange = { ( e ) => setIsDone ( e . currentTarget . checked ) }
326369 />
327370 < span
328- className = { `text-slate-600 ${
371+ className = { `leading-tight justify-self-start w-full text-slate-600 ${
329372 isDone ? 'line-through text-slate-500 opacity-50' : ''
330373 } `}
331374 >
332375 { description }
333376 </ span >
334- < span
335- className = { `text-slate-600 ${
336- isDone ? 'line-through text-slate-500 opacity-50' : ''
337- } `}
338- >
377+ < span className = { `text-slate-600 text-right ${ isDone ? 'line-through text-slate-500 opacity-50' : '' } ` } >
339378 { minutes }
340379 </ span >
341380 </ >
0 commit comments