@learnpack/learnpack 5.0.69 → 5.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/lib/creatorDist/assets/{index-C7bLE5wU.js → index-Chx6V3zd.js} +9427 -9322
- package/lib/creatorDist/assets/{index-C_Rp91QE.css → index-Dqo9u2iR.css} +98 -31
- package/lib/creatorDist/index.html +2 -2
- package/lib/creatorDist/rigo-float.gif +0 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/creator/public/rigo-float.gif +0 -0
- package/src/creator/src/App.tsx +202 -163
- package/src/creator/src/assets/svgs.tsx +1 -1
- package/src/creator/src/components/LessonItem.tsx +10 -3
- package/src/creator/src/components/Message.tsx +1 -1
- package/src/creator/src/components/StepWizard.tsx +32 -18
- package/src/creator/src/components/syllabus/ContentIndex.tsx +44 -9
- package/src/creator/src/components/syllabus/Sidebar.tsx +123 -0
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +6 -124
- package/src/creator/src/index.css +2 -6
- package/src/creator/src/utils/store.ts +12 -3
- package/src/creatorDist/assets/{index-C7bLE5wU.js → index-Chx6V3zd.js} +9427 -9322
- package/src/creatorDist/assets/{index-C_Rp91QE.css → index-Dqo9u2iR.css} +98 -31
- package/src/creatorDist/index.html +2 -2
- package/src/creatorDist/rigo-float.gif +0 -0
package/src/creator/src/App.tsx
CHANGED
@@ -1,20 +1,18 @@
|
|
1
|
-
import { useEffect
|
2
|
-
import StepWizard
|
1
|
+
import { useEffect } from "react"
|
2
|
+
import StepWizard from "./components/StepWizard"
|
3
3
|
import SelectableCard from "./components/SelectableCard"
|
4
4
|
import Loader from "./components/Loader"
|
5
|
-
import { SVGS } from "./assets/svgs"
|
6
5
|
import { useNavigate } from "react-router"
|
7
6
|
import Login, { loginWithToken } from "./components/Login"
|
8
7
|
import { useShallow } from "zustand/react/shallow"
|
9
8
|
import useStore from "./utils/store"
|
10
9
|
import { interactiveCreation } from "./utils/rigo"
|
11
10
|
import { checkParams, parseLesson } from "./utils/lib"
|
11
|
+
import FileUploader from "./components/FileUploader"
|
12
12
|
|
13
|
-
//
|
14
|
-
|
15
|
-
|
16
|
-
-Introduction to Machine Learning: Explain What is machine learning and its aplications
|
17
|
-
-What is an AI Model: Explain what is an AI model an its applications`
|
13
|
+
// const exampleContentIndex = `-Introduction to AI: Explain what is AI and its applications
|
14
|
+
// -Introduction to Machine Learning: Explain What is machine learning and its aplications
|
15
|
+
// -What is an AI Model: Explain what is an AI model an its applications`
|
18
16
|
|
19
17
|
function App() {
|
20
18
|
const navigate = useNavigate()
|
@@ -29,157 +27,8 @@ function App() {
|
|
29
27
|
}))
|
30
28
|
)
|
31
29
|
|
32
|
-
// const [currentStep, setCurrentStep] = useState(0)
|
33
|
-
const [steps, setSteps] = useState<Array<Step>>([
|
34
|
-
{
|
35
|
-
title: "Provide a description for your tutorial",
|
36
|
-
slug: "description",
|
37
|
-
content: (
|
38
|
-
<textarea
|
39
|
-
placeholder="Describe your tutorial"
|
40
|
-
className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
|
41
|
-
defaultValue={formState.description}
|
42
|
-
onBlur={(e) => {
|
43
|
-
setFormState({
|
44
|
-
description: e.target.value,
|
45
|
-
currentStep: 1,
|
46
|
-
})
|
47
|
-
}}
|
48
|
-
/>
|
49
|
-
),
|
50
|
-
},
|
51
|
-
{
|
52
|
-
title:
|
53
|
-
"First you need to login with 4Geeks.com to use AI Generation tool for creators. ",
|
54
|
-
slug: "login",
|
55
|
-
content: (
|
56
|
-
<Login
|
57
|
-
onFinish={() => {
|
58
|
-
setFormState({
|
59
|
-
currentStep: 2,
|
60
|
-
})
|
61
|
-
}}
|
62
|
-
/>
|
63
|
-
),
|
64
|
-
},
|
65
|
-
{
|
66
|
-
title: "What is the estimated duration for this tutorial?",
|
67
|
-
slug: "duration",
|
68
|
-
content: (
|
69
|
-
<div className="flex flex-row gap-4">
|
70
|
-
<SelectableCard
|
71
|
-
title="Around 30 minutes"
|
72
|
-
// subtitle="This is a tutorial that will take 30 minutes to complete"
|
73
|
-
onClick={() => {
|
74
|
-
setFormState({
|
75
|
-
duration: 30,
|
76
|
-
currentStep: 3,
|
77
|
-
})
|
78
|
-
}}
|
79
|
-
selected={false}
|
80
|
-
/>
|
81
|
-
<SelectableCard
|
82
|
-
title="Around 1 hour"
|
83
|
-
// subtitle="This is a tutorial that will take 1 hour to complete"
|
84
|
-
onClick={() => {
|
85
|
-
setFormState({
|
86
|
-
duration: 60,
|
87
|
-
currentStep: 3,
|
88
|
-
})
|
89
|
-
}}
|
90
|
-
selected={false}
|
91
|
-
/>
|
92
|
-
<SelectableCard
|
93
|
-
title="Around 2 hours"
|
94
|
-
// subtitle="This is a tutorial that will take 2 hours to complete"
|
95
|
-
onClick={() => {
|
96
|
-
setFormState({
|
97
|
-
duration: 120,
|
98
|
-
currentStep: 3,
|
99
|
-
})
|
100
|
-
}}
|
101
|
-
selected={false}
|
102
|
-
/>
|
103
|
-
</div>
|
104
|
-
),
|
105
|
-
},
|
106
|
-
{
|
107
|
-
title: "What is the target audience for this tutorial?",
|
108
|
-
slug: "target-audience",
|
109
|
-
content: (
|
110
|
-
<div className="flex flex-row gap-4">
|
111
|
-
<textarea
|
112
|
-
placeholder="Describe the target audience for this tutorial"
|
113
|
-
className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
|
114
|
-
defaultValue={formState.targetAudience}
|
115
|
-
onBlur={(e) => {
|
116
|
-
setFormState({
|
117
|
-
targetAudience: e.target.value,
|
118
|
-
currentStep: 3,
|
119
|
-
})
|
120
|
-
}}
|
121
|
-
/>
|
122
|
-
</div>
|
123
|
-
),
|
124
|
-
},
|
125
|
-
{
|
126
|
-
title: "Do you have a content index for this tutorial?",
|
127
|
-
slug: "content-index",
|
128
|
-
content: (
|
129
|
-
<div className="flex flex-row gap-4 justify-center">
|
130
|
-
<SelectableCard
|
131
|
-
title="Yes"
|
132
|
-
onClick={() => {
|
133
|
-
setFormState({
|
134
|
-
hasContentIndex: true,
|
135
|
-
currentStep: 4,
|
136
|
-
})
|
137
|
-
setSteps([
|
138
|
-
...steps,
|
139
|
-
{
|
140
|
-
title:
|
141
|
-
"Write or paste your content index below, each topic should be defined on a new line, here is an example:",
|
142
|
-
slug: "content-index",
|
143
|
-
content: (
|
144
|
-
<textarea
|
145
|
-
placeholder="Provide a content index for this tutorial"
|
146
|
-
className="w-full h-24 border-2 border-gray-300 rounded-md p-2"
|
147
|
-
defaultValue={exampleContentIndex}
|
148
|
-
onBlur={(e) => {
|
149
|
-
setFormState({
|
150
|
-
contentIndex: e.target.value,
|
151
|
-
isCompleted: true,
|
152
|
-
})
|
153
|
-
}}
|
154
|
-
/>
|
155
|
-
),
|
156
|
-
},
|
157
|
-
])
|
158
|
-
}}
|
159
|
-
selected={false}
|
160
|
-
/>
|
161
|
-
<SelectableCard
|
162
|
-
title="No, help me create one"
|
163
|
-
onClick={() => {
|
164
|
-
setFormState({
|
165
|
-
hasContentIndex: false,
|
166
|
-
isCompleted: true,
|
167
|
-
})
|
168
|
-
|
169
|
-
// createTutorial()
|
170
|
-
// setCurrentStep(4)
|
171
|
-
}}
|
172
|
-
selected={false}
|
173
|
-
/>
|
174
|
-
</div>
|
175
|
-
),
|
176
|
-
},
|
177
|
-
])
|
178
|
-
|
179
30
|
useEffect(() => {
|
180
|
-
console.log(formState, "FORM STATE")
|
181
31
|
if (formState.isCompleted) {
|
182
|
-
// navigate("/syllabus")
|
183
32
|
handleCreateTutorial()
|
184
33
|
}
|
185
34
|
}, [formState])
|
@@ -192,14 +41,29 @@ function App() {
|
|
192
41
|
const { token } = checkParams()
|
193
42
|
if (token) {
|
194
43
|
const user = await loginWithToken(token)
|
195
|
-
console.log(user, "user at the beginning")
|
196
44
|
setAuth({
|
197
45
|
bcToken: token,
|
198
46
|
userId: user.id,
|
199
47
|
rigoToken: user.rigobot.key,
|
200
48
|
})
|
201
|
-
|
202
|
-
|
49
|
+
setFormState({
|
50
|
+
variables: [
|
51
|
+
"description",
|
52
|
+
"duration",
|
53
|
+
"targetAudience",
|
54
|
+
"hasContentIndex",
|
55
|
+
],
|
56
|
+
})
|
57
|
+
} else {
|
58
|
+
setFormState({
|
59
|
+
variables: [
|
60
|
+
"description",
|
61
|
+
"login",
|
62
|
+
"duration",
|
63
|
+
"targetAudience",
|
64
|
+
"hasContentIndex",
|
65
|
+
],
|
66
|
+
})
|
203
67
|
}
|
204
68
|
}
|
205
69
|
|
@@ -226,19 +90,194 @@ function App() {
|
|
226
90
|
hasContentIndex: false,
|
227
91
|
contentIndex: "",
|
228
92
|
isCompleted: false,
|
229
|
-
currentStep:
|
93
|
+
currentStep: "description",
|
230
94
|
})
|
231
95
|
}
|
232
96
|
|
97
|
+
const buildSteps = () => {
|
98
|
+
const steps = [
|
99
|
+
{
|
100
|
+
title: "Provide a description for your tutorial",
|
101
|
+
slug: "description",
|
102
|
+
isCompleted: false,
|
103
|
+
content: (
|
104
|
+
<textarea
|
105
|
+
placeholder="Describe your tutorial"
|
106
|
+
className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
|
107
|
+
value={formState.description}
|
108
|
+
onChange={(e) => {
|
109
|
+
setFormState({
|
110
|
+
description: e.target.value,
|
111
|
+
})
|
112
|
+
}}
|
113
|
+
/>
|
114
|
+
),
|
115
|
+
},
|
116
|
+
{
|
117
|
+
title:
|
118
|
+
"First you need to login with 4Geeks.com to use AI Generation tool for creators. ",
|
119
|
+
slug: "login",
|
120
|
+
isCompleted: false,
|
121
|
+
content: (
|
122
|
+
<Login
|
123
|
+
onFinish={() => {
|
124
|
+
setFormState({
|
125
|
+
currentStep: "duration",
|
126
|
+
})
|
127
|
+
}}
|
128
|
+
/>
|
129
|
+
),
|
130
|
+
},
|
131
|
+
{
|
132
|
+
title: "What is the estimated duration for this tutorial?",
|
133
|
+
slug: "duration",
|
134
|
+
isCompleted: false,
|
135
|
+
content: (
|
136
|
+
<div className="flex flex-col md:flex-row gap-2">
|
137
|
+
<SelectableCard
|
138
|
+
title="Around 30 minutes"
|
139
|
+
// subtitle="This is a tutorial that will take 30 minutes to complete"
|
140
|
+
onClick={() => {
|
141
|
+
setFormState({
|
142
|
+
duration: 30,
|
143
|
+
currentStep: "targetAudience",
|
144
|
+
})
|
145
|
+
}}
|
146
|
+
selected={formState.duration === 30}
|
147
|
+
/>
|
148
|
+
<SelectableCard
|
149
|
+
title="Around 1 hour"
|
150
|
+
// subtitle="This is a tutorial that will take 1 hour to complete"
|
151
|
+
onClick={() => {
|
152
|
+
setFormState({
|
153
|
+
duration: 60,
|
154
|
+
currentStep: "targetAudience",
|
155
|
+
})
|
156
|
+
}}
|
157
|
+
selected={formState.duration === 60}
|
158
|
+
/>
|
159
|
+
<SelectableCard
|
160
|
+
title="Around 2 hours"
|
161
|
+
// subtitle="This is a tutorial that will take 2 hours to complete"
|
162
|
+
onClick={() => {
|
163
|
+
setFormState({
|
164
|
+
duration: 120,
|
165
|
+
currentStep: "targetAudience",
|
166
|
+
})
|
167
|
+
}}
|
168
|
+
selected={formState.duration === 120}
|
169
|
+
/>
|
170
|
+
</div>
|
171
|
+
),
|
172
|
+
},
|
173
|
+
{
|
174
|
+
title: "What is the target audience for this tutorial?",
|
175
|
+
slug: "targetAudience",
|
176
|
+
isCompleted: false,
|
177
|
+
content: (
|
178
|
+
<div className="flex flex-row gap-4">
|
179
|
+
<textarea
|
180
|
+
placeholder="Describe the target audience for this tutorial"
|
181
|
+
className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
|
182
|
+
defaultValue={formState.targetAudience}
|
183
|
+
onBlur={(e) => {
|
184
|
+
setFormState({
|
185
|
+
targetAudience: e.target.value,
|
186
|
+
})
|
187
|
+
}}
|
188
|
+
/>
|
189
|
+
</div>
|
190
|
+
),
|
191
|
+
},
|
192
|
+
{
|
193
|
+
title: "Do you have a content index for this tutorial?",
|
194
|
+
slug: "hasContentIndex",
|
195
|
+
isCompleted: false,
|
196
|
+
content: (
|
197
|
+
<div className="flex flex-col md:flex-row gap-2 justify-center">
|
198
|
+
<SelectableCard
|
199
|
+
title="Yes"
|
200
|
+
onClick={() => {
|
201
|
+
setFormState({
|
202
|
+
hasContentIndex: true,
|
203
|
+
currentStep: "contentIndex",
|
204
|
+
variables: [...formState.variables, "contentIndex"],
|
205
|
+
})
|
206
|
+
}}
|
207
|
+
selected={false}
|
208
|
+
/>
|
209
|
+
<SelectableCard
|
210
|
+
title="No, help me create one"
|
211
|
+
onClick={() => {
|
212
|
+
setFormState({
|
213
|
+
hasContentIndex: false,
|
214
|
+
isCompleted: true,
|
215
|
+
})
|
216
|
+
}}
|
217
|
+
selected={false}
|
218
|
+
/>
|
219
|
+
</div>
|
220
|
+
),
|
221
|
+
},
|
222
|
+
{
|
223
|
+
title:
|
224
|
+
"Write or paste your content index below, each topic should be defined on a new line, here is an example:",
|
225
|
+
slug: "contentIndex",
|
226
|
+
isCompleted: false,
|
227
|
+
content: (
|
228
|
+
<>
|
229
|
+
<textarea
|
230
|
+
placeholder="Provide a content index for this tutorial"
|
231
|
+
className="w-full h-40 border-2 border-gray-300 rounded-md p-2"
|
232
|
+
value={formState.contentIndex}
|
233
|
+
onChange={(e) => {
|
234
|
+
setFormState({
|
235
|
+
contentIndex: e.target.value,
|
236
|
+
// isCompleted: true,
|
237
|
+
})
|
238
|
+
}}
|
239
|
+
/>
|
240
|
+
<FileUploader
|
241
|
+
onResult={(files) => {
|
242
|
+
// toast.success("File uploaded successfully")
|
243
|
+
let allFilesText = ``
|
244
|
+
files.forEach((file) => {
|
245
|
+
allFilesText += `<FILE NAME="${file.name}">${file.text}</FILE>\n`
|
246
|
+
})
|
247
|
+
setFormState({
|
248
|
+
contentIndex: allFilesText,
|
249
|
+
})
|
250
|
+
}}
|
251
|
+
/>
|
252
|
+
</>
|
253
|
+
),
|
254
|
+
},
|
255
|
+
]
|
256
|
+
|
257
|
+
return steps.filter(
|
258
|
+
(step) =>
|
259
|
+
formState.variables.includes(step.slug) ||
|
260
|
+
step.slug === formState.currentStep
|
261
|
+
)
|
262
|
+
}
|
233
263
|
return (
|
234
264
|
<>
|
235
265
|
{formState.isCompleted ? (
|
236
266
|
<Loader
|
237
267
|
text="Learnpack is setting up your tutorial. It may take a moment..."
|
238
|
-
icon={
|
268
|
+
icon={<img src={"rigo-float.gif"} alt="rigo" className="w-20 h-20" />}
|
239
269
|
/>
|
240
270
|
) : (
|
241
|
-
<StepWizard
|
271
|
+
<StepWizard
|
272
|
+
formState={formState}
|
273
|
+
steps={buildSteps()}
|
274
|
+
setFormState={setFormState}
|
275
|
+
onFinish={() => {
|
276
|
+
setFormState({
|
277
|
+
isCompleted: true,
|
278
|
+
})
|
279
|
+
}}
|
280
|
+
/>
|
242
281
|
)}
|
243
282
|
</>
|
244
283
|
)
|
@@ -11,15 +11,22 @@ export interface Lesson {
|
|
11
11
|
|
12
12
|
interface LessonItemProps {
|
13
13
|
lesson: Lesson
|
14
|
-
index: string
|
15
14
|
isNew: boolean
|
16
15
|
onChange: (id: string, newTitle: string) => void
|
17
16
|
onRemove: (id: string) => void
|
18
17
|
}
|
19
18
|
|
19
|
+
function cleanFloatString(input: string): string {
|
20
|
+
const num = parseFloat(input)
|
21
|
+
const isInteger = Number.isInteger(num)
|
22
|
+
|
23
|
+
return isInteger
|
24
|
+
? Math.floor(num).toString().padStart(2, "0")
|
25
|
+
: num.toString()
|
26
|
+
}
|
27
|
+
|
20
28
|
export const LessonItem: React.FC<LessonItemProps> = ({
|
21
29
|
lesson,
|
22
|
-
index,
|
23
30
|
onChange,
|
24
31
|
onRemove,
|
25
32
|
isNew,
|
@@ -32,7 +39,7 @@ export const LessonItem: React.FC<LessonItemProps> = ({
|
|
32
39
|
isNew ? "bg-yellow-50" : ""
|
33
40
|
}`}
|
34
41
|
>
|
35
|
-
<span className="index-circle">{
|
42
|
+
<span className="index-circle">{cleanFloatString(lesson.id)}</span>
|
36
43
|
<span className="text-gray-500 text-sm">
|
37
44
|
{lesson.type[0] + lesson.type.slice(1).toLowerCase()} ●
|
38
45
|
</span>
|
@@ -12,7 +12,7 @@ export const Message: React.FC<TMessage> = ({ type, content }) => {
|
|
12
12
|
|
13
13
|
const isLoading = isAI && !content
|
14
14
|
return isLoading ? (
|
15
|
-
<RigoLoader text="Thinking..." svg={
|
15
|
+
<RigoLoader text="Thinking..." svg={<img src="rigo-float.gif" />} />
|
16
16
|
) : (
|
17
17
|
<div
|
18
18
|
className={`flex items-start space-x-2 p-3 rounded-md border ${
|
@@ -1,45 +1,60 @@
|
|
1
|
-
import React
|
1
|
+
import React from "react"
|
2
2
|
|
3
3
|
export type Step = {
|
4
4
|
title: string
|
5
5
|
subtitle?: string
|
6
6
|
content: React.ReactNode
|
7
7
|
slug: string
|
8
|
+
isCompleted: boolean
|
8
9
|
}
|
9
10
|
|
10
11
|
type Props = {
|
11
12
|
steps: Step[]
|
12
|
-
|
13
|
+
formState: any
|
14
|
+
setFormState: (formState: any) => void
|
15
|
+
onFinish: () => void
|
13
16
|
}
|
14
17
|
|
15
|
-
const StepWizard: React.FC<Props> = ({
|
16
|
-
|
18
|
+
const StepWizard: React.FC<Props> = ({
|
19
|
+
steps,
|
20
|
+
formState,
|
21
|
+
setFormState,
|
22
|
+
onFinish,
|
23
|
+
}) => {
|
24
|
+
const currentStep = formState.currentStep
|
25
|
+
const index = steps.findIndex((step) => step.slug === currentStep)
|
17
26
|
const totalSteps = steps.length
|
18
27
|
|
19
28
|
const goNext = () => {
|
20
|
-
|
29
|
+
const index = steps.findIndex((step) => step.slug === currentStep)
|
30
|
+
if (index < totalSteps - 1)
|
31
|
+
setFormState({
|
32
|
+
...formState,
|
33
|
+
currentStep: steps[index + 1].slug,
|
34
|
+
})
|
21
35
|
}
|
22
36
|
|
23
37
|
const goBack = () => {
|
24
|
-
|
38
|
+
const index = steps.findIndex((step) => step.slug === currentStep)
|
39
|
+
if (index > 0)
|
40
|
+
setFormState({ ...formState, currentStep: steps[index - 1].slug })
|
25
41
|
}
|
26
42
|
|
27
|
-
useEffect(() => {
|
28
|
-
setCurrentStep(initialStep)
|
29
|
-
}, [initialStep])
|
30
|
-
|
31
43
|
return (
|
32
44
|
<div className="min-h-screen flex flex-col items-center justify-center text-center px-4">
|
33
45
|
<div className=" rounded-xl p-8 w-full max-w-xl">
|
34
46
|
<h5 className="text-sm text-blue-400 uppercase mb-2">
|
35
|
-
{steps
|
47
|
+
{steps.find((step) => step.slug === currentStep)?.subtitle ||
|
48
|
+
"Setting up your tutorial"}
|
36
49
|
</h5>
|
37
50
|
<h2 className="text-lg font-medium mb-6">
|
38
|
-
<span className="text-blue-600 mr-1">{
|
39
|
-
{steps
|
51
|
+
<span className="text-blue-600 mr-1">{index + 1}.</span>
|
52
|
+
{steps.find((step) => step.slug === currentStep)?.title}
|
40
53
|
</h2>
|
41
54
|
|
42
|
-
<div className="mb-6">
|
55
|
+
<div className="mb-6">
|
56
|
+
{steps.find((step) => step.slug === currentStep)?.content}
|
57
|
+
</div>
|
43
58
|
|
44
59
|
{/* Dot Indicators */}
|
45
60
|
<div className="flex justify-center mb-4 space-x-2">
|
@@ -47,7 +62,7 @@ const StepWizard: React.FC<Props> = ({ steps, initialStep = 0 }) => {
|
|
47
62
|
<span
|
48
63
|
key={i}
|
49
64
|
className={`h-2 w-2 rounded-full ${
|
50
|
-
i ===
|
65
|
+
i === index ? "bg-blue-600" : "bg-gray-300"
|
51
66
|
}`}
|
52
67
|
/>
|
53
68
|
))}
|
@@ -57,14 +72,13 @@ const StepWizard: React.FC<Props> = ({ steps, initialStep = 0 }) => {
|
|
57
72
|
<div className="flex justify-between">
|
58
73
|
<button
|
59
74
|
onClick={goBack}
|
60
|
-
disabled={
|
75
|
+
disabled={index === 0}
|
61
76
|
className="text-sm text-gray-500 hover:text-gray-900 disabled:opacity-40"
|
62
77
|
>
|
63
78
|
⬅ Back
|
64
79
|
</button>
|
65
80
|
<button
|
66
|
-
onClick={goNext}
|
67
|
-
disabled={currentStep === totalSteps - 1}
|
81
|
+
onClick={index === totalSteps - 1 ? onFinish : goNext}
|
68
82
|
className="text-sm text-blue-600 hover:text-blue-800 disabled:opacity-40"
|
69
83
|
>
|
70
84
|
Next ➡
|
@@ -1,7 +1,38 @@
|
|
1
1
|
import { useEffect, useState, useRef } from "react"
|
2
|
-
import useStore from "../../utils/store"
|
2
|
+
import useStore, { Syllabus } from "../../utils/store"
|
3
3
|
import { Lesson, LessonItem } from "../LessonItem"
|
4
4
|
import { SVGS } from "../../assets/svgs"
|
5
|
+
import { TMessage } from "../Message"
|
6
|
+
|
7
|
+
const ContentIndexHeader = ({
|
8
|
+
messages,
|
9
|
+
syllabus,
|
10
|
+
}: {
|
11
|
+
messages: TMessage[]
|
12
|
+
syllabus: Syllabus
|
13
|
+
}) => {
|
14
|
+
return (
|
15
|
+
<div>
|
16
|
+
<h2 className="text-lg font-semibold">
|
17
|
+
{messages.filter((m) => m.type === "assistant" && m.content.length > 0)
|
18
|
+
.length === 0
|
19
|
+
? "I've created a detailed structure for your course."
|
20
|
+
: "I've updated the structure based on your feedback."}
|
21
|
+
</h2>
|
22
|
+
<p className="text-sm text-gray-600">
|
23
|
+
{messages.filter((m) => m.type === "assistant" && m.content.length > 0)
|
24
|
+
.length === 0
|
25
|
+
? `It includes a mix of reading, coding exercises, and quizzes. Give
|
26
|
+
it a look and let me know if it aligns with your expectations or if
|
27
|
+
there are any changes you'd like to make.`
|
28
|
+
: "Based on your input, here is the new syllabus, updates are highlighted in yellow"}
|
29
|
+
</p>
|
30
|
+
<h3 className="text-sm text-gray-600 mt-2 font-bold">
|
31
|
+
{syllabus.courseInfo.title}
|
32
|
+
</h3>
|
33
|
+
</div>
|
34
|
+
)
|
35
|
+
}
|
5
36
|
|
6
37
|
export const GenerateButton = ({
|
7
38
|
handleSubmit,
|
@@ -25,19 +56,23 @@ export const GenerateButton = ({
|
|
25
56
|
export const ContentIndex = ({
|
26
57
|
prevLessons,
|
27
58
|
handleSubmit,
|
59
|
+
messages,
|
28
60
|
}: {
|
29
61
|
prevLessons?: Lesson[]
|
30
62
|
handleSubmit: () => void
|
63
|
+
messages: TMessage[]
|
31
64
|
}) => {
|
32
65
|
const syllabus = useStore((state) => state.syllabus)
|
33
66
|
const setSyllabus = useStore((state) => state.setSyllabus)
|
34
67
|
const containerRef = useRef<HTMLDivElement>(null)
|
35
68
|
const [showScrollHint, setShowScrollHint] = useState(false)
|
36
69
|
|
37
|
-
const handleRemove = (
|
70
|
+
const handleRemove = (lesson: Lesson) => {
|
38
71
|
setSyllabus({
|
39
72
|
...syllabus,
|
40
|
-
lessons: syllabus.lessons.filter(
|
73
|
+
lessons: syllabus.lessons.filter(
|
74
|
+
(l) => l.id !== lesson.id && l.title !== lesson.title
|
75
|
+
),
|
41
76
|
})
|
42
77
|
}
|
43
78
|
|
@@ -52,7 +87,7 @@ export const ContentIndex = ({
|
|
52
87
|
|
53
88
|
const addLessonAfter = (index: number, id: string) => {
|
54
89
|
const newLesson: Lesson = {
|
55
|
-
id: (parseFloat(id) + 0.1).
|
90
|
+
id: (parseFloat(id) + 0.1).toFixed(1),
|
56
91
|
title: "Hello World",
|
57
92
|
type: "READ",
|
58
93
|
duration: 2,
|
@@ -100,19 +135,19 @@ export const ContentIndex = ({
|
|
100
135
|
}
|
101
136
|
|
102
137
|
return (
|
103
|
-
<div className="relative">
|
138
|
+
<div className="relative ">
|
139
|
+
<ContentIndexHeader messages={messages} syllabus={syllabus} />
|
104
140
|
<div
|
105
141
|
ref={containerRef}
|
106
|
-
className=" space-y-3 overflow-y-auto max-h-[70vh] pr-2 scrollbar-hide relative"
|
142
|
+
className=" space-y-3 overflow-y-auto max-h-[70vh] pr-2 scrollbar-hide relative pb-5"
|
107
143
|
>
|
108
144
|
{syllabus.lessons.map((lesson, index) => (
|
109
145
|
<div key={lesson.id}>
|
110
146
|
<LessonItem
|
111
|
-
key={lesson.id}
|
112
|
-
index={lesson.id}
|
147
|
+
key={lesson.id + index + lesson.title}
|
113
148
|
lesson={lesson}
|
114
149
|
onChange={handleChange}
|
115
|
-
onRemove={handleRemove}
|
150
|
+
onRemove={() => handleRemove(lesson)}
|
116
151
|
isNew={Boolean(
|
117
152
|
prevLessons &&
|
118
153
|
prevLessons.length > 0 &&
|