@learnpack/learnpack 5.0.67 → 5.0.69

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.
@@ -17,6 +17,15 @@
17
17
  --tw-space-y-reverse: 0;
18
18
  --tw-space-x-reverse: 0;
19
19
  --tw-border-style: solid;
20
+ --tw-gradient-position: initial;
21
+ --tw-gradient-from: #0000;
22
+ --tw-gradient-via: #0000;
23
+ --tw-gradient-to: #0000;
24
+ --tw-gradient-stops: initial;
25
+ --tw-gradient-via-stops: initial;
26
+ --tw-gradient-from-position: 0%;
27
+ --tw-gradient-via-position: 50%;
28
+ --tw-gradient-to-position: 100%;
20
29
  --tw-leading: initial;
21
30
  --tw-font-weight: initial;
22
31
  --tw-shadow: 0 0 #0000;
@@ -346,6 +355,12 @@
346
355
  }
347
356
  @layer components;
348
357
  @layer utilities {
358
+ .pointer-events-auto {
359
+ pointer-events: auto;
360
+ }
361
+ .pointer-events-none {
362
+ pointer-events: none;
363
+ }
349
364
  .absolute {
350
365
  position: absolute;
351
366
  }
@@ -364,15 +379,58 @@
364
379
  .right-2 {
365
380
  right: calc(var(--spacing) * 2);
366
381
  }
382
+ .bottom-0 {
383
+ bottom: calc(var(--spacing) * 0);
384
+ }
367
385
  .bottom-2 {
368
386
  bottom: calc(var(--spacing) * 2);
369
387
  }
388
+ .bottom-3 {
389
+ bottom: calc(var(--spacing) * 3);
390
+ }
370
391
  .bottom-4 {
371
392
  bottom: calc(var(--spacing) * 4);
372
393
  }
394
+ .left-0 {
395
+ left: calc(var(--spacing) * 0);
396
+ }
373
397
  .left-1\/2 {
374
398
  left: 50%;
375
399
  }
400
+ .z-10 {
401
+ z-index: 10;
402
+ }
403
+ .z-20 {
404
+ z-index: 20;
405
+ }
406
+ .container {
407
+ width: 100%;
408
+ }
409
+ @media (min-width: 40rem) {
410
+ .container {
411
+ max-width: 40rem;
412
+ }
413
+ }
414
+ @media (min-width: 48rem) {
415
+ .container {
416
+ max-width: 48rem;
417
+ }
418
+ }
419
+ @media (min-width: 64rem) {
420
+ .container {
421
+ max-width: 64rem;
422
+ }
423
+ }
424
+ @media (min-width: 80rem) {
425
+ .container {
426
+ max-width: 80rem;
427
+ }
428
+ }
429
+ @media (min-width: 96rem) {
430
+ .container {
431
+ max-width: 96rem;
432
+ }
433
+ }
376
434
  .m-0 {
377
435
  margin: calc(var(--spacing) * 0);
378
436
  }
@@ -436,6 +494,9 @@
436
494
  .h-6 {
437
495
  height: calc(var(--spacing) * 6);
438
496
  }
497
+ .h-20 {
498
+ height: calc(var(--spacing) * 20);
499
+ }
439
500
  .h-24 {
440
501
  height: calc(var(--spacing) * 24);
441
502
  }
@@ -445,8 +506,8 @@
445
506
  .h-screen {
446
507
  height: 100vh;
447
508
  }
448
- .max-h-\[60vh\] {
449
- max-height: 60vh;
509
+ .max-h-\[70vh\] {
510
+ max-height: 70vh;
450
511
  }
451
512
  .max-h-\[300px\] {
452
513
  max-height: 300px;
@@ -660,15 +721,34 @@
660
721
  .bg-yellow-50 {
661
722
  background-color: var(--color-yellow-50);
662
723
  }
724
+ .bg-gradient-to-t {
725
+ --tw-gradient-position: to top in oklab;
726
+ background-image: linear-gradient(var(--tw-gradient-stops));
727
+ }
728
+ .from-white {
729
+ --tw-gradient-from: var(--color-white);
730
+ --tw-gradient-stops: var(
731
+ --tw-gradient-via-stops,
732
+ var(--tw-gradient-position),
733
+ var(--tw-gradient-from) var(--tw-gradient-from-position),
734
+ var(--tw-gradient-to) var(--tw-gradient-to-position)
735
+ );
736
+ }
737
+ .to-transparent {
738
+ --tw-gradient-to: transparent;
739
+ --tw-gradient-stops: var(
740
+ --tw-gradient-via-stops,
741
+ var(--tw-gradient-position),
742
+ var(--tw-gradient-from) var(--tw-gradient-from-position),
743
+ var(--tw-gradient-to) var(--tw-gradient-to-position)
744
+ );
745
+ }
663
746
  .p-2 {
664
747
  padding: calc(var(--spacing) * 2);
665
748
  }
666
749
  .p-3 {
667
750
  padding: calc(var(--spacing) * 3);
668
751
  }
669
- .p-4 {
670
- padding: calc(var(--spacing) * 4);
671
- }
672
752
  .p-6 {
673
753
  padding: calc(var(--spacing) * 6);
674
754
  }
@@ -785,6 +865,12 @@
785
865
  .opacity-30 {
786
866
  opacity: 0.3;
787
867
  }
868
+ .shadow {
869
+ --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a),
870
+ 0 1px 2px -1px var(--tw-shadow-color, #0000001a);
871
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
872
+ var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
873
+ }
788
874
  .shadow-md {
789
875
  --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, #0000001a),
790
876
  0 2px 4px -2px var(--tw-shadow-color, #0000001a);
@@ -806,6 +892,9 @@
806
892
  transition-duration: var(--tw-duration, var(--default-transition-duration));
807
893
  }
808
894
  @media (hover: hover) {
895
+ .hover\:bg-blue-50:hover {
896
+ background-color: var(--color-blue-50);
897
+ }
809
898
  .hover\:bg-blue-200:hover {
810
899
  background-color: var(--color-blue-200);
811
900
  }
@@ -998,6 +1087,48 @@ h1 {
998
1087
  inherits: false;
999
1088
  initial-value: solid;
1000
1089
  }
1090
+ @property --tw-gradient-position {
1091
+ syntax: "*";
1092
+ inherits: false;
1093
+ }
1094
+ @property --tw-gradient-from {
1095
+ syntax: "<color>";
1096
+ inherits: false;
1097
+ initial-value: #0000;
1098
+ }
1099
+ @property --tw-gradient-via {
1100
+ syntax: "<color>";
1101
+ inherits: false;
1102
+ initial-value: #0000;
1103
+ }
1104
+ @property --tw-gradient-to {
1105
+ syntax: "<color>";
1106
+ inherits: false;
1107
+ initial-value: #0000;
1108
+ }
1109
+ @property --tw-gradient-stops {
1110
+ syntax: "*";
1111
+ inherits: false;
1112
+ }
1113
+ @property --tw-gradient-via-stops {
1114
+ syntax: "*";
1115
+ inherits: false;
1116
+ }
1117
+ @property --tw-gradient-from-position {
1118
+ syntax: "<length-percentage>";
1119
+ inherits: false;
1120
+ initial-value: 0%;
1121
+ }
1122
+ @property --tw-gradient-via-position {
1123
+ syntax: "<length-percentage>";
1124
+ inherits: false;
1125
+ initial-value: 50%;
1126
+ }
1127
+ @property --tw-gradient-to-position {
1128
+ syntax: "<length-percentage>";
1129
+ inherits: false;
1130
+ initial-value: 100%;
1131
+ }
1001
1132
  @property --tw-leading {
1002
1133
  syntax: "*";
1003
1134
  inherits: false;
@@ -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-tZYXMzIW.js"></script>
14
- <link rel="stylesheet" crossorigin href="/creator/assets/index-t6ma_gVm.css">
13
+ <script type="module" crossorigin src="/creator/assets/index-C7bLE5wU.js"></script>
14
+ <link rel="stylesheet" crossorigin href="/creator/assets/index-C_Rp91QE.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
package/src/utils/api.ts CHANGED
@@ -351,11 +351,11 @@ export interface TAcademy {
351
351
 
352
352
  const with_crud_asset_roles = new Set([
353
353
  "content_writer",
354
- "growth_manager",
355
354
  "syllabus_coordinator",
356
355
  "country_manager",
357
- "community_manager",
358
356
  "carrer_support_head",
357
+ "admin",
358
+ "student",
359
359
  ])
360
360
 
361
361
  export const listUserAcademies = async (
@@ -1,281 +0,0 @@
1
- import React, { useRef, useState } from "react"
2
- import { SVGS } from "../assets/svgs"
3
- import { useShallow } from "zustand/react/shallow"
4
- import useStore from "../utils/store"
5
- import { interactiveCreation } from "../utils/rigo"
6
- import { parseLesson, uploadFileToBucket } from "../utils/lib"
7
- import {
8
- createLearnJson,
9
- processExercise,
10
- slugify,
11
- randomUUID,
12
- } from "../utils/creatorUtils"
13
-
14
- import Loader from "./Loader"
15
- import { Message, TMessage } from "./Message"
16
- import { LessonItem, Lesson } from "./LessonItem"
17
- import FileUploader from "./FileUploader"
18
-
19
- // types.ts
20
-
21
- const SyllabusEditor: React.FC = () => {
22
- const inputRef = useRef<HTMLTextAreaElement>(null)
23
- // const navigate = useNavigate()
24
- const [messages, setMessages] = useState<TMessage[]>([])
25
- const [isGenerating, setIsGenerating] = useState(false)
26
- const prevLessons = useRef<Lesson[]>([])
27
- const { syllabus, setSyllabus, auth } = useStore(
28
- useShallow((state) => ({
29
- syllabus: state.syllabus,
30
- setSyllabus: state.setSyllabus,
31
- auth: state.auth,
32
- }))
33
- )
34
-
35
- const handleRemove = (id: string) => {
36
- setSyllabus({
37
- ...syllabus,
38
- lessons: syllabus.lessons.filter((lesson) => lesson.id !== id),
39
- })
40
- }
41
-
42
- const handleChange = (id: string, newTitle: string) => {
43
- setSyllabus({
44
- ...syllabus,
45
- lessons: syllabus.lessons.map((lesson) =>
46
- lesson.id === id ? { ...lesson, title: newTitle } : lesson
47
- ),
48
- })
49
- }
50
-
51
- const addLessonAfter = (index: number, id: string) => {
52
- const newLesson: Lesson = {
53
- id: (parseFloat(id) + 0.1).toString(),
54
- title: "Hello World",
55
- type: "READ",
56
- duration: 2,
57
- description: "Hello World",
58
- }
59
- const updated = [...syllabus.lessons]
60
- updated.splice(index + 1, 0, newLesson)
61
-
62
- setSyllabus({
63
- lessons: updated,
64
- })
65
- }
66
-
67
- const sendPrompt = async (prompt: string) => {
68
- setMessages([
69
- ...messages,
70
- { type: "user", content: prompt },
71
- { type: "assistant", content: "" },
72
- ])
73
- prevLessons.current = syllabus.lessons
74
- const res = await interactiveCreation(auth.rigoToken, {
75
- courseInfo: JSON.stringify(syllabus),
76
- prevInteractions:
77
- messages
78
- .map((message) => `${message.type}: ${message.content}`)
79
- .join("\n") + `\nUSER: ${prompt}`,
80
- })
81
- console.log(res, "RES")
82
- const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
83
- parseLesson(step)
84
- )
85
- setSyllabus({
86
- ...syllabus,
87
- lessons: lessons,
88
- courseInfo: {
89
- ...syllabus.courseInfo,
90
- title: res.parsed.title || syllabus.courseInfo.title,
91
- },
92
- })
93
- setMessages((prev) => {
94
- const newMessages = [...prev]
95
- newMessages[newMessages.length - 1].content = res.parsed.aiMessage
96
- return newMessages
97
- })
98
- }
99
-
100
- const handleSubmit = async () => {
101
- setIsGenerating(true)
102
-
103
- const lessonsPromises = syllabus.lessons.map((lesson) =>
104
- processExercise(
105
- auth.rigoToken,
106
- syllabus.lessons,
107
- JSON.stringify(syllabus.courseInfo),
108
- lesson,
109
- "courses/" +
110
- slugify(syllabus.courseInfo.title || randomUUID()) +
111
- "/exercises"
112
- )
113
- )
114
- await Promise.all(lessonsPromises)
115
-
116
- const learnJson = createLearnJson(syllabus.courseInfo)
117
- await uploadFileToBucket(
118
- JSON.stringify(learnJson),
119
- "courses/" +
120
- slugify(syllabus.courseInfo.title || randomUUID()) +
121
- "/learn.json"
122
- )
123
- setIsGenerating(false)
124
-
125
- window.location.href = `/?slug=${slugify(
126
- syllabus.courseInfo.title || "exercises"
127
- )}&token=${auth.bcToken}`
128
- }
129
-
130
- return isGenerating ? (
131
- <Loader
132
- listeningTo="course-generation"
133
- icon={SVGS.rigoSoftBlue}
134
- initialBuffer="🚀 Starting course generation..."
135
- text="Learnpack is setting up your tutorial.
136
- It may take a moment..."
137
- />
138
- ) : (
139
- <div className="flex w-full bg-white rounded-md shadow-md overflow-hidden h-screen ">
140
- {/* Sidebar */}
141
- <div className="w-1/3 p-6 text-sm text-gray-700 border-r bg-learnpack-blue h-screen overflow-y-auto scrollbar-hide relative">
142
- <div className="p-4 rounded-md bg-white shadow-sm">
143
- <p>We generated this syllabus based on your answers.</p>
144
- <p className="mt-2">If you're satisfied, type "OK" in the chat.</p>
145
- <p className="mt-2">If not, use the chat to give more context.</p>
146
- </div>
147
-
148
- <div className="mt-10 space-y-2 pb-16">
149
- {messages.map((message, index) => (
150
- <Message
151
- key={index}
152
- type={message.type}
153
- content={message.content}
154
- />
155
- ))}
156
- </div>
157
-
158
- <div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 w-[90%] border rounded-md bg-white text-gray-400 resize-none h-24 ">
159
- <textarea
160
- ref={inputRef}
161
- style={{ resize: "none" }}
162
- className="w-full h-full p-2"
163
- placeholder="How can Learnpack help you?"
164
- autoFocus
165
- onKeyUp={(e) => {
166
- if (e.key === "Enter" && !e.shiftKey) {
167
- e.preventDefault()
168
- sendPrompt(inputRef.current?.value || "")
169
- inputRef.current!.value = ""
170
- }
171
- }}
172
- />
173
- <div className="absolute bottom-2 right-2 flex gap-1 items-center">
174
- <div className="relative inline-block">
175
- {syllabus.uploadedFiles?.length > 0 && (
176
- <span
177
- className="absolute -top-1 right-0 inline-flex items-center justify-center w-3 h-3 text-[10px] text-white bg-blue-200 rounded-full hover:bg-red-300 cursor-pointer"
178
- title="Remove uploaded files"
179
- onClick={() => {
180
- setSyllabus({
181
- ...syllabus,
182
- uploadedFiles: [],
183
- })
184
- }}
185
- >
186
- {syllabus.uploadedFiles?.length}
187
- </span>
188
- )}
189
- <FileUploader
190
- onResult={(res) => {
191
- setSyllabus({
192
- ...syllabus,
193
- uploadedFiles: [...syllabus.uploadedFiles, ...res],
194
- })
195
- }}
196
- />
197
- </div>
198
-
199
- <button
200
- className="cursor-pointer blue-on-hover flex items-center justify-center w-6 h-6"
201
- onClick={() => sendPrompt(inputRef.current?.value || "")}
202
- >
203
- {SVGS.send}
204
- </button>
205
- </div>
206
- </div>
207
- </div>
208
-
209
- {/* Editor */}
210
- <div className="w-2/3 p-8 space-y-6">
211
- <div>
212
- <h2 className="text-lg font-semibold">
213
- {messages.filter(
214
- (m) => m.type === "assistant" && m.content.length > 0
215
- ).length === 0
216
- ? "I've created a detailed structure for your course."
217
- : "I've updated the structure based on your feedback."}
218
- </h2>
219
- <p className="text-sm text-gray-600">
220
- {messages.filter(
221
- (m) => m.type === "assistant" && m.content.length > 0
222
- ).length === 0
223
- ? `It includes a mix of reading, coding exercises, and quizzes. Give
224
- it a look and let me know if it aligns with your expectations or if
225
- there are any changes you'd like to make.`
226
- : "Based on your input, here is the new syllabus, updates are highlighted in yellow"}
227
- </p>
228
- <h3 className="text-sm text-gray-600 mt-2 font-bold">
229
- {syllabus.courseInfo.title}
230
- </h3>
231
- </div>
232
-
233
- {/* Lessons */}
234
- <div className="space-y-3 overflow-y-auto max-h-[60vh] pr-2 scrollbar-hide">
235
- {syllabus.lessons.map((lesson, index) => (
236
- <div key={lesson.id}>
237
- <LessonItem
238
- key={lesson.id}
239
- index={lesson.id}
240
- lesson={lesson}
241
- onChange={handleChange}
242
- onRemove={handleRemove}
243
- isNew={
244
- prevLessons.current.length > 0 &&
245
- !prevLessons.current.some(
246
- (l) =>
247
- l.id === lesson.id &&
248
- l.title === lesson.title &&
249
- l.type === lesson.type
250
- )
251
- }
252
- />
253
- <div className="relative h-6">
254
- <div className="absolute left-1/2 -translate-x-1/2 -top-3">
255
- <button
256
- onClick={() => addLessonAfter(index, lesson.id)}
257
- className="w-6 h-6 flex items-center justify-center bg-blue-100 text-blue-600 rounded hover:bg-blue-200 shadow-sm text-sm font-semibold cursor-pointer"
258
- >
259
- +
260
- </button>
261
- </div>
262
- </div>
263
- </div>
264
- ))}
265
- </div>
266
-
267
- <div className="flex justify-end mt-6">
268
- <button
269
- onClick={handleSubmit}
270
- className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 cursor-pointer flex items-center gap-2"
271
- >
272
- <span>I'm ready. Create the course for me! </span>
273
- {SVGS.rigoSoftBlue}
274
- </button>
275
- </div>
276
- </div>
277
- </div>
278
- )
279
- }
280
-
281
- export default SyllabusEditor