@learnpack/learnpack 5.0.68 → 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-B01XTAAq.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,300 +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 {
7
- parseLesson,
8
- uploadFileToBucket,
9
- useConsumableCall,
10
- } from "../utils/lib"
11
- import {
12
- createLearnJson,
13
- processExercise,
14
- slugify,
15
- randomUUID,
16
- } from "../utils/creatorUtils"
17
-
18
- import Loader from "./Loader"
19
- import { Message, TMessage } from "./Message"
20
- import { LessonItem, Lesson } from "./LessonItem"
21
- import FileUploader from "./FileUploader"
22
- import { ConsumablesManager } from "./ConsumablesManager"
23
- import toast from "react-hot-toast"
24
-
25
- const GenerateButton = ({ handleSubmit }: { handleSubmit: () => void }) => {
26
- const auth = useStore((state) => state.auth)
27
- return (
28
- <div className="flex justify-end mt-6">
29
- <button
30
- onClick={async () => {
31
- const success = await useConsumableCall(auth.bcToken, "ai-generation")
32
- if (success) {
33
- handleSubmit()
34
- } else {
35
- toast.error("You don't have enough credits to generate a course.")
36
- }
37
- }}
38
- className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 cursor-pointer flex items-center gap-2"
39
- >
40
- <span>I'm ready. Create the course for me!</span>
41
- {SVGS.rigoSoftBlue}
42
- </button>
43
- </div>
44
- )
45
- }
46
-
47
- const SyllabusEditor: React.FC = () => {
48
- const inputRef = useRef<HTMLTextAreaElement>(null)
49
- // const navigate = useNavigate()
50
- const [messages, setMessages] = useState<TMessage[]>([])
51
- const [isGenerating, setIsGenerating] = useState(false)
52
- const prevLessons = useRef<Lesson[]>([])
53
- const { syllabus, setSyllabus, auth } = useStore(
54
- useShallow((state) => ({
55
- syllabus: state.syllabus,
56
- setSyllabus: state.setSyllabus,
57
- auth: state.auth,
58
- }))
59
- )
60
-
61
- const handleRemove = (id: string) => {
62
- setSyllabus({
63
- ...syllabus,
64
- lessons: syllabus.lessons.filter((lesson) => lesson.id !== id),
65
- })
66
- }
67
-
68
- const handleChange = (id: string, newTitle: string) => {
69
- setSyllabus({
70
- ...syllabus,
71
- lessons: syllabus.lessons.map((lesson) =>
72
- lesson.id === id ? { ...lesson, title: newTitle } : lesson
73
- ),
74
- })
75
- }
76
-
77
- const addLessonAfter = (index: number, id: string) => {
78
- const newLesson: Lesson = {
79
- id: (parseFloat(id) + 0.1).toString(),
80
- title: "Hello World",
81
- type: "READ",
82
- duration: 2,
83
- description: "Hello World",
84
- }
85
- const updated = [...syllabus.lessons]
86
- updated.splice(index + 1, 0, newLesson)
87
-
88
- setSyllabus({
89
- lessons: updated,
90
- })
91
- }
92
-
93
- const sendPrompt = async (prompt: string) => {
94
- setMessages([
95
- ...messages,
96
- { type: "user", content: prompt },
97
- { type: "assistant", content: "" },
98
- ])
99
- prevLessons.current = syllabus.lessons
100
- const res = await interactiveCreation(auth.rigoToken, {
101
- courseInfo: JSON.stringify(syllabus),
102
- prevInteractions:
103
- messages
104
- .map((message) => `${message.type}: ${message.content}`)
105
- .join("\n") + `\nUSER: ${prompt}`,
106
- })
107
- console.log(res, "RES")
108
- const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
109
- parseLesson(step)
110
- )
111
- setSyllabus({
112
- ...syllabus,
113
- lessons: lessons,
114
- courseInfo: {
115
- ...syllabus.courseInfo,
116
- title: res.parsed.title || syllabus.courseInfo.title,
117
- },
118
- })
119
- setMessages((prev) => {
120
- const newMessages = [...prev]
121
- newMessages[newMessages.length - 1].content = res.parsed.aiMessage
122
- return newMessages
123
- })
124
- }
125
-
126
- const handleSubmit = async () => {
127
- setIsGenerating(true)
128
-
129
- const lessonsPromises = syllabus.lessons.map((lesson) =>
130
- processExercise(
131
- auth.rigoToken,
132
- syllabus.lessons,
133
- JSON.stringify(syllabus.courseInfo),
134
- lesson,
135
- "courses/" +
136
- slugify(syllabus.courseInfo.title || randomUUID()) +
137
- "/exercises"
138
- )
139
- )
140
- await Promise.all(lessonsPromises)
141
-
142
- const learnJson = createLearnJson(syllabus.courseInfo)
143
- await uploadFileToBucket(
144
- JSON.stringify(learnJson),
145
- "courses/" +
146
- slugify(syllabus.courseInfo.title || randomUUID()) +
147
- "/learn.json"
148
- )
149
- setIsGenerating(false)
150
-
151
- window.location.href = `/?slug=${slugify(
152
- syllabus.courseInfo.title || "exercises"
153
- )}&token=${auth.bcToken}`
154
- }
155
-
156
- return isGenerating ? (
157
- <Loader
158
- listeningTo="course-generation"
159
- icon={SVGS.rigoSoftBlue}
160
- initialBuffer="🚀 Starting course generation..."
161
- text="Learnpack is setting up your tutorial.
162
- It may take a moment..."
163
- />
164
- ) : (
165
- <div className="flex w-full bg-white rounded-md shadow-md overflow-hidden h-screen ">
166
- <ConsumablesManager />
167
- {/* Sidebar */}
168
- <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">
169
- <div className="p-4 rounded-md bg-white shadow-sm">
170
- <p>We generated this syllabus based on your answers.</p>
171
- <p className="mt-2">If you're satisfied, type "OK" in the chat.</p>
172
- <p className="mt-2">If not, use the chat to give more context.</p>
173
- </div>
174
-
175
- <div className="mt-10 space-y-2 pb-16">
176
- {messages.map((message, index) => (
177
- <Message
178
- key={index}
179
- type={message.type}
180
- content={message.content}
181
- />
182
- ))}
183
- </div>
184
-
185
- <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 ">
186
- <textarea
187
- ref={inputRef}
188
- style={{ resize: "none" }}
189
- className="w-full h-full p-2"
190
- placeholder="How can Learnpack help you?"
191
- autoFocus
192
- onKeyUp={(e) => {
193
- if (e.key === "Enter" && !e.shiftKey) {
194
- e.preventDefault()
195
- sendPrompt(inputRef.current?.value || "")
196
- inputRef.current!.value = ""
197
- }
198
- }}
199
- />
200
- <div className="absolute bottom-2 right-2 flex gap-1 items-center">
201
- <div className="relative inline-block">
202
- {syllabus.uploadedFiles?.length > 0 && (
203
- <span
204
- 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"
205
- title="Remove uploaded files"
206
- onClick={() => {
207
- setSyllabus({
208
- ...syllabus,
209
- uploadedFiles: [],
210
- })
211
- }}
212
- >
213
- {syllabus.uploadedFiles?.length}
214
- </span>
215
- )}
216
- <FileUploader
217
- onResult={(res) => {
218
- setSyllabus({
219
- ...syllabus,
220
- uploadedFiles: [...syllabus.uploadedFiles, ...res],
221
- })
222
- }}
223
- />
224
- </div>
225
-
226
- <button
227
- className="cursor-pointer blue-on-hover flex items-center justify-center w-6 h-6"
228
- onClick={() => sendPrompt(inputRef.current?.value || "")}
229
- >
230
- {SVGS.send}
231
- </button>
232
- </div>
233
- </div>
234
- </div>
235
-
236
- {/* Editor */}
237
- <div className="w-2/3 p-8 space-y-6">
238
- <div>
239
- <h2 className="text-lg font-semibold">
240
- {messages.filter(
241
- (m) => m.type === "assistant" && m.content.length > 0
242
- ).length === 0
243
- ? "I've created a detailed structure for your course."
244
- : "I've updated the structure based on your feedback."}
245
- </h2>
246
- <p className="text-sm text-gray-600">
247
- {messages.filter(
248
- (m) => m.type === "assistant" && m.content.length > 0
249
- ).length === 0
250
- ? `It includes a mix of reading, coding exercises, and quizzes. Give
251
- it a look and let me know if it aligns with your expectations or if
252
- there are any changes you'd like to make.`
253
- : "Based on your input, here is the new syllabus, updates are highlighted in yellow"}
254
- </p>
255
- <h3 className="text-sm text-gray-600 mt-2 font-bold">
256
- {syllabus.courseInfo.title}
257
- </h3>
258
- </div>
259
-
260
- {/* Lessons */}
261
- <div className="space-y-3 overflow-y-auto max-h-[60vh] pr-2 scrollbar-hide">
262
- {syllabus.lessons.map((lesson, index) => (
263
- <div key={lesson.id}>
264
- <LessonItem
265
- key={lesson.id}
266
- index={lesson.id}
267
- lesson={lesson}
268
- onChange={handleChange}
269
- onRemove={handleRemove}
270
- isNew={
271
- prevLessons.current.length > 0 &&
272
- !prevLessons.current.some(
273
- (l) =>
274
- l.id === lesson.id &&
275
- l.title === lesson.title &&
276
- l.type === lesson.type
277
- )
278
- }
279
- />
280
- <div className="relative h-6">
281
- <div className="absolute left-1/2 -translate-x-1/2 -top-3">
282
- <button
283
- onClick={() => addLessonAfter(index, lesson.id)}
284
- 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"
285
- >
286
- +
287
- </button>
288
- </div>
289
- </div>
290
- </div>
291
- ))}
292
- </div>
293
-
294
- <GenerateButton handleSubmit={handleSubmit} />
295
- </div>
296
- </div>
297
- )
298
- }
299
-
300
- export default SyllabusEditor