@learnpack/learnpack 5.0.204 → 5.0.211

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.
@@ -189,12 +189,11 @@ function App() {
189
189
  },
190
190
  {
191
191
  type: "assistant",
192
- content: "If you're satisfied, type 'OK' in the chat.",
192
+ content: t("contentIndex.okMessage"),
193
193
  },
194
194
  {
195
195
  type: "assistant",
196
- content:
197
- "If not, what would you like me to change? You can sat things like:\n - Add more exercises\n - Make it more difficult\n - Remove step 1.1 and replace it with a new step that explains the concept of X ",
196
+ content: t("contentIndex.instructionsMessage"),
198
197
  },
199
198
  ])
200
199
  } catch (error) {
@@ -248,8 +248,9 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
248
248
  Register here.
249
249
  </button> */}
250
250
  <a
251
- href={`https://www.learnpack.co/register`}
251
+ href={`https://www.learnpack.co/register?callback=${encodeURIComponent(window.location.href)}`}
252
252
  target="_blank"
253
+ rel="noopener noreferrer"
253
254
  className="text-blue-600 font-medium"
254
255
  >
255
256
  {t("login.registerHere")}
@@ -9,6 +9,7 @@ import { motion, AnimatePresence } from "framer-motion"
9
9
  import toast from "react-hot-toast"
10
10
  import { randomUUID } from "../../utils/creatorUtils"
11
11
  import { useTranslation } from "react-i18next"
12
+ import { t } from "i18next"
12
13
 
13
14
  const ContentIndexHeader = ({
14
15
  messages,
@@ -295,7 +296,7 @@ export const ContentIndex = ({
295
296
  onClick={() => scrollToBottom("bottom")}
296
297
  className="px-4 py-1 bg-white text-sm rounded hover:bg-blue-50 cursor-pointer pointer-events-auto font-bold flex items-center gap-2 "
297
298
  >
298
- Continue scrolling
299
+ {t("contentIndex.continueScrolling")}
299
300
  {SVGS.downArrow}
300
301
  </button>
301
302
  </div>
@@ -10,7 +10,7 @@ import {
10
10
  loginWithToken,
11
11
  createCourse,
12
12
  isSlugAvailable,
13
- reWriteTitle,
13
+ // reWriteTitle,
14
14
  useConsumableCall,
15
15
  isValidRigoToken,
16
16
  fixTitleLength,
@@ -26,7 +26,7 @@ import { Sidebar } from "./Sidebar"
26
26
  import Login from "../Login"
27
27
  import { useNavigate } from "react-router"
28
28
  import { ParamsChecker } from "../ParamsChecker"
29
- import { slugify } from "../../utils/creatorUtils"
29
+ import { randomUUID, slugify } from "../../utils/creatorUtils"
30
30
  import { RIGO_FLOAT_GIF } from "../../utils/constants"
31
31
  import { useTranslation } from "react-i18next"
32
32
 
@@ -80,21 +80,35 @@ const SyllabusEditor: React.FC = () => {
80
80
  }, [])
81
81
 
82
82
  const checkSlug = async () => {
83
- const slug = slugify(syllabus.courseInfo.title || "")
84
- const isAvailable = await isSlugAvailable(slug)
85
- if (!isAvailable) {
86
- const newTitle = await reWriteTitle(
87
- syllabus.courseInfo.title || "",
88
- auth.rigoToken
89
- )
90
- push({
91
- ...syllabus,
92
- courseInfo: {
93
- ...syllabus.courseInfo,
94
- title: newTitle,
95
- },
96
- })
83
+ console.log("Checking slug")
84
+
85
+ if (!syllabus.courseInfo.title) {
86
+ toast.error("Please provide a title for the course")
87
+ return
97
88
  }
89
+
90
+ let slug = slugify(syllabus.courseInfo.title)
91
+ let newTitle = syllabus.courseInfo.title
92
+ let isAvailable = await isSlugAvailable(slug)
93
+
94
+ while (!isAvailable) {
95
+ if (newTitle.length > 50) {
96
+ newTitle = newTitle.slice(0, 44)
97
+ }
98
+ newTitle = newTitle + "-" + randomUUID()
99
+ newTitle = newTitle.slice(0, 50)
100
+ slug = slugify(newTitle)
101
+ isAvailable = await isSlugAvailable(slug)
102
+ }
103
+ console.log("Slug is available", slug)
104
+
105
+ push({
106
+ ...syllabus,
107
+ courseInfo: {
108
+ ...syllabus.courseInfo,
109
+ title: newTitle,
110
+ },
111
+ })
98
112
  }
99
113
 
100
114
  const sendPrompt = async (prompt: string) => {
@@ -210,25 +224,32 @@ const SyllabusEditor: React.FC = () => {
210
224
  toast.error(
211
225
  "You don't have enough credits to generate a course! Please add more credits to your account."
212
226
  )
213
- toast.error("You will be redirected to the settings page in 5 seconds.")
227
+ toast.loading("You will be redirected to the settings page in 5 seconds.")
214
228
  setTimeout(() => {
215
229
  window.location.href =
216
230
  "https://learnpack.co/settings?token=" + auth.bcToken
217
231
  }, 5000)
218
232
  return
219
233
  }
220
- console.log(syllabus, "SYLLABUS")
221
234
 
222
235
  setIsGenerating(true)
223
- createCourse(syllabus, tokenToUse, auth.bcToken)
236
+ console.log("Timeout set")
224
237
 
225
- setTimeout(() => {
238
+ const timeout = setTimeout(() => {
226
239
  cleanAll()
227
- setIsGenerating(false)
228
240
  window.location.href = `/preview/${slugify(
229
241
  syllabus.courseInfo.title || ""
230
242
  )}?token=${auth.bcToken}&language=${syllabus.courseInfo.language}`
231
- }, 3000)
243
+ }, 8000)
244
+ try {
245
+ console.log("Creating course")
246
+ await createCourse(syllabus, tokenToUse, auth.bcToken)
247
+ } catch (error) {
248
+ console.log("Failed to create course, clearing timeout")
249
+ console.error("Failed to create course:", error)
250
+ clearTimeout(timeout)
251
+ setIsGenerating(false)
252
+ }
232
253
  }
233
254
 
234
255
  if (!syllabus) return null
@@ -17,7 +17,7 @@
17
17
  "60": "Around 1 hour",
18
18
  "120": "Around 2 hours"
19
19
  },
20
- "purpose": "How would you like to use learnpack?",
20
+ "purpose": "What’s your primary learning goal for this tutorial?",
21
21
  "verifyHuman": "Please verify you are a human",
22
22
  "humanSuccess": "You are a human! 👌🏻",
23
23
  "hasContentIndex": "Any materials to get this course started?",
@@ -31,19 +31,19 @@
31
31
  "purposeSelector": {
32
32
  "learnpack-lesson-writer": {
33
33
  "label": "Understand a new topic",
34
- "description": "Learn about a new concept (e.g., ISO 27001 or exponential decay)."
34
+ "description": "Learn about a new concept (e.g., What is compound interest?)."
35
35
  },
36
36
  "homework-and-exam-preparation-aid": {
37
37
  "label": "Practice for an exam or homework",
38
- "description": "Solve math problems or certification questions."
38
+ "description": "Timed problems with instant feedback in math, STEM or any other topic"
39
39
  },
40
40
  "skill-building-facilitator": {
41
41
  "label": "Build real-world skills",
42
- "description": "Apply concepts to projects, data science, or security audits."
42
+ "description": "Step-by-step projects that mirror real tasks (data-science notebooks, security audits…)"
43
43
  },
44
44
  "certification-preparation-specialist": {
45
45
  "label": "Prepare for a certification",
46
- "description": "Get ready for exams like ISO 27001 Lead Auditor."
46
+ "description": "Full mock exams for standard certifications & study sheets. E.g: TOEFL®, CompTIA, etc"
47
47
  }
48
48
  },
49
49
  "uploader": {
@@ -79,7 +79,10 @@
79
79
  "changesReverted": "Changes reverted!",
80
80
  "readyToCreate": "I'm ready. Create the course for me!",
81
81
  "creatingCourseAs": "Creating the course as {{name}}",
82
- "loginAsSomeoneElse": "Login as someone else"
82
+ "loginAsSomeoneElse": "Login as someone else",
83
+ "continueScrolling": "Continue scrolling",
84
+ "okMessage": "If you're satisfied, type 'OK' in the chat.",
85
+ "instructionsMessage": "If not, what would you like me to change? You can say things like:\n - Add more exercises\n - Make it more difficult\n - Remove step 1.1 and replace it with a new step that explains the concept of X "
83
86
  },
84
87
  "login": {
85
88
  "creatingAccount": "Creating account…",
@@ -107,4 +110,4 @@
107
110
  "youDontHaveAnAccount": "You don't have an account?",
108
111
  "registerHere": "Register here."
109
112
  }
110
- }
113
+ }
@@ -79,7 +79,10 @@
79
79
  "changesReverted": "Cambios revertidos!",
80
80
  "readyToCreate": "Estoy listo. ¡Crea el curso por mí!",
81
81
  "creatingCourseAs": "Creando el curso como {{name}}",
82
- "loginAsSomeoneElse": "Iniciar sesión como alguien más"
82
+ "loginAsSomeoneElse": "Iniciar sesión como alguien más",
83
+ "continueScrolling": "Continuar desplazando",
84
+ "okMessage": "Si estás satisfecho, escribe 'OK' en el chat.",
85
+ "instructionsMessage": "Si no, ¿qué te gustaría que cambiara? Puedes decir cosas como:\n - Añadir más ejercicios\n - Hacerlo más difícil\n - Eliminar el paso 1.1 y reemplazarlo con un nuevo paso que explique el concepto de X "
83
86
  },
84
87
  "login": {
85
88
  "creatingAccount": "Creando cuenta…",
@@ -4,14 +4,16 @@ import { generateImage, getFilenameFromUrl, uploadImageToBucket } from "./lib"
4
4
  export const slugify = (text: string) => {
5
5
  return text
6
6
  .toString()
7
- .normalize("NFD")
8
- .replace(/[\u0300-\u036F]/g, "")
7
+ .normalize("NFD") // Remove accents
8
+ .replace(/[\u0300-\u036F]/g, "") // Remove diacritics
9
9
  .toLowerCase()
10
10
  .trim()
11
- .replace(/\s+/g, "-")
12
- .replace(/[<>:"/\\|?*]/g, "")
13
- .replace(/-+/g, "-")
11
+ .replace(/[^a-z0-9\s._-]/g, "") // Hyphen at the end, no escape needed
12
+ .replace(/\s+/g, "-") // Replace spaces with hyphens
13
+ .replace(/-+/g, "-") // Remove duplicate hyphens
14
+ .replace(/^-+|-+$/g, "") // Trim hyphens from start/end
14
15
  }
16
+
15
17
  export const randomUUID = () => {
16
18
  return Math.random().toString(36).substring(2, 15)
17
19
  }