@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/lib/utils/api.js CHANGED
@@ -274,11 +274,11 @@ const getConsumable = async (token, consumableSlug = "ai-generation") => {
274
274
  exports.getConsumable = getConsumable;
275
275
  const with_crud_asset_roles = new Set([
276
276
  "content_writer",
277
- "growth_manager",
278
277
  "syllabus_coordinator",
279
278
  "country_manager",
280
- "community_manager",
281
279
  "carrer_support_head",
280
+ "admin",
281
+ "student",
282
282
  ]);
283
283
  const listUserAcademies = async (breathecodeToken) => {
284
284
  const url = "https://breathecode.herokuapp.com/v1/auth/user/me";
@@ -1 +1 @@
1
- {"version":"5.0.67","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.69","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.67",
4
+ "version": "5.0.69",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -223,4 +223,44 @@ export const SVGS = {
223
223
  />
224
224
  </svg>
225
225
  ),
226
+ downArrow: (
227
+ <svg
228
+ width="7"
229
+ height="6"
230
+ viewBox="0 0 7 6"
231
+ fill="none"
232
+ xmlns="http://www.w3.org/2000/svg"
233
+ >
234
+ <path
235
+ fillRule="evenodd"
236
+ clipRule="evenodd"
237
+ d="M0.100496 1.0705C0.22866 0.950272 0.430019 0.956708 0.550243 1.08487L3.5 4.2294L6.44976 1.08487C6.56998 0.956708 6.77134 0.950272 6.8995 1.0705C7.02767 1.19072 7.0341 1.39208 6.91388 1.52024L3.73206 4.91216C3.67191 4.97628 3.58792 5.01266 3.5 5.01266C3.41208 5.01266 3.32809 4.97628 3.26794 4.91216L0.086122 1.52024C-0.0341029 1.39208 -0.0276674 1.19072 0.100496 1.0705Z"
238
+ fill="#0084FF"
239
+ />
240
+ </svg>
241
+ ),
242
+ bottom: (
243
+ <svg
244
+ width="12"
245
+ height="12"
246
+ viewBox="0 0 24 24"
247
+ fill="none"
248
+ xmlns="http://www.w3.org/2000/svg"
249
+ >
250
+ <path
251
+ d="M11.9297 2V22"
252
+ stroke="#0084FF"
253
+ strokeWidth="1"
254
+ strokeLinecap="round"
255
+ strokeLinejoin="round"
256
+ />
257
+ <path
258
+ d="M19 16L14 21.1599C13.7437 21.4336 13.434 21.6519 13.0899 21.801C12.7459 21.9502 12.375 22.0271 12 22.0271C11.625 22.0271 11.2541 21.9502 10.9101 21.801C10.566 21.6519 10.2563 21.4336 10 21.1599L5 16"
259
+ stroke="#0084FF"
260
+ strokeWidth="1"
261
+ strokeLinecap="round"
262
+ strokeLinejoin="round"
263
+ />
264
+ </svg>
265
+ ),
226
266
  }
@@ -0,0 +1,21 @@
1
+ import { useEffect } from "react"
2
+ import { getConsumables, parseConsumables } from "../utils/lib"
3
+ import useStore from "../utils/store"
4
+
5
+ export const ConsumablesManager = () => {
6
+ const auth = useStore((state) => state.auth)
7
+ const setConsumables = useStore((state) => state.setConsumables)
8
+
9
+ useEffect(() => {
10
+ if (auth.bcToken) {
11
+ fetchConsumables()
12
+ }
13
+ }, [auth])
14
+
15
+ const fetchConsumables = async () => {
16
+ const consumables = await getConsumables(auth.bcToken)
17
+ setConsumables(parseConsumables(consumables.voids))
18
+ }
19
+
20
+ return <></>
21
+ }
@@ -0,0 +1,167 @@
1
+ import { useEffect, useState, useRef } from "react"
2
+ import useStore from "../../utils/store"
3
+ import { Lesson, LessonItem } from "../LessonItem"
4
+ import { SVGS } from "../../assets/svgs"
5
+
6
+ export const GenerateButton = ({
7
+ handleSubmit,
8
+ }: {
9
+ handleSubmit: () => void
10
+ }) => {
11
+ return (
12
+ <div className="flex justify-end mt-6">
13
+ <button
14
+ onClick={async () => {
15
+ handleSubmit()
16
+ }}
17
+ className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 cursor-pointer flex items-center gap-2"
18
+ >
19
+ <span>I'm ready. Create the course for me!</span>
20
+ {SVGS.rigoSoftBlue}
21
+ </button>
22
+ </div>
23
+ )
24
+ }
25
+ export const ContentIndex = ({
26
+ prevLessons,
27
+ handleSubmit,
28
+ }: {
29
+ prevLessons?: Lesson[]
30
+ handleSubmit: () => void
31
+ }) => {
32
+ const syllabus = useStore((state) => state.syllabus)
33
+ const setSyllabus = useStore((state) => state.setSyllabus)
34
+ const containerRef = useRef<HTMLDivElement>(null)
35
+ const [showScrollHint, setShowScrollHint] = useState(false)
36
+
37
+ const handleRemove = (id: string) => {
38
+ setSyllabus({
39
+ ...syllabus,
40
+ lessons: syllabus.lessons.filter((lesson) => lesson.id !== id),
41
+ })
42
+ }
43
+
44
+ const handleChange = (id: string, newTitle: string) => {
45
+ setSyllabus({
46
+ ...syllabus,
47
+ lessons: syllabus.lessons.map((lesson) =>
48
+ lesson.id === id ? { ...lesson, title: newTitle } : lesson
49
+ ),
50
+ })
51
+ }
52
+
53
+ const addLessonAfter = (index: number, id: string) => {
54
+ const newLesson: Lesson = {
55
+ id: (parseFloat(id) + 0.1).toString(),
56
+ title: "Hello World",
57
+ type: "READ",
58
+ duration: 2,
59
+ description: "Hello World",
60
+ }
61
+ const updated = [...syllabus.lessons]
62
+ updated.splice(index + 1, 0, newLesson)
63
+
64
+ setSyllabus({
65
+ lessons: updated,
66
+ })
67
+ }
68
+
69
+ useEffect(() => {
70
+ const container = containerRef.current
71
+
72
+ const checkScroll = () => {
73
+ if (container) {
74
+ const nearBottom =
75
+ container.scrollHeight >
76
+ container.clientHeight + container.scrollTop + 100
77
+
78
+ setShowScrollHint(nearBottom)
79
+ }
80
+ }
81
+
82
+ checkScroll()
83
+ container?.addEventListener("scroll", checkScroll)
84
+ return () => container?.removeEventListener("scroll", checkScroll)
85
+ }, [syllabus.lessons])
86
+
87
+ const scrollToBottom = (target: "bottom" | "continue") => {
88
+ if (target === "continue") {
89
+ const container = containerRef.current
90
+ if (container) {
91
+ const scrollStep = container.clientHeight * 0.8
92
+ container.scrollBy({ top: scrollStep, behavior: "smooth" })
93
+ }
94
+ } else {
95
+ const container = containerRef.current
96
+ if (container) {
97
+ container.scrollTo({ top: container.scrollHeight, behavior: "smooth" })
98
+ }
99
+ }
100
+ }
101
+
102
+ return (
103
+ <div className="relative">
104
+ <div
105
+ ref={containerRef}
106
+ className=" space-y-3 overflow-y-auto max-h-[70vh] pr-2 scrollbar-hide relative"
107
+ >
108
+ {syllabus.lessons.map((lesson, index) => (
109
+ <div key={lesson.id}>
110
+ <LessonItem
111
+ key={lesson.id}
112
+ index={lesson.id}
113
+ lesson={lesson}
114
+ onChange={handleChange}
115
+ onRemove={handleRemove}
116
+ isNew={Boolean(
117
+ prevLessons &&
118
+ prevLessons.length > 0 &&
119
+ !prevLessons.some(
120
+ (l) =>
121
+ l.id === lesson.id &&
122
+ l.title === lesson.title &&
123
+ l.type === lesson.type
124
+ )
125
+ )}
126
+ />
127
+ <div className="relative h-6">
128
+ <div className="absolute left-1/2 -translate-x-1/2 -top-3">
129
+ <button
130
+ onClick={() => addLessonAfter(index, lesson.id)}
131
+ 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"
132
+ >
133
+ +
134
+ </button>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ ))}
139
+ <GenerateButton handleSubmit={handleSubmit} />
140
+ </div>
141
+
142
+ {showScrollHint && (
143
+ <div className="pointer-events-none relative">
144
+ <div className="absolute bottom-0 left-0 w-full h-20 bg-gradient-to-t from-white to-transparent z-10" />
145
+ <div className="absolute bottom-3 left-0 w-full flex justify-center z-20">
146
+ <button
147
+ style={{ color: "#0084FF" }}
148
+ onClick={() => scrollToBottom("continue")}
149
+ className="px-4 py-1 bg-white text-sm rounded shadow hover:bg-blue-50 cursor-pointer pointer-events-auto font-bold flex items-center gap-2"
150
+ >
151
+ Continue scrolling
152
+ {SVGS.downArrow}
153
+ </button>
154
+ <button
155
+ style={{ color: "#0084FF" }}
156
+ onClick={() => scrollToBottom("bottom")}
157
+ className="px-4 py-1 bg-white text-sm rounded shadow hover:bg-blue-50 cursor-pointer pointer-events-auto font-bold flex items-center gap-2"
158
+ >
159
+ Scroll to bottom
160
+ {SVGS.bottom}
161
+ </button>
162
+ </div>
163
+ </div>
164
+ )}
165
+ </div>
166
+ )
167
+ }