@learnpack/learnpack 5.0.168 → 5.0.176

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.
Files changed (36) hide show
  1. package/README.md +13 -13
  2. package/lib/commands/serve.js +42 -2
  3. package/{src/creatorDist/assets/index-C_YTggyk.css → lib/creatorDist/assets/index-CrWESWmj.css} +43 -11
  4. package/lib/creatorDist/assets/index-T7usmMYO.js +32991 -0
  5. package/lib/creatorDist/index.html +2 -2
  6. package/lib/utils/api.d.ts +1 -1
  7. package/lib/utils/api.js +2 -1
  8. package/lib/utils/readDocuments.d.ts +0 -0
  9. package/lib/utils/readDocuments.js +1 -0
  10. package/oclif.manifest.json +1 -1
  11. package/package.json +3 -1
  12. package/src/commands/serve.ts +56 -2
  13. package/src/creator/src/App.tsx +51 -34
  14. package/src/creator/src/components/ConsumablesManager.tsx +1 -0
  15. package/src/creator/src/components/FileUploader.tsx +64 -52
  16. package/src/creator/src/components/Login.tsx +172 -82
  17. package/src/creator/src/components/ResumeCourseModal.tsx +38 -0
  18. package/src/creator/src/components/StepWizard.tsx +12 -10
  19. package/src/creator/src/components/TurnstileChallenge.tsx +2 -7
  20. package/src/creator/src/components/syllabus/ContentIndex.tsx +1 -0
  21. package/src/creator/src/components/syllabus/SyllabusEditor.tsx +63 -29
  22. package/src/creator/src/utils/constants.ts +2 -1
  23. package/src/creator/src/utils/lib.ts +55 -0
  24. package/src/creator/src/utils/rigo.ts +12 -5
  25. package/src/creator/src/utils/store.ts +22 -1
  26. package/{lib/creatorDist/assets/index-C_YTggyk.css → src/creatorDist/assets/index-CrWESWmj.css} +43 -11
  27. package/src/creatorDist/assets/index-T7usmMYO.js +32991 -0
  28. package/src/creatorDist/index.html +2 -2
  29. package/src/ui/_app/app.js +286 -286
  30. package/src/ui/app.tar.gz +0 -0
  31. package/src/utils/api.ts +2 -1
  32. package/src/utils/readDocuments.ts +0 -0
  33. package/lib/creatorDist/assets/index-4XkqESUr.js +0 -83719
  34. package/lib/creatorDist/assets/pdf.worker-DSVOJ9H9.js +0 -56037
  35. package/src/creatorDist/assets/index-4XkqESUr.js +0 -83719
  36. package/src/creatorDist/assets/pdf.worker-DSVOJ9H9.js +0 -56037
@@ -10,8 +10,8 @@
10
10
  />
11
11
 
12
12
  <title>Learnpack Creator: Craft tutorials in seconds!</title>
13
- <script type="module" crossorigin src="/creator/assets/index-4XkqESUr.js"></script>
14
- <link rel="stylesheet" crossorigin href="/creator/assets/index-C_YTggyk.css">
13
+ <script type="module" crossorigin src="/creator/assets/index-T7usmMYO.js"></script>
14
+ <link rel="stylesheet" crossorigin href="/creator/assets/index-CrWESWmj.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -1,4 +1,4 @@
1
- export declare const RIGOBOT_HOST = "https://rigobot.herokuapp.com";
1
+ export declare const RIGOBOT_HOST = "https://rigobot-test-cca7d841c9d8.herokuapp.com";
2
2
  type TConsumableSlug = "ai-conversation-message" | "ai-compilation" | "ai-tutorial-generation" | "ai-generation" | "learnpack-publish";
3
3
  export declare const countConsumables: (consumables: any, consumableSlug?: TConsumableSlug) => any;
4
4
  export declare const getConsumable: (token: string, consumableSlug?: TConsumableSlug) => Promise<any>;
package/lib/utils/api.js CHANGED
@@ -6,7 +6,8 @@ const storage = require("node-persist");
6
6
  const cli_ux_1 = require("cli-ux");
7
7
  const axios_1 = require("axios");
8
8
  const HOST = "https://breathecode.herokuapp.com";
9
- exports.RIGOBOT_HOST = "https://rigobot.herokuapp.com";
9
+ // export const RIGOBOT_HOST = "https://rigobot.herokuapp.com"
10
+ exports.RIGOBOT_HOST = "https://rigobot-test-cca7d841c9d8.herokuapp.com";
10
11
  // export const RIGOBOT_HOST =
11
12
  // "https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us118.gitpod.io"
12
13
  // eslint-disable-next-line
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -1 +1 @@
1
- {"version":"5.0.168","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":[]}}}
1
+ {"version":"5.0.176","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.168",
4
+ "version": "5.0.176",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -44,10 +44,12 @@
44
44
  "espree": "^9.3.2",
45
45
  "eta": "^1.2.0",
46
46
  "express": "^4.17.1",
47
+ "file-type": "^21.0.0",
47
48
  "form-data": "^4.0.2",
48
49
  "front-matter": "^4.0.2",
49
50
  "html-to-text": "^9.0.5",
50
51
  "js-yaml": "^4.1.0",
52
+ "jszip": "^3.10.1",
51
53
  "markdown-it": "^14.1.0",
52
54
  "mkdirp": "^3.0.1",
53
55
  "moment": "^2.27.0",
@@ -1,6 +1,6 @@
1
1
  import { flags } from "@oclif/command"
2
- // import multer from "multer"
3
- // import * as ytdl from "ytdl-core"
2
+
3
+ // import { readDocument } from "../utils/readDocuments"
4
4
  import { YoutubeTranscript } from "youtube-transcript"
5
5
  import * as express from "express"
6
6
  import * as cors from "cors"
@@ -535,6 +535,60 @@ export default class ServeCommand extends SessionCommand {
535
535
  }
536
536
  )
537
537
 
538
+ app.post("/read-document", upload.single("file"), async (req, res) => {
539
+ console.log("READING A DOCUMENT")
540
+ // const rigoToken = req.header("x-rigo-token")
541
+ // if (!rigoToken) {
542
+ // return res.status(400).json({ error: "Rigo token is required" })
543
+ // }
544
+
545
+ try {
546
+ // eslint-disable-next-line
547
+ // @ts-ignore
548
+ if (!req.file) {
549
+ return res.status(400).json({ error: "Missing file" })
550
+ }
551
+
552
+ const resultId = `${Date.now()}-${Math.floor(Math.random() * 1e6)}`
553
+
554
+ const webhookUrl = `https://9cw5zmww-3000.use2.devtunnels.ms/document/results/result_id/${resultId}/`
555
+
556
+ // Construir form-data para enviar al servidor proxy
557
+ const formData = new FormData()
558
+ // eslint-disable-next-line
559
+ // @ts-ignore
560
+ formData.append("file", req.file.buffer, {
561
+ // eslint-disable-next-line
562
+ // @ts-ignore
563
+ filename: req.file.originalname,
564
+ // eslint-disable-next-line
565
+ // @ts-ignore
566
+ contentType: req.file.mimetype,
567
+ })
568
+ formData.append("webhook_callback_url", webhookUrl)
569
+
570
+ // Hacer POST al servidor externo
571
+ const response = await axios.post(
572
+ `${RIGOBOT_HOST}/v1/learnpack/documents/pdf/read`,
573
+ formData,
574
+ {
575
+ headers: {
576
+ ...formData.getHeaders(),
577
+ Authorization: `Token `,
578
+ },
579
+ maxBodyLength: Infinity,
580
+ }
581
+ )
582
+ console.log("RESPONSE FROM RIGOBOT", response)
583
+
584
+ // Responder con el UUID generado
585
+ return res.json({ resultId })
586
+ } catch (error) {
587
+ console.error("❌ Error in /read-document:", error)
588
+ return res.status(500).json({ error: (error as Error).message })
589
+ }
590
+ })
591
+
538
592
  app.get("/check-preview-image/:slug", async (req, res) => {
539
593
  const { slug } = req.params
540
594
  const file = bucket.file(`courses/${slug}/preview.png`)
@@ -6,7 +6,7 @@ import { useNavigate } from "react-router"
6
6
  import { useShallow } from "zustand/react/shallow"
7
7
  import useStore from "./utils/store"
8
8
 
9
- import { interactiveCreation, isHuman } from "./utils/rigo"
9
+ import { publicInteractiveCreation, isHuman } from "./utils/rigo"
10
10
  import { checkParams, loginWithToken, parseLesson } from "./utils/lib"
11
11
 
12
12
  import { Uploader } from "./components/Uploader"
@@ -15,6 +15,7 @@ import { ParamsChecker } from "./components/ParamsChecker"
15
15
  import { RIGO_FLOAT_GIT } from "./utils/constants"
16
16
  import TurnstileChallenge from "./components/TurnstileChallenge"
17
17
  // import TurnstileChallenge from "./components/TurnstileChallenge"
18
+ import ResumeCourseModal from "./components/ResumeCourseModal"
18
19
 
19
20
  function App() {
20
21
  const navigate = useNavigate()
@@ -26,16 +27,22 @@ function App() {
26
27
  push,
27
28
  cleanHistory,
28
29
  setPlanToRedirect,
30
+ history,
29
31
  uploadedFiles,
32
+ auth,
33
+ resetFormState,
30
34
  } = useStore(
31
35
  useShallow((state) => ({
32
36
  formState: state.formState,
33
37
  setFormState: state.setFormState,
34
38
  setAuth: state.setAuth,
35
39
  push: state.push,
40
+ history: state.history,
36
41
  cleanHistory: state.cleanHistory,
37
42
  setPlanToRedirect: state.setPlanToRedirect,
38
43
  uploadedFiles: state.uploadedFiles,
44
+ auth: state.auth,
45
+ resetFormState: state.resetFormState,
39
46
  }))
40
47
  )
41
48
 
@@ -59,6 +66,7 @@ function App() {
59
66
  userId: user.id,
60
67
  rigoToken: user.rigobot.key,
61
68
  user: user,
69
+ publicToken: "",
62
70
  })
63
71
  }
64
72
  }
@@ -100,8 +108,9 @@ function App() {
100
108
  try {
101
109
  cleanHistory()
102
110
 
103
- const res = await interactiveCreation({
104
- courseInfo: `${JSON.stringify(formState)}
111
+ const res = await publicInteractiveCreation(
112
+ {
113
+ courseInfo: `${JSON.stringify(formState)}
105
114
  ${
106
115
  uploadedFiles.length > 0
107
116
  ? `These files where uploaded by the user: \n\n${JSON.stringify(
@@ -109,8 +118,10 @@ ${
109
118
  )}`
110
119
  : ""
111
120
  }`,
112
- prevInteractions: "",
113
- })
121
+ prevInteractions: "",
122
+ },
123
+ auth.publicToken
124
+ )
114
125
  const lessons = res.parsed.listOfSteps.map((lesson: any) => {
115
126
  return parseLesson(lesson, [])
116
127
  })
@@ -126,22 +137,11 @@ ${
126
137
  })
127
138
  navigate("/creator/syllabus")
128
139
  setFormState({
129
- description: "",
130
- duration: 0,
131
- targetAudience: "",
132
- hasContentIndex: false,
133
- contentIndex: "",
134
140
  isCompleted: false,
135
141
  currentStep: "description",
136
- variables: [
137
- "description",
138
- "duration",
139
- "hasContentIndex",
140
- "verifyHuman",
141
- ],
142
142
  })
143
143
  } catch (error) {
144
- console.error(error)
144
+ console.error(error, "ERROR CREATING TUTORIAL")
145
145
  toast.error("Something went wrong. Please try again.")
146
146
  setFormState({
147
147
  isCompleted: false,
@@ -279,14 +279,19 @@ ${
279
279
  isCompleted: false,
280
280
  content: (
281
281
  <TurnstileChallenge
282
- siteKey={"0x4AAAAAABeZ9tjEevGBsJFU"}
282
+ siteKey={"0x4AAAAAABeKMBYYinMU4Ib0"}
283
283
  onSuccess={async (token) => {
284
- const _isHuman = await isHuman(token)
285
- if (_isHuman) {
284
+ const { human, message, token: jwtToken } = await isHuman(token)
285
+ if (human) {
286
286
  toast.success("You are a human! 👌🏻")
287
+
288
+ setAuth({
289
+ ...auth,
290
+ publicToken: jwtToken,
291
+ })
287
292
  setFormState({ isCompleted: true })
288
293
  } else {
289
- toast.error("You are not a human! 🤖")
294
+ toast.error(message)
290
295
  setFormState({
291
296
  currentStep: "hasContentIndex",
292
297
  })
@@ -297,8 +302,6 @@ ${
297
302
  },
298
303
  ]
299
304
 
300
- console.log(formState.variables, "FORM STATE VAIRABLEs")
301
-
302
305
  return steps.filter(
303
306
  (step) =>
304
307
  formState.variables.includes(step.slug) ||
@@ -309,22 +312,36 @@ ${
309
312
  return (
310
313
  <>
311
314
  <ParamsChecker />
312
- {formState.isCompleted ? (
315
+ {formState.isCompleted && history.length === 0 ? (
313
316
  <Loader
314
317
  text="Learnpack is setting up your tutorial. It may take a moment..."
315
318
  icon={<img src={RIGO_FLOAT_GIT} alt="rigo" className="w-20 h-20" />}
316
319
  />
317
320
  ) : (
318
- <StepWizard
319
- formState={formState}
320
- steps={buildSteps()}
321
- setFormState={setFormState}
322
- onFinish={() => {
323
- setFormState({
324
- isCompleted: true,
325
- })
326
- }}
327
- />
321
+ <>
322
+ {history.length > 0 && (
323
+ <ResumeCourseModal
324
+ onContinue={() => {
325
+ navigate("/creator/syllabus")
326
+ }}
327
+ onStartOver={() => {
328
+ resetFormState()
329
+ cleanHistory()
330
+ }}
331
+ />
332
+ )}
333
+ <StepWizard
334
+ hideLastButton={true}
335
+ formState={formState}
336
+ steps={buildSteps()}
337
+ setFormState={setFormState}
338
+ onFinish={() => {
339
+ setFormState({
340
+ isCompleted: true,
341
+ })
342
+ }}
343
+ />
344
+ </>
328
345
  )}
329
346
  </>
330
347
  )
@@ -24,6 +24,7 @@ export const ConsumablesManager = () => {
24
24
  rigoToken: "",
25
25
  userId: "",
26
26
  user: null,
27
+ publicToken: "",
27
28
  })
28
29
  }
29
30
  }
@@ -1,12 +1,8 @@
1
1
  import React, { useRef, useState } from "react"
2
- import * as pdfjsLib from "pdfjs-dist"
3
- import mammoth from "mammoth"
4
2
  import { SVGS } from "../assets/svgs"
5
- import pdfWorker from "pdfjs-dist/build/pdf.worker?worker"
6
3
  import { ContentCard } from "./ContentCard"
7
4
  import useStore from "../utils/store"
8
-
9
- pdfjsLib.GlobalWorkerOptions.workerPort = new pdfWorker()
5
+ import toast from "react-hot-toast"
10
6
 
11
7
  const allowedTypes = [
12
8
  "application/pdf",
@@ -21,53 +17,64 @@ export interface ParsedFile {
21
17
  }
22
18
 
23
19
  interface FileUploaderProps {
24
- // onResult: (files: ParsedFile[]) => void
25
20
  styledAs?: "button" | "card"
26
21
  }
27
22
 
28
23
  const FileUploader: React.FC<FileUploaderProps> = ({ styledAs = "button" }) => {
24
+ const rigoToken = useStore((state) => state.auth.rigoToken)
29
25
  const inputRef = useRef<HTMLInputElement>(null)
30
26
  const uploadedFiles = useStore((state) => state.uploadedFiles)
31
27
  const setUploadedFiles = useStore((state) => state.setUploadedFiles)
32
28
  const [isDragging, setIsDragging] = useState(false)
33
- // const [parsedFiles, setParsedFiles] = useState<ParsedFile[]>([]) F
29
+ const [isLoading, setIsLoading] = useState(false)
34
30
 
35
- const extractText = async (file: File): Promise<ParsedFile> => {
31
+ const extractTextFromFile = async (file: File): Promise<ParsedFile> => {
36
32
  const { type, name } = file
37
33
 
38
- if (type === "application/pdf") {
39
- const arrayBuffer = await file.arrayBuffer()
40
- const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise
41
- let text = ""
42
-
43
- for (let i = 0; i < pdf.numPages; i++) {
44
- const page = await pdf.getPage(i + 1)
45
- const content = await page.getTextContent()
46
- text += content.items.map((item: any) => item.str).join(" ") + "\n"
47
- }
48
-
34
+ if (type === "text/plain" || type === "text/markdown") {
35
+ const text = await file.text()
49
36
  return { name, text }
50
37
  }
51
38
 
52
- if (
53
- type ===
54
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
55
- ) {
56
- const arrayBuffer = await file.arrayBuffer()
57
- const { value } = await mammoth.extractRawText({ arrayBuffer })
58
- return { name, text: value }
39
+ const formData = new FormData()
40
+ formData.append("file", file)
41
+
42
+ const loadingToast = toast.loading(`Processing ${file.name}...`)
43
+ try {
44
+ const res = await fetch("http://localhost:3000/read-document", {
45
+ method: "POST",
46
+ headers: {
47
+ "x-rigo-token": rigoToken,
48
+ },
49
+ body: formData,
50
+ })
51
+
52
+ if (!res.ok) throw new Error(`Failed to read ${file.name}`)
53
+ const data = await res.json()
54
+ console.log("DATA FROM BACKEND", data)
55
+ toast.success(`✅ ${file.name} processed`, { id: loadingToast })
56
+ return { name, text: data.content }
57
+ } catch (err: any) {
58
+ toast.error(`❌ ${file.name} failed: ${err.message}`, {
59
+ id: loadingToast,
60
+ })
61
+ return { name, text: "" }
59
62
  }
60
-
61
- const text = await file.text()
62
- return { name, text }
63
63
  }
64
64
 
65
65
  const parseFiles = async (files: FileList | File[]) => {
66
66
  const validFiles = Array.from(files).filter((file) =>
67
67
  allowedTypes.includes(file.type)
68
68
  )
69
- const parsed = await Promise.all(validFiles.map(extractText))
69
+ if (validFiles.length === 0) {
70
+ toast.error("No valid files selected")
71
+ return
72
+ }
73
+
74
+ setIsLoading(true)
75
+ const parsed = await Promise.all(validFiles.map(extractTextFromFile))
70
76
  setUploadedFiles([...uploadedFiles, ...parsed])
77
+ setIsLoading(false)
71
78
  }
72
79
 
73
80
  const handleInput = async (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -86,16 +93,16 @@ const FileUploader: React.FC<FileUploaderProps> = ({ styledAs = "button" }) => {
86
93
  return (
87
94
  <div className="flex flex-col gap-2 w-full">
88
95
  {uploadedFiles.length > 0 && styledAs === "card" && (
89
- <div className="w-full flex flex-row gap-2 flex-wrap justify-center items-center ">
96
+ <div className="w-full flex flex-row gap-2 flex-wrap justify-center items-center">
90
97
  {uploadedFiles.map((file, idx) => (
91
98
  <div
92
99
  key={idx}
93
- className="p-3 rounded-md bg-white shadow-sm text-sm text-gray-800 text-left "
100
+ className="p-3 rounded-md bg-white shadow-sm text-sm text-gray-800 text-left"
94
101
  title={file.name}
95
102
  >
96
103
  <strong>{file.name.slice(0, 20)}...</strong>
97
104
  <button
98
- className="text-gray-600 mt-1 float-right cursor-pointer"
105
+ className="text-gray-600 mt-1 float-right cursor-pointer"
99
106
  onClick={() =>
100
107
  setUploadedFiles(uploadedFiles.filter((_, i) => i !== idx))
101
108
  }
@@ -106,6 +113,7 @@ const FileUploader: React.FC<FileUploaderProps> = ({ styledAs = "button" }) => {
106
113
  ))}
107
114
  </div>
108
115
  )}
116
+
109
117
  {styledAs === "button" && (
110
118
  <div className="flex items-center justify-end gap-2 w-100">
111
119
  <button
@@ -113,30 +121,34 @@ const FileUploader: React.FC<FileUploaderProps> = ({ styledAs = "button" }) => {
113
121
  className="cursor-pointer blue-on-hover flex items-center justify-center w-6 h-6"
114
122
  onClick={() => inputRef.current?.click()}
115
123
  >
116
- {SVGS.clip}
124
+ {isLoading ? (
125
+ <div className="loader w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
126
+ ) : (
127
+ SVGS.clip
128
+ )}
117
129
  </button>
118
130
  </div>
119
131
  )}
120
132
 
121
133
  {styledAs === "card" && (
122
- <>
123
- <ContentCard
124
- description={
125
- isDragging
126
- ? "Drop it here"
127
- : "Upload a PDF or DOCX file or drag it here"
128
- }
129
- icon={isDragging ? SVGS.clip : SVGS.pdf}
130
- onClick={() => inputRef.current?.click()}
131
- onDragOver={(e) => {
132
- e.preventDefault()
133
- setIsDragging(true)
134
- }}
135
- onDragLeave={() => setIsDragging(false)}
136
- onDrop={handleDrop}
137
- className={isDragging ? "border-blue-600 bg-blue-50" : ""}
138
- />
139
- </>
134
+ <ContentCard
135
+ description={
136
+ isDragging
137
+ ? "Drop it here"
138
+ : isLoading
139
+ ? "Processing..."
140
+ : "Upload a PDF or DOCX file or drag it here"
141
+ }
142
+ icon={isDragging ? SVGS.clip : SVGS.pdf}
143
+ onClick={() => inputRef.current?.click()}
144
+ onDragOver={(e) => {
145
+ e.preventDefault()
146
+ setIsDragging(true)
147
+ }}
148
+ onDragLeave={() => setIsDragging(false)}
149
+ onDrop={handleDrop}
150
+ className={isDragging ? "border-blue-600 bg-blue-50" : ""}
151
+ />
140
152
  )}
141
153
 
142
154
  <input