Skip to content

Commit 29996a2

Browse files
committed
update OpenAI demo app
1 parent 99fd761 commit 29996a2

File tree

5 files changed

+269
-247
lines changed

5 files changed

+269
-247
lines changed

app/main.wasp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ app SaaSTemplate {
8282
("headlessui", "^0.0.0"),
8383
("@faker-js/faker", "8.3.1"),
8484
("@google-analytics/data", "4.1.0"),
85-
("openai", "^4.24.1"),
85+
("openai", "^4.28.0"),
8686
("prettier", "3.1.1"),
8787
("prettier-plugin-tailwindcss", "0.5.11"),
8888
("zod", "3.22.4"),

app/src/client/app/DemoAppPage.tsx

Lines changed: 149 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@ import getAllTasksByUser from '@wasp/queries/getAllTasksByUser';
88
import { Task } from '@wasp/entities';
99
import { CgSpinner } from 'react-icons/cg';
1010
import { TiDelete } from 'react-icons/ti';
11+
import { type GeneratedSchedule } from '../../shared/types';
12+
import { MainTask, Subtask } from '@wasp/shared/types';
1113

1214
export 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() {
3639
type TodoProps = Pick<Task, 'id' | 'isDone' | 'description' | 'time'>;
3740

3841
function 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+
305350
function 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

Comments
 (0)