@learnpack/learnpack 5.0.234 → 5.0.238
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/lib/commands/serve.d.ts +1 -2
- package/lib/commands/serve.js +8 -109
- package/lib/creatorDist/assets/{index-CFK5bQP2.js → index-x_kA-1DY.js} +8168 -8115
- package/lib/creatorDist/index.html +1 -1
- package/oclif.manifest.json +1 -0
- package/package.json +2 -2
- package/src/commands/serve.ts +8 -165
- package/src/creator/src/App.tsx +67 -62
- package/src/creator/src/components/LessonItem.tsx +9 -3
- package/src/creator/src/components/NotificationListener.tsx +32 -0
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +42 -44
- package/src/creator/src/utils/creatorUtils.ts +2 -2
- package/src/creator/src/utils/rigo.ts +9 -2
- package/src/creator/src/utils/store.ts +13 -2
- package/src/creatorDist/assets/{index-CFK5bQP2.js → index-x_kA-1DY.js} +8168 -8115
- package/src/creatorDist/index.html +1 -1
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +1 -1
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/configBuilder.ts +1 -2
@@ -10,7 +10,7 @@
|
|
10
10
|
/>
|
11
11
|
|
12
12
|
<title>Learnpack Creator: Craft tutorials in seconds!</title>
|
13
|
-
<script type="module" crossorigin src="/creator/assets/index-
|
13
|
+
<script type="module" crossorigin src="/creator/assets/index-x_kA-1DY.js"></script>
|
14
14
|
<link rel="stylesheet" crossorigin href="/creator/assets/index-DmpsXknz.css">
|
15
15
|
</head>
|
16
16
|
<body>
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":"5.0.236","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false}},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"serve":{"id":"serve","description":"Runs a small server to build tutorials","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@learnpack/learnpack",
|
3
3
|
"description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
|
4
|
-
"version": "5.0.
|
4
|
+
"version": "5.0.238",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
@@ -157,7 +157,7 @@
|
|
157
157
|
"copy-assets": "npx cpy src/creatorDist/**/* lib/creatorDist --parents --verbose",
|
158
158
|
"tsc": "tsc -b",
|
159
159
|
"postpack": "rm -f oclif.manifest.json && eslint . --ext .ts --config .eslintrc",
|
160
|
-
"prepack": "rm -rf lib && tsc -b && npm run copy-assets
|
160
|
+
"prepack": "rm -rf lib && tsc -b && npm run copy-assets",
|
161
161
|
"pre": "node ./test/precommit/index.ts",
|
162
162
|
"test": "NODE_ENV=test nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
|
163
163
|
"version": "oclif-dev readme && git add README.md",
|
package/src/commands/serve.ts
CHANGED
@@ -142,8 +142,10 @@ export const processImage = async (
|
|
142
142
|
rigoToken: string
|
143
143
|
) => {
|
144
144
|
try {
|
145
|
+
// TODO: MAKE THIS ASYNC
|
145
146
|
const filename = getFilenameFromUrl(url)
|
146
147
|
|
148
|
+
console.log("🖼️ IMAGE FILENAME", filename)
|
147
149
|
const imagePath = `${tutorialDir}/.learn/assets/${filename}`
|
148
150
|
|
149
151
|
console.log("🖼️ Generating image", imagePath)
|
@@ -249,131 +251,6 @@ async function startExerciseGeneration(
|
|
249
251
|
purposeSlug,
|
250
252
|
webhookUrl
|
251
253
|
)
|
252
|
-
// console.log("res processing in background", res)
|
253
|
-
}
|
254
|
-
|
255
|
-
export async function processExercise(
|
256
|
-
bucket: Bucket,
|
257
|
-
rigoToken: string,
|
258
|
-
steps: Lesson[],
|
259
|
-
packageContext: FormState,
|
260
|
-
exercise: Lesson,
|
261
|
-
tutorialDir: string,
|
262
|
-
courseSlug: string,
|
263
|
-
purposeSlug: string,
|
264
|
-
lastLesson = ""
|
265
|
-
): Promise<string> {
|
266
|
-
const exercisesDir = `${tutorialDir}/exercises`
|
267
|
-
// const tid = toast.loading("Generating lesson...")
|
268
|
-
const exSlug = slugify(exercise.id + "-" + exercise.title)
|
269
|
-
console.log("exSlug", exSlug)
|
270
|
-
|
271
|
-
const readmeFilename = `README.${
|
272
|
-
packageContext.language && packageContext.language !== "en" ?
|
273
|
-
`${packageContext.language}.` :
|
274
|
-
""
|
275
|
-
}md`
|
276
|
-
const targetDir = `${exercisesDir}/${exSlug}`
|
277
|
-
|
278
|
-
console.log("✍🏻 Generating lesson", exercise.id, exercise.title)
|
279
|
-
|
280
|
-
const readme = await readmeCreator(
|
281
|
-
rigoToken,
|
282
|
-
{
|
283
|
-
title: `${exercise.id} - ${exercise.title}`,
|
284
|
-
output_lang: packageContext.language || "en",
|
285
|
-
list_of_exercises: JSON.stringify(
|
286
|
-
steps.map(step => step.id + "-" + step.title)
|
287
|
-
),
|
288
|
-
tutorial_description: JSON.stringify(cleanFormState(packageContext)),
|
289
|
-
lesson_description: exercise.description,
|
290
|
-
kind: exercise.type.toLowerCase(),
|
291
|
-
last_lesson: lastLesson,
|
292
|
-
},
|
293
|
-
purposeSlug
|
294
|
-
)
|
295
|
-
|
296
|
-
const duration = exercise.duration
|
297
|
-
// let attempts = 0
|
298
|
-
const readability = checkReadability(
|
299
|
-
readme.parsed.content,
|
300
|
-
PARAMS.max_words,
|
301
|
-
duration || 3
|
302
|
-
)
|
303
|
-
|
304
|
-
emitToCourse(courseSlug, "course-creation", {
|
305
|
-
lesson: exSlug,
|
306
|
-
status: "generating",
|
307
|
-
log: `🔄 The lesson ${exercise.id} - ${exercise.title} has a readability score of ${readability.fkglResult.fkgl}`,
|
308
|
-
})
|
309
|
-
// while (
|
310
|
-
// readability.fkglResult.fkgl > PARAMS.max_fkgl &&
|
311
|
-
// attempts < PARAMS.max_rewrite_attempts
|
312
|
-
// ) {
|
313
|
-
|
314
|
-
// // eslint-disable-next-line
|
315
|
-
// const reducedReadme = await makeReadmeReadable(
|
316
|
-
// rigoToken,
|
317
|
-
// {
|
318
|
-
// lesson: readability.body,
|
319
|
-
// number_of_words: readability.minutes.toString(),
|
320
|
-
// expected_number_words: PARAMS.max_words.toString(),
|
321
|
-
// fkgl_results: JSON.stringify(readability.fkglResult),
|
322
|
-
// expected_grade_level: PARAMS.expected_grade_level,
|
323
|
-
// },
|
324
|
-
// purposeSlug
|
325
|
-
// )
|
326
|
-
|
327
|
-
// if (!reducedReadme) break
|
328
|
-
|
329
|
-
// readability = checkReadability(
|
330
|
-
// reducedReadme.parsed.content,
|
331
|
-
// PARAMS.max_words,
|
332
|
-
// duration || 3
|
333
|
-
// )
|
334
|
-
|
335
|
-
// attempts++
|
336
|
-
// }
|
337
|
-
|
338
|
-
await uploadFileToBucket(
|
339
|
-
bucket,
|
340
|
-
readability.newMarkdown,
|
341
|
-
`${targetDir}/${readmeFilename}`
|
342
|
-
)
|
343
|
-
|
344
|
-
if (
|
345
|
-
exercise.type.toLowerCase() === "code" &&
|
346
|
-
readme.parsed.codefile_content
|
347
|
-
) {
|
348
|
-
console.log("🔍 Creating code file for", exercise.title)
|
349
|
-
|
350
|
-
await uploadFileToBucket(
|
351
|
-
bucket,
|
352
|
-
readme.parsed.codefile_content,
|
353
|
-
`${targetDir}/${readme.parsed.codefile_name.toLowerCase().trim()}`
|
354
|
-
)
|
355
|
-
}
|
356
|
-
|
357
|
-
const imagesArray: any[] = extractImagesFromMarkdown(readability.newMarkdown)
|
358
|
-
|
359
|
-
if (imagesArray.length > 0) {
|
360
|
-
emitToCourse(courseSlug, "course-creation", {
|
361
|
-
lesson: exSlug,
|
362
|
-
status: "done",
|
363
|
-
log: `🔄 Generating images for ${exercise.title}`,
|
364
|
-
})
|
365
|
-
for (const image of imagesArray) {
|
366
|
-
// eslint-disable-next-line no-await-in-loop
|
367
|
-
await processImage(bucket, tutorialDir, image.url, image.alt, rigoToken)
|
368
|
-
}
|
369
|
-
}
|
370
|
-
|
371
|
-
emitToCourse(courseSlug, "course-creation", {
|
372
|
-
lesson: exSlug,
|
373
|
-
status: "done",
|
374
|
-
log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
|
375
|
-
})
|
376
|
-
return readability.newMarkdown
|
377
254
|
}
|
378
255
|
|
379
256
|
const fixPreviewUrl = (slug: string, previewUrl: string) => {
|
@@ -719,6 +596,11 @@ export default class ServeCommand extends SessionCommand {
|
|
719
596
|
readme.parsed.codefile_content,
|
720
597
|
`${targetDir}/${readme.parsed.codefile_name.toLowerCase().trim()}`
|
721
598
|
)
|
599
|
+
emitToCourse(courseSlug, "course-creation", {
|
600
|
+
lesson: exSlug,
|
601
|
+
status: "done",
|
602
|
+
log: `✅ Code file created for ${exercise.title}`,
|
603
|
+
})
|
722
604
|
}
|
723
605
|
|
724
606
|
if (nextExercise) {
|
@@ -740,10 +622,6 @@ export default class ServeCommand extends SessionCommand {
|
|
740
622
|
)
|
741
623
|
|
742
624
|
if (imagesArray.length > 0) {
|
743
|
-
console.log(
|
744
|
-
"This course requires images and I don't have the token :)"
|
745
|
-
)
|
746
|
-
|
747
625
|
emitToCourse(courseSlug, "course-creation", {
|
748
626
|
lesson: exSlug,
|
749
627
|
status: "pending",
|
@@ -1271,7 +1149,7 @@ export default class ServeCommand extends SessionCommand {
|
|
1271
1149
|
|
1272
1150
|
const lastResult = "Nothing"
|
1273
1151
|
|
1274
|
-
startExerciseGeneration(
|
1152
|
+
await startExerciseGeneration(
|
1275
1153
|
bucket,
|
1276
1154
|
rigoToken,
|
1277
1155
|
syllabus.lessons,
|
@@ -1289,41 +1167,6 @@ export default class ServeCommand extends SessionCommand {
|
|
1289
1167
|
})
|
1290
1168
|
})
|
1291
1169
|
|
1292
|
-
// app.post(
|
1293
|
-
// "/check-latex/:courseSlug/:exerciseSlug/:lang",
|
1294
|
-
// async (req, res) => {
|
1295
|
-
// const { courseSlug, exerciseSlug, lang } = req.params
|
1296
|
-
|
1297
|
-
// const rigoToken = req.header("x-rigo-token")
|
1298
|
-
|
1299
|
-
// if (!rigoToken) {
|
1300
|
-
// return res.status(400).json({ error: "Missing tokens" })
|
1301
|
-
// }
|
1302
|
-
|
1303
|
-
// const exercise = await bucket.file(
|
1304
|
-
// `courses/${courseSlug}/exercises/${exerciseSlug}/README.${lang}.md`
|
1305
|
-
// )
|
1306
|
-
// const [content] = await exercise.download()
|
1307
|
-
// const headers = {
|
1308
|
-
// Authorization: `Token ${rigoToken}`,
|
1309
|
-
// }
|
1310
|
-
// const response = await axios.get(
|
1311
|
-
// `${RIGOBOT_HOST}/v1/prompting/completion/60865/`,
|
1312
|
-
// {
|
1313
|
-
// headers,
|
1314
|
-
// }
|
1315
|
-
// )
|
1316
|
-
|
1317
|
-
// console.log(response.data.parsed.content, "RESPONSE from Rigobot")
|
1318
|
-
|
1319
|
-
// res.json({
|
1320
|
-
// message: "Exercise downloaded",
|
1321
|
-
// completion: response.data,
|
1322
|
-
// exercise: content.toString(),
|
1323
|
-
// })
|
1324
|
-
// }
|
1325
|
-
// )
|
1326
|
-
|
1327
1170
|
app.get("/courses/:courseSlug/syllabus", async (req, res) => {
|
1328
1171
|
try {
|
1329
1172
|
console.log("GET /courses/:courseSlug/syllabus")
|
package/src/creator/src/App.tsx
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { useEffect } from "react"
|
1
|
+
import { useEffect, useState } from "react"
|
2
2
|
import StepWizard from "./components/StepWizard"
|
3
3
|
import SelectableCard from "./components/SelectableCard"
|
4
4
|
import Loader from "./components/Loader"
|
@@ -25,6 +25,7 @@ import TurnstileChallenge from "./components/TurnstileChallenge"
|
|
25
25
|
import ResumeCourseModal from "./components/ResumeCourseModal"
|
26
26
|
import { possiblePurposes, PurposeSelector } from "./components/PurposeSelector"
|
27
27
|
import { useTranslation } from "react-i18next"
|
28
|
+
import NotificationListener from "./components/NotificationListener"
|
28
29
|
|
29
30
|
function App() {
|
30
31
|
const navigate = useNavigate()
|
@@ -64,6 +65,8 @@ function App() {
|
|
64
65
|
}))
|
65
66
|
)
|
66
67
|
|
68
|
+
const [notificationId, setNotificationId] = useState<string>("")
|
69
|
+
|
67
70
|
useEffect(() => {
|
68
71
|
if (formState.isCompleted) {
|
69
72
|
handleCreateTutorial()
|
@@ -111,7 +114,6 @@ function App() {
|
|
111
114
|
cleanAll()
|
112
115
|
}
|
113
116
|
|
114
|
-
|
115
117
|
if (description) {
|
116
118
|
console.log("description", description)
|
117
119
|
setFormState({
|
@@ -187,51 +189,9 @@ function App() {
|
|
187
189
|
formState.purpose || "learnpack-lesson-writer",
|
188
190
|
auth.rigoToken && isAuthenticated ? false : true
|
189
191
|
)
|
190
|
-
|
191
|
-
return parseLesson(lesson, [])
|
192
|
-
})
|
193
|
-
|
194
|
-
push({
|
195
|
-
lessons,
|
196
|
-
courseInfo: {
|
197
|
-
...formState,
|
198
|
-
title: fixTitleLength(res.parsed.title),
|
199
|
-
description: res.parsed.description,
|
200
|
-
language: res.parsed.languageCode || formState.language || "en",
|
201
|
-
technologies:
|
202
|
-
res.parsed.technologies.length > 0
|
203
|
-
? res.parsed.technologies
|
204
|
-
: ["education", "quizzes"],
|
205
|
-
},
|
206
|
-
})
|
192
|
+
console.log("RES", res)
|
207
193
|
|
208
|
-
|
209
|
-
i18n.changeLanguage(res.parsed.languageCode)
|
210
|
-
}
|
211
|
-
|
212
|
-
navigate("/creator/syllabus")
|
213
|
-
setFormState({
|
214
|
-
isCompleted: false,
|
215
|
-
currentStep: "description",
|
216
|
-
})
|
217
|
-
setMessages([
|
218
|
-
{
|
219
|
-
type: "user",
|
220
|
-
content: formState.description,
|
221
|
-
},
|
222
|
-
{
|
223
|
-
type: "assistant",
|
224
|
-
content: res.parsed.aiMessage,
|
225
|
-
},
|
226
|
-
{
|
227
|
-
type: "assistant",
|
228
|
-
content: t("contentIndex.okMessage"),
|
229
|
-
},
|
230
|
-
{
|
231
|
-
type: "assistant",
|
232
|
-
content: t("contentIndex.instructionsMessage"),
|
233
|
-
},
|
234
|
-
])
|
194
|
+
setNotificationId(res.notificationId)
|
235
195
|
} catch (error) {
|
236
196
|
console.error(error, "ERROR CREATING TUTORIAL")
|
237
197
|
toast.error("Something went wrong. Please try again.")
|
@@ -432,24 +392,69 @@ function App() {
|
|
432
392
|
<>
|
433
393
|
<ParamsChecker />
|
434
394
|
{formState.isCompleted && history.length === 0 ? (
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
395
|
+
<>
|
396
|
+
<Loader
|
397
|
+
text={t("loader.text")}
|
398
|
+
icon={<img src={RIGO_FLOAT_GIF} alt="rigo" className="w-20 h-20" />}
|
399
|
+
/>
|
400
|
+
{notificationId && (
|
401
|
+
<NotificationListener
|
402
|
+
onNotification={(res) => {
|
403
|
+
console.log("Async response", res)
|
404
|
+
toast.success("Initial course created successfully")
|
405
|
+
const lessons = res.parsed.listOfSteps.map((lesson: any) => {
|
406
|
+
return parseLesson(lesson, [])
|
407
|
+
})
|
408
|
+
|
409
|
+
push({
|
410
|
+
lessons,
|
411
|
+
courseInfo: {
|
412
|
+
...formState,
|
413
|
+
title: fixTitleLength(res.parsed.title),
|
414
|
+
description: res.parsed.description,
|
415
|
+
language:
|
416
|
+
res.parsed.languageCode || formState.language || "en",
|
417
|
+
technologies:
|
418
|
+
res.parsed.technologies.length > 0
|
419
|
+
? res.parsed.technologies
|
420
|
+
: ["education", "quizzes"],
|
421
|
+
},
|
422
|
+
})
|
423
|
+
|
424
|
+
if (res.parsed.languageCode) {
|
425
|
+
i18n.changeLanguage(res.parsed.languageCode)
|
426
|
+
}
|
427
|
+
|
428
|
+
setMessages([
|
429
|
+
{
|
430
|
+
type: "user",
|
431
|
+
content: formState.description,
|
432
|
+
},
|
433
|
+
{
|
434
|
+
type: "assistant",
|
435
|
+
content: res.parsed.aiMessage,
|
436
|
+
},
|
437
|
+
{
|
438
|
+
type: "assistant",
|
439
|
+
content: t("contentIndex.okMessage"),
|
440
|
+
},
|
441
|
+
{
|
442
|
+
type: "assistant",
|
443
|
+
content: t("contentIndex.instructionsMessage"),
|
444
|
+
},
|
445
|
+
])
|
446
|
+
navigate("/creator/syllabus")
|
447
|
+
setFormState({
|
448
|
+
isCompleted: false,
|
449
|
+
currentStep: "description",
|
450
|
+
})
|
451
|
+
}}
|
452
|
+
notificationId={notificationId}
|
453
|
+
/>
|
454
|
+
)}
|
455
|
+
</>
|
439
456
|
) : (
|
440
457
|
<>
|
441
|
-
{/* {formState.language && (
|
442
|
-
<div className="flex flex-col items-center justify-center">
|
443
|
-
<p>
|
444
|
-
<span className="font-bold">Language:</span>{" "}
|
445
|
-
{formState.language}
|
446
|
-
</p>
|
447
|
-
<p>
|
448
|
-
<span className="font-bold">Technologies:</span>{" "}
|
449
|
-
{formState.technologies?.join(", ")}
|
450
|
-
</p>
|
451
|
-
</div>
|
452
|
-
)} */}
|
453
458
|
{history.length > 0 && (
|
454
459
|
<ResumeCourseModal
|
455
460
|
onContinue={() => {
|
@@ -15,6 +15,12 @@ export interface Lesson {
|
|
15
15
|
duration?: number
|
16
16
|
}
|
17
17
|
|
18
|
+
const typeToEmoji: Record<string, string> = {
|
19
|
+
read: "📖",
|
20
|
+
code: "💻",
|
21
|
+
quiz: "🧠",
|
22
|
+
}
|
23
|
+
|
18
24
|
interface LessonItemProps {
|
19
25
|
lesson: Lesson
|
20
26
|
isNew: boolean
|
@@ -58,12 +64,12 @@ export const LessonItem: React.FC<LessonItemProps> = ({
|
|
58
64
|
{mode === "teacher" && (
|
59
65
|
<>
|
60
66
|
<span className="index-circle">{cleanFloatString(lesson.id)}</span>
|
61
|
-
<span className="text-gray-500 text-sm">
|
62
|
-
{lesson.type[0] + lesson.type.slice(1).toLowerCase()} ●
|
63
|
-
</span>
|
64
67
|
</>
|
65
68
|
)}
|
66
69
|
|
70
|
+
<span className="text-gray-500 text-sm">
|
71
|
+
{typeToEmoji[lesson.type.toLowerCase()]}
|
72
|
+
</span>
|
67
73
|
{isEditing ? (
|
68
74
|
<input
|
69
75
|
defaultValue={lesson.title}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import { useEffect } from "react"
|
2
|
+
import CreatorSocket from "../utils/socket"
|
3
|
+
|
4
|
+
interface NotificationListenerProps {
|
5
|
+
notificationId: string
|
6
|
+
onNotification: (data: any) => void
|
7
|
+
}
|
8
|
+
|
9
|
+
const socketClient = new CreatorSocket("")
|
10
|
+
|
11
|
+
const NotificationListener: React.FC<NotificationListenerProps> = ({
|
12
|
+
notificationId,
|
13
|
+
onNotification,
|
14
|
+
}) => {
|
15
|
+
useEffect(() => {
|
16
|
+
console.log("NOTIFICATION ID listening", notificationId)
|
17
|
+
if (!notificationId) return
|
18
|
+
|
19
|
+
socketClient.connect()
|
20
|
+
socketClient.on(notificationId, onNotification)
|
21
|
+
socketClient.emit("registerNotification", { notificationId })
|
22
|
+
|
23
|
+
return () => {
|
24
|
+
socketClient.off(notificationId, onNotification)
|
25
|
+
socketClient.disconnect()
|
26
|
+
}
|
27
|
+
}, [notificationId, onNotification])
|
28
|
+
|
29
|
+
return null
|
30
|
+
}
|
31
|
+
|
32
|
+
export default NotificationListener
|
@@ -29,6 +29,7 @@ import { ParamsChecker } from "../ParamsChecker"
|
|
29
29
|
import { randomUUID, slugify } from "../../utils/creatorUtils"
|
30
30
|
import { RIGO_FLOAT_GIF } from "../../utils/constants"
|
31
31
|
import { useTranslation } from "react-i18next"
|
32
|
+
import NotificationListener from "../NotificationListener"
|
32
33
|
|
33
34
|
const SyllabusEditor: React.FC = () => {
|
34
35
|
const navigate = useNavigate()
|
@@ -60,6 +61,7 @@ const SyllabusEditor: React.FC = () => {
|
|
60
61
|
const [isGenerating, setIsGenerating] = useState(false)
|
61
62
|
const [showLoginModal, setShowLoginModal] = useState(false)
|
62
63
|
const [isThinking, setIsThinking] = useState(false)
|
64
|
+
const [notificationId, setNotificationId] = useState<string>("")
|
63
65
|
|
64
66
|
const syllabus = history[history.length - 1]
|
65
67
|
|
@@ -89,8 +91,6 @@ const SyllabusEditor: React.FC = () => {
|
|
89
91
|
}, [])
|
90
92
|
|
91
93
|
const checkSlug = async () => {
|
92
|
-
console.log("Checking slug")
|
93
|
-
|
94
94
|
if (!syllabus.courseInfo.title) {
|
95
95
|
toast.error("Please provide a title for the course")
|
96
96
|
return
|
@@ -109,7 +109,6 @@ const SyllabusEditor: React.FC = () => {
|
|
109
109
|
slug = slugify(newTitle)
|
110
110
|
isAvailable = await isSlugAvailable(slug)
|
111
111
|
}
|
112
|
-
console.log("Slug is available", slug)
|
113
112
|
|
114
113
|
push({
|
115
114
|
...syllabus,
|
@@ -162,44 +161,9 @@ const SyllabusEditor: React.FC = () => {
|
|
162
161
|
auth.rigoToken && isAuthenticated ? false : true
|
163
162
|
)
|
164
163
|
|
165
|
-
|
166
|
-
parseLesson(step, syllabus.lessons)
|
167
|
-
)
|
168
|
-
push({
|
169
|
-
...syllabus,
|
170
|
-
lessons: lessons,
|
171
|
-
courseInfo: {
|
172
|
-
...syllabus.courseInfo,
|
173
|
-
title: fixTitleLength(res.parsed.title) || syllabus.courseInfo.title,
|
174
|
-
description:
|
175
|
-
res.parsed.description || syllabus.courseInfo.description,
|
176
|
-
language:
|
177
|
-
res.parsed.languageCode || syllabus.courseInfo.language || "en",
|
178
|
-
technologies:
|
179
|
-
res.parsed.technologies || syllabus.courseInfo.technologies || [],
|
180
|
-
},
|
181
|
-
})
|
182
|
-
|
183
|
-
if (res.parsed.languageCode) {
|
184
|
-
i18n.changeLanguage(res.parsed.languageCode)
|
185
|
-
}
|
186
|
-
|
187
|
-
const newMessages: TMessage[] = [
|
188
|
-
...messages,
|
189
|
-
{
|
190
|
-
type: "user",
|
191
|
-
content: prompt,
|
192
|
-
},
|
193
|
-
{
|
194
|
-
type: "assistant",
|
195
|
-
content: res.parsed.aiMessage,
|
196
|
-
},
|
197
|
-
]
|
198
|
-
setMessages(newMessages)
|
199
|
-
setIsThinking(false)
|
164
|
+
setNotificationId(res.notificationId)
|
200
165
|
} catch (error) {
|
201
166
|
console.error(error)
|
202
|
-
// Remove the last message
|
203
167
|
const newMessages = [...messages]
|
204
168
|
newMessages.pop()
|
205
169
|
setMessages(newMessages as TMessage[])
|
@@ -248,7 +212,6 @@ const SyllabusEditor: React.FC = () => {
|
|
248
212
|
}
|
249
213
|
|
250
214
|
setIsGenerating(true)
|
251
|
-
console.log("Timeout set")
|
252
215
|
|
253
216
|
const timeout = setTimeout(() => {
|
254
217
|
cleanAll()
|
@@ -257,10 +220,8 @@ const SyllabusEditor: React.FC = () => {
|
|
257
220
|
)}?token=${auth.bcToken}&language=${syllabus.courseInfo.language}`
|
258
221
|
}, 8000)
|
259
222
|
try {
|
260
|
-
console.log("Creating course")
|
261
223
|
await createCourse(syllabus, tokenToUse, auth.bcToken)
|
262
224
|
} catch (error) {
|
263
|
-
console.log("Failed to create course, clearing timeout")
|
264
225
|
console.error("Failed to create course:", error)
|
265
226
|
clearTimeout(timeout)
|
266
227
|
setIsGenerating(false)
|
@@ -269,8 +230,6 @@ const SyllabusEditor: React.FC = () => {
|
|
269
230
|
|
270
231
|
if (!syllabus) return null
|
271
232
|
|
272
|
-
console.log(syllabus, "SYLLABUS")
|
273
|
-
|
274
233
|
return isGenerating ? (
|
275
234
|
<>
|
276
235
|
<Loader
|
@@ -284,6 +243,45 @@ It may take a moment..."
|
|
284
243
|
) : (
|
285
244
|
<div className="flex w-full bg-white rounded-md shadow-md overflow-hidden h-screen ">
|
286
245
|
<ParamsChecker />
|
246
|
+
<NotificationListener
|
247
|
+
onNotification={(res) => {
|
248
|
+
const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
|
249
|
+
parseLesson(step, syllabus.lessons)
|
250
|
+
)
|
251
|
+
push({
|
252
|
+
...syllabus,
|
253
|
+
lessons: lessons,
|
254
|
+
courseInfo: {
|
255
|
+
...syllabus.courseInfo,
|
256
|
+
title:
|
257
|
+
fixTitleLength(res.parsed.title) || syllabus.courseInfo.title,
|
258
|
+
description:
|
259
|
+
res.parsed.description || syllabus.courseInfo.description,
|
260
|
+
language:
|
261
|
+
res.parsed.languageCode || syllabus.courseInfo.language || "en",
|
262
|
+
technologies:
|
263
|
+
res.parsed.technologies.length > 0
|
264
|
+
? res.parsed.technologies
|
265
|
+
: syllabus.courseInfo.technologies || [],
|
266
|
+
},
|
267
|
+
})
|
268
|
+
|
269
|
+
if (res.parsed.languageCode) {
|
270
|
+
i18n.changeLanguage(res.parsed.languageCode)
|
271
|
+
}
|
272
|
+
|
273
|
+
setMessages((prev) => [
|
274
|
+
...prev.filter((m) => Boolean(m.content)),
|
275
|
+
{
|
276
|
+
type: "assistant",
|
277
|
+
content: res.parsed.aiMessage,
|
278
|
+
},
|
279
|
+
])
|
280
|
+
setIsThinking(false)
|
281
|
+
setNotificationId("")
|
282
|
+
}}
|
283
|
+
notificationId={notificationId}
|
284
|
+
/>
|
287
285
|
{showLoginModal && (
|
288
286
|
<Login
|
289
287
|
onFinish={() => {
|
@@ -14,8 +14,8 @@ export const slugify = (text: string) => {
|
|
14
14
|
.replace(/^-+|-+$/g, "") // Trim hyphens from start/end
|
15
15
|
}
|
16
16
|
|
17
|
-
export const randomUUID = () => {
|
18
|
-
return Math.random().toString(36).substring(2,
|
17
|
+
export const randomUUID = (length = 15) => {
|
18
|
+
return Math.random().toString(36).substring(2, length)
|
19
19
|
}
|
20
20
|
|
21
21
|
export const processImage = async (
|