@learnpack/learnpack 5.0.306 → 5.0.307

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.
@@ -1,21 +1,21 @@
1
- import axios from "axios";
2
- import { franc } from "franc";
3
- import { BREATHECODE_HOST, DEV_MODE, RIGOBOT_HOST } from "./constants";
4
- import { Lesson } from "../components/LessonItem";
5
- import { randomUUID } from "./creatorUtils";
6
- import { Syllabus } from "./store";
1
+ import axios from "axios"
2
+ import { franc } from "franc"
3
+ import { BREATHECODE_HOST, DEV_MODE, RIGOBOT_HOST } from "./constants"
4
+ import { Lesson } from "../components/LessonItem"
5
+ import { randomUUID } from "./creatorUtils"
6
+ import { Syllabus } from "./store"
7
7
 
8
8
  export function parseLesson(input: string, previous: Lesson[]): Lesson | null {
9
- const pattern = /^([\d.]+)\s*-\s*(.*?)\s*\[(\w+):\s*(.+)\]$/;
10
- const match = input.match(pattern);
9
+ const pattern = /^([\d.]+)\s*-\s*(.*?)\s*\[(\w+):\s*(.+)\]$/
10
+ const match = input.match(pattern)
11
11
 
12
- if (!match) return null;
12
+ if (!match) return null
13
13
 
14
- const [, index, title, type, description] = match;
14
+ const [, index, title, type, description] = match
15
15
 
16
16
  const alreadyExistsIndex = previous.findIndex(
17
17
  (lesson) => lesson.id === index && lesson.title === title
18
- );
18
+ )
19
19
 
20
20
  if (alreadyExistsIndex !== -1) {
21
21
  return {
@@ -26,7 +26,7 @@ export function parseLesson(input: string, previous: Lesson[]): Lesson | null {
26
26
  description: description.trim(),
27
27
  duration: previous[alreadyExistsIndex].duration,
28
28
  locked: previous[alreadyExistsIndex].locked || false,
29
- };
29
+ }
30
30
  }
31
31
 
32
32
  return {
@@ -37,51 +37,51 @@ export function parseLesson(input: string, previous: Lesson[]): Lesson | null {
37
37
  duration: 2,
38
38
  uid: randomUUID(),
39
39
  locked: false,
40
- };
40
+ }
41
41
  }
42
42
 
43
43
  export const uploadFileToBucket = async (content: string, path: string) => {
44
44
  const response = await axios.post(`/upload`, {
45
45
  content,
46
46
  destination: path,
47
- });
48
- return response.data;
49
- };
47
+ })
48
+ return response.data
49
+ }
50
50
  export const uploadImageToBucket = async (imageUrl: string, path: string) => {
51
51
  const response = await axios.post(`/upload-image`, {
52
52
  image_url: imageUrl,
53
53
  destination: path,
54
- });
55
- return response.data;
56
- };
54
+ })
55
+ return response.data
56
+ }
57
57
 
58
58
  export const checkParams = (paramsToCheck: string[]) => {
59
- const urlParams = new URLSearchParams(window.location.search);
60
- const result: Record<string, string> = {};
59
+ const urlParams = new URLSearchParams(window.location.search)
60
+ const result: Record<string, string> = {}
61
61
 
62
62
  paramsToCheck.forEach((param) => {
63
- const value = urlParams.get(param);
63
+ const value = urlParams.get(param)
64
64
  if (value !== null) {
65
- result[param] = value;
65
+ result[param] = value
66
66
  }
67
- });
67
+ })
68
68
 
69
- return result;
70
- };
69
+ return result
70
+ }
71
71
 
72
72
  export async function getConsumables(token: string): Promise<any> {
73
- const url = `${BREATHECODE_HOST}/v1/payments/me/service/consumable?virtual=true`;
73
+ const url = `${BREATHECODE_HOST}/v1/payments/me/service/consumable?virtual=true`
74
74
 
75
75
  const headers = {
76
76
  Authorization: `Token ${token}`,
77
- };
77
+ }
78
78
 
79
79
  try {
80
- const response = await axios.get(url, { headers });
81
- return response.data;
80
+ const response = await axios.get(url, { headers })
81
+ return response.data
82
82
  } catch (error) {
83
- console.error("Error fetching consumables:", error);
84
- throw error;
83
+ console.error("Error fetching consumables:", error)
84
+ throw error
85
85
  }
86
86
  }
87
87
 
@@ -89,78 +89,78 @@ type ConsumableSlug =
89
89
  | "ai-conversation-message"
90
90
  | "ai-compilation"
91
91
  | "ai-generation"
92
- | "ai-course-generation";
92
+ | "ai-course-generation"
93
93
 
94
94
  export async function useConsumableCall(
95
95
  breathecodeToken: string,
96
96
  consumableSlug: ConsumableSlug = "ai-conversation-message"
97
97
  ): Promise<boolean> {
98
- const url = `${BREATHECODE_HOST}/v1/payments/me/service/${consumableSlug}/consumptionsession`;
98
+ const url = `${BREATHECODE_HOST}/v1/payments/me/service/${consumableSlug}/consumptionsession`
99
99
 
100
100
  const headers = {
101
101
  Authorization: `Token ${breathecodeToken}`,
102
- };
102
+ }
103
103
 
104
104
  try {
105
- const response = await axios.put(url, {}, { headers });
105
+ const response = await axios.put(url, {}, { headers })
106
106
 
107
107
  if (response.status >= 200 && response.status < 300) {
108
- console.log(`Successfully consumed ${consumableSlug}`);
109
- return true;
108
+ console.log(`Successfully consumed ${consumableSlug}`)
109
+ return true
110
110
  } else {
111
- console.error(`Request failed with status code: ${response.status}`);
112
- console.error(`Response: ${response.data}`);
113
- return false;
111
+ console.error(`Request failed with status code: ${response.status}`)
112
+ console.error(`Response: ${response.data}`)
113
+ return false
114
114
  }
115
115
  } catch (error) {
116
- console.error(`Error consuming ${consumableSlug}:`, error);
117
- return false;
116
+ console.error(`Error consuming ${consumableSlug}:`, error)
117
+ return false
118
118
  }
119
119
  }
120
120
 
121
121
  type ConsumableItem = {
122
- id: number;
123
- how_many: number;
124
- unit_type: string;
125
- valid_until: string | null;
126
- };
122
+ id: number
123
+ how_many: number
124
+ unit_type: string
125
+ valid_until: string | null
126
+ }
127
127
 
128
128
  type VoidEntry = {
129
- id: number;
130
- slug: string;
131
- balance: { unit: number };
132
- items: ConsumableItem[];
133
- };
129
+ id: number
130
+ slug: string
131
+ balance: { unit: number }
132
+ items: ConsumableItem[]
133
+ }
134
134
 
135
135
  export const parseConsumables = (
136
136
  voids: VoidEntry[]
137
137
  ): Record<string, number> => {
138
- const result: Record<string, number> = {};
138
+ const result: Record<string, number> = {}
139
139
 
140
140
  voids.forEach((entry) => {
141
141
  const maxHowMany = entry.items.length
142
142
  ? Math.max(...entry.items.map((item) => item.how_many))
143
- : 0;
144
- result[entry.slug] = maxHowMany;
145
- });
143
+ : 0
144
+ result[entry.slug] = maxHowMany
145
+ })
146
146
 
147
- return result;
148
- };
147
+ return result
148
+ }
149
149
 
150
150
  type LoginInfo = {
151
- email: string;
152
- password: string;
153
- };
151
+ email: string
152
+ password: string
153
+ }
154
154
 
155
155
  export const getRigobotJSON = async (breathecodeToken: string) => {
156
- const rigoUrl = `${RIGOBOT_HOST}/v1/auth/me/token?breathecode_token=${breathecodeToken}`;
157
- const rigoResp = await fetch(rigoUrl);
156
+ const rigoUrl = `${RIGOBOT_HOST}/v1/auth/me/token?breathecode_token=${breathecodeToken}`
157
+ const rigoResp = await fetch(rigoUrl)
158
158
  if (!rigoResp.ok) {
159
- throw new Error("Unable to obtain Rigobot token");
159
+ throw new Error("Unable to obtain Rigobot token")
160
160
  }
161
- const rigobotJson = await rigoResp.json();
162
- return rigobotJson;
163
- };
161
+ const rigobotJson = await rigoResp.json()
162
+ return rigobotJson
163
+ }
164
164
  export const validateUser = async (breathecodeToken: string) => {
165
165
  const config = {
166
166
  method: "GET",
@@ -168,30 +168,30 @@ export const validateUser = async (breathecodeToken: string) => {
168
168
  "Content-Type": "application/json",
169
169
  Authorization: `Token ${breathecodeToken}`,
170
170
  },
171
- };
171
+ }
172
172
 
173
- const res = await fetch(`${BREATHECODE_HOST}/v1/auth/user/me`, config);
173
+ const res = await fetch(`${BREATHECODE_HOST}/v1/auth/user/me`, config)
174
174
  if (!res.ok) {
175
- console.log("ERROR", res);
176
- return null;
175
+ console.log("ERROR", res)
176
+ return null
177
177
  }
178
- const json = await res.json();
178
+ const json = await res.json()
179
179
 
180
180
  if ("roles" in json) {
181
- delete json.roles;
181
+ delete json.roles
182
182
  }
183
183
  if ("permissions" in json) {
184
- delete json.permissions;
184
+ delete json.permissions
185
185
  }
186
186
  if ("settings" in json) {
187
- delete json.settings;
187
+ delete json.settings
188
188
  }
189
189
 
190
- return json;
191
- };
190
+ return json
191
+ }
192
192
 
193
193
  export const login4Geeks = async (loginInfo: LoginInfo) => {
194
- const url = `${BREATHECODE_HOST}/v1/auth/login/`;
194
+ const url = `${BREATHECODE_HOST}/v1/auth/login/`
195
195
 
196
196
  const res = await fetch(url, {
197
197
  body: JSON.stringify(loginInfo),
@@ -199,87 +199,87 @@ export const login4Geeks = async (loginInfo: LoginInfo) => {
199
199
  headers: {
200
200
  "Content-Type": "application/json",
201
201
  },
202
- });
202
+ })
203
203
 
204
204
  if (!res.ok) {
205
- throw Error("Unable to login with provided credentials");
205
+ throw Error("Unable to login with provided credentials")
206
206
  }
207
207
 
208
- const json = await res.json();
208
+ const json = await res.json()
209
209
 
210
- const rigoJson = await getRigobotJSON(json.token);
210
+ const rigoJson = await getRigobotJSON(json.token)
211
211
 
212
- const user = await validateUser(json.token);
213
- const returns = { ...json, rigobot: { ...rigoJson }, user };
212
+ const user = await validateUser(json.token)
213
+ const returns = { ...json, rigobot: { ...rigoJson }, user }
214
214
 
215
- return returns;
216
- };
215
+ return returns
216
+ }
217
217
 
218
218
  export const loginWithToken = async (token: string) => {
219
- const rigoJson = await getRigobotJSON(token);
219
+ const rigoJson = await getRigobotJSON(token)
220
220
 
221
- const user = await validateUser(token);
221
+ const user = await validateUser(token)
222
222
 
223
- const returns = { rigobot: { ...rigoJson }, ...user };
223
+ const returns = { rigobot: { ...rigoJson }, ...user }
224
224
 
225
- return returns;
226
- };
225
+ return returns
226
+ }
227
227
 
228
228
  export const validateTokens = async (
229
229
  breathecodeToken: string,
230
230
  onValidRigoToken: (token: string) => void
231
231
  ) => {
232
- const user = await validateUser(breathecodeToken);
233
- console.log("USER", user);
232
+ const user = await validateUser(breathecodeToken)
233
+ console.log("USER", user)
234
234
  if (!user) {
235
- return false;
235
+ return false
236
236
  }
237
237
 
238
- const rigobotJson = await getRigobotJSON(breathecodeToken);
238
+ const rigobotJson = await getRigobotJSON(breathecodeToken)
239
239
  if (!rigobotJson) {
240
- return false;
240
+ return false
241
241
  }
242
- onValidRigoToken(rigobotJson.key);
243
- return true;
244
- };
242
+ onValidRigoToken(rigobotJson.key)
243
+ return true
244
+ }
245
245
 
246
246
  export function extractImagesFromMarkdown(markdown: string) {
247
- const imageRegex = /!\[([^\]]*)]\(([^)]+)\)/g;
248
- const images = [];
249
- let match;
247
+ const imageRegex = /!\[([^\]]*)]\(([^)]+)\)/g
248
+ const images = []
249
+ let match
250
250
 
251
251
  while ((match = imageRegex.exec(markdown)) !== null) {
252
- const altText = match[1];
253
- const url = match[2];
254
- images.push({ alt: altText, url: url });
252
+ const altText = match[1]
253
+ const url = match[2]
254
+ images.push({ alt: altText, url: url })
255
255
  }
256
256
 
257
- return images;
257
+ return images
258
258
  }
259
259
 
260
260
  export function getFilenameFromUrl(url: string): string {
261
261
  try {
262
262
  // 1) Use the URL constructor to strip off protocol/host/search/hash
263
- const pathname = new URL(url, location.href).pathname;
263
+ const pathname = new URL(url, location.href).pathname
264
264
  // 2) Grab everything after the last “/”
265
- return pathname.substring(pathname.lastIndexOf("/") + 1);
265
+ return pathname.substring(pathname.lastIndexOf("/") + 1)
266
266
  } catch {
267
267
  // Fallback for non-absolute URLs or invalid inputs
268
- const clean = url.split("?")[0].split("#")[0];
269
- return clean.substring(clean.lastIndexOf("/") + 1);
268
+ const clean = url.split("?")[0].split("#")[0]
269
+ return clean.substring(clean.lastIndexOf("/") + 1)
270
270
  }
271
271
  }
272
272
 
273
273
  export const makeCallbackUrl = (slug: string) => {
274
274
  if (DEV_MODE) {
275
- return `https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us120.gitpod.io/v1/learnpack/tools/images/callback?slug=${slug}`;
275
+ return `https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us120.gitpod.io/v1/learnpack/tools/images/callback?slug=${slug}`
276
276
  }
277
- return `${window.location.origin}/api/v1/webhooks/images`;
278
- };
277
+ return `${window.location.origin}/api/v1/webhooks/images`
278
+ }
279
279
 
280
280
  type TGenerateImageParams = {
281
- prompt: string;
282
- };
281
+ prompt: string
282
+ }
283
283
 
284
284
  export const generateImage = async (
285
285
  token: string,
@@ -298,14 +298,14 @@ export const generateImage = async (
298
298
  Authorization: "Token " + token,
299
299
  },
300
300
  }
301
- );
301
+ )
302
302
 
303
- return response.data;
303
+ return response.data
304
304
  } catch (error) {
305
- console.error("Error generating image:", error);
306
- return null;
305
+ console.error("Error generating image:", error)
306
+ return null
307
307
  }
308
- };
308
+ }
309
309
 
310
310
  export const createCourse = async (
311
311
  syllabus: Syllabus,
@@ -323,27 +323,27 @@ export const createCourse = async (
323
323
  "x-rigo-token": token,
324
324
  },
325
325
  }
326
- );
327
- return response.data;
328
- };
326
+ )
327
+ return response.data
328
+ }
329
329
 
330
330
  interface SlugAvailabilityResponse {
331
- slug: string;
332
- available: boolean;
331
+ slug: string
332
+ available: boolean
333
333
  }
334
334
 
335
335
  export const isSlugAvailable = async (slug: string): Promise<boolean> => {
336
336
  try {
337
337
  const url = `${RIGOBOT_HOST}/v1/learnpack/check-slug-availability?slug=${encodeURIComponent(
338
338
  slug
339
- )}`;
340
- const response = await axios.get<SlugAvailabilityResponse>(url);
341
- return response.data.available;
339
+ )}`
340
+ const response = await axios.get<SlugAvailabilityResponse>(url)
341
+ return response.data.available
342
342
  } catch (error) {
343
- console.error("Error checking slug availability:", error);
344
- throw error;
343
+ console.error("Error checking slug availability:", error)
344
+ throw error
345
345
  }
346
- };
346
+ }
347
347
 
348
348
  export const reWriteTitle = async (title: string, token: string) => {
349
349
  // We hav in the parsed the newTitle
@@ -363,25 +363,25 @@ export const reWriteTitle = async (title: string, token: string) => {
363
363
  Authorization: "Token " + token,
364
364
  },
365
365
  }
366
- );
367
- console.log("RESPONSE", response.data);
368
- return response.data.parsed.newTitle;
366
+ )
367
+ console.log("RESPONSE", response.data)
368
+ return response.data.parsed.newTitle
369
369
  } catch (error) {
370
- console.error("Error rewriting title:", error);
370
+ console.error("Error rewriting title:", error)
371
371
  // Return the title as it is with a random number of 4 characters
372
- return `${title} ${Math.random().toString(36).substring(2, 6)}`;
372
+ return `${title} ${Math.random().toString(36).substring(2, 6)}`
373
373
  }
374
- };
374
+ }
375
375
 
376
376
  export async function registerUserWithFormData(
377
377
  firstName: string,
378
378
  lastName: string,
379
379
  email: string
380
380
  ): Promise<any> {
381
- const formData = new FormData();
382
- formData.append("1_first_name", firstName);
383
- formData.append("1_last_name", lastName);
384
- formData.append("1_email", email);
381
+ const formData = new FormData()
382
+ formData.append("1_first_name", firstName)
383
+ formData.append("1_last_name", lastName)
384
+ formData.append("1_email", email)
385
385
 
386
386
  // Puedes modificar este objeto para agregar info real de tracking si la tienes.
387
387
  const conversionArray = [
@@ -402,9 +402,9 @@ export async function registerUserWithFormData(
402
402
  internal_cta_content: "$undefined",
403
403
  internal_cta_campaign: "$undefined",
404
404
  },
405
- ];
405
+ ]
406
406
 
407
- formData.append("0", JSON.stringify(conversionArray));
407
+ formData.append("0", JSON.stringify(conversionArray))
408
408
 
409
409
  try {
410
410
  const response = await axios.post(
@@ -415,56 +415,57 @@ export async function registerUserWithFormData(
415
415
  "Content-Type": "multipart/form-data",
416
416
  },
417
417
  }
418
- );
419
- return response.data;
418
+ )
419
+ return response.data
420
420
  } catch (error: any) {
421
421
  // Manejo de errores básico
422
- console.error(error, "ERROR REGISTERING IN LEARNPACK");
422
+ console.error(error, "ERROR REGISTERING IN LEARNPACK")
423
423
  return {
424
424
  success: false,
425
425
  message: error?.response?.data?.detail || "Registration error",
426
426
  data: error?.response?.data || null,
427
- };
427
+ }
428
428
  }
429
429
  }
430
430
 
431
431
  export const isValidRigoToken = async (rigobotToken: string) => {
432
- const rigoUrl = `${RIGOBOT_HOST}/v1/auth/token/${rigobotToken}`;
433
- const rigoResp = await fetch(rigoUrl);
432
+ const rigoUrl = `${RIGOBOT_HOST}/v1/auth/token/${rigobotToken}`
433
+ const rigoResp = await fetch(rigoUrl)
434
434
  if (!rigoResp.ok) {
435
- return false;
435
+ return false
436
436
  }
437
437
 
438
- return true;
439
- };
438
+ return true
439
+ }
440
440
  export const isValidPublicToken = async (publicToken: string) => {
441
441
  const headers = {
442
442
  "Content-Type": "application/json",
443
443
  Authorization: "Token " + publicToken,
444
- };
445
- const rigoUrl = `${RIGOBOT_HOST}/v1/auth/public/token/validate`;
446
- const rigoResp = await fetch(rigoUrl, { headers });
444
+ }
445
+ const rigoUrl = `${RIGOBOT_HOST}/v1/auth/public/token/validate`
446
+ const rigoResp = await fetch(rigoUrl, { headers })
447
447
  if (!rigoResp.ok) {
448
- return false;
448
+ return false
449
449
  }
450
450
 
451
- return true;
452
- };
451
+ return true
452
+ }
453
453
 
454
454
  export const fixTitleLength = (title: string) => {
455
- const MAX_LENGTH = 49;
456
- let fixed = title.slice(0, MAX_LENGTH);
457
- fixed = fixed.replace(/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g, "");
458
- return fixed;
459
- };
455
+ const MAX_LENGTH = 49
456
+ let fixed = title.slice(0, MAX_LENGTH)
457
+ fixed = fixed.replace(/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g, "")
458
+ return fixed
459
+ }
460
460
 
461
461
  export const getTechnologies = async () => {
462
- const response = await axios.get(`/technologies`);
463
- return response.data;
464
- };
462
+ const response = await axios.get(`/technologies`)
463
+ return response.data
464
+ }
465
465
 
466
466
  export const detectLanguage = (text: string) => {
467
- const lang = franc(text);
468
- if (lang === "spa") return "es";
469
- else return "en";
470
- };
467
+ const lang = franc(text)
468
+ if (lang === "spa") return "es"
469
+ if (lang === "eng") return "en"
470
+ return "en" // fallback to English
471
+ }