@learnpack/learnpack 5.0.298 → 5.0.300
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.
- package/README.md +409 -409
- package/lib/commands/audit.js +15 -15
- package/lib/commands/breakToken.js +19 -19
- package/lib/commands/clean.js +3 -3
- package/lib/commands/logout.js +3 -3
- package/lib/commands/serve.js +31 -11
- package/lib/creatorDist/assets/{index-D25zkBaN.js → index-DoYRptnk.js} +11875 -11992
- package/lib/creatorDist/index.html +1 -1
- package/lib/managers/config/index.js +77 -77
- package/lib/utils/creatorUtilities.js +14 -14
- package/lib/utils/templates/isolated/exercises/01-hello-world/README.es.md +26 -26
- package/lib/utils/templates/isolated/exercises/01-hello-world/README.md +26 -26
- package/lib/utils/templates/scorm/adlcp_rootv1p2.xsd +110 -110
- package/lib/utils/templates/scorm/config/index.html +209 -209
- package/lib/utils/templates/scorm/ims_xml.xsd +1 -1
- package/lib/utils/templates/scorm/imscp_rootv1p1p2.xsd +345 -345
- package/lib/utils/templates/scorm/imsmanifest.xml +38 -38
- package/lib/utils/templates/scorm/imsmd_rootv1p2p1.xsd +573 -573
- package/package.json +1 -1
- package/src/commands/audit.ts +487 -487
- package/src/commands/breakToken.ts +67 -67
- package/src/commands/clean.ts +30 -30
- package/src/commands/logout.ts +38 -38
- package/src/commands/serve.ts +47 -25
- package/src/commands/start.ts +333 -333
- package/src/commands/translate.ts +123 -123
- package/src/creator/README.md +54 -54
- package/src/creator/package-lock.json +6621 -6621
- package/src/creator/package.json +55 -55
- package/src/creator/src/App.tsx +569 -569
- package/src/creator/src/components/FileUploader.tsx +302 -302
- package/src/creator/src/components/Icon.tsx +18 -18
- package/src/creator/src/components/LessonItem.tsx +152 -152
- package/src/creator/src/components/Login.tsx +259 -259
- package/src/creator/src/components/syllabus/ContentIndex.tsx +323 -323
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +337 -337
- package/src/creator/src/i18n.ts +28 -28
- package/src/creator/src/locales/en.json +127 -127
- package/src/creator/src/locales/es.json +127 -127
- package/src/creator/src/utils/configTypes.ts +122 -122
- package/src/creator/src/utils/constants.ts +13 -13
- package/src/creator/src/utils/creatorUtils.ts +46 -46
- package/src/creator/src/utils/eventBus.ts +2 -2
- package/src/creator/src/utils/socket.ts +61 -61
- package/src/creator/src/utils/store.ts +222 -222
- package/src/creator/src/vite-env.d.ts +1 -1
- package/src/creator/vite.config.ts +13 -13
- package/src/creatorDist/assets/{index-D25zkBaN.js → index-DoYRptnk.js} +11875 -11992
- package/src/creatorDist/index.html +1 -1
- package/src/managers/config/defaults.ts +49 -49
- package/src/managers/config/exercise.ts +364 -364
- package/src/managers/config/index.ts +775 -775
- package/src/managers/file.ts +236 -236
- package/src/managers/server/routes.ts +554 -554
- package/src/managers/telemetry.ts +188 -188
- package/src/models/action.ts +13 -13
- package/src/models/config-manager.ts +28 -28
- package/src/models/config.ts +106 -106
- package/src/models/exercise-obj.ts +30 -30
- package/src/models/session.ts +39 -39
- package/src/models/socket.ts +61 -61
- package/src/models/status.ts +16 -16
- package/src/utils/BaseCommand.ts +56 -56
- package/src/utils/audit.ts +392 -392
- package/src/utils/checkNotInstalled.ts +267 -267
- package/src/utils/convertCreds.js +34 -34
- package/src/utils/creatorUtilities.ts +504 -504
- package/src/utils/export/README.md +178 -178
- package/src/utils/incrementVersion.js +74 -74
- package/src/utils/misc.ts +58 -58
- package/src/utils/sidebarGenerator.ts +195 -195
- package/src/utils/templates/isolated/exercises/01-hello-world/README.es.md +26 -26
- package/src/utils/templates/isolated/exercises/01-hello-world/README.md +26 -26
- package/src/utils/templates/scorm/adlcp_rootv1p2.xsd +110 -110
- package/src/utils/templates/scorm/config/index.html +209 -209
- package/src/utils/templates/scorm/ims_xml.xsd +1 -1
- package/src/utils/templates/scorm/imscp_rootv1p1p2.xsd +345 -345
- package/src/utils/templates/scorm/imsmanifest.xml +38 -38
- package/src/utils/templates/scorm/imsmd_rootv1p2p1.xsd +573 -573
@@ -1,152 +1,152 @@
|
|
1
|
-
// import { useState, useRef } from "react"
|
2
|
-
// import { SVGS } from "../assets/svgs"
|
3
|
-
|
4
|
-
import { useState } from "react"
|
5
|
-
import { useRef } from "react"
|
6
|
-
import { SVGS } from "../assets/svgs"
|
7
|
-
import useStore from "../utils/store"
|
8
|
-
import { Icon } from "./Icon"
|
9
|
-
|
10
|
-
export interface Lesson {
|
11
|
-
id: string
|
12
|
-
uid: string
|
13
|
-
title: string
|
14
|
-
type: "READ" | "CODE" | "QUIZ"
|
15
|
-
description: string
|
16
|
-
duration?: number
|
17
|
-
locked?: boolean
|
18
|
-
}
|
19
|
-
|
20
|
-
const typeToEmoji: Record<string, string> = {
|
21
|
-
read: "📖",
|
22
|
-
code: "💻",
|
23
|
-
quiz: "🧠",
|
24
|
-
}
|
25
|
-
|
26
|
-
interface LessonItemProps {
|
27
|
-
lesson: Lesson
|
28
|
-
isNew: boolean
|
29
|
-
onChange: (uid: string, newTitle: string) => void
|
30
|
-
onRemove: () => void
|
31
|
-
onToggleLock?: (uid: string) => void
|
32
|
-
}
|
33
|
-
|
34
|
-
function cleanFloatString(input: string): string {
|
35
|
-
const num = parseFloat(input)
|
36
|
-
const isInteger = Number.isInteger(num)
|
37
|
-
|
38
|
-
return isInteger
|
39
|
-
? Math.floor(num).toString().padStart(2, "0")
|
40
|
-
: num.toString()
|
41
|
-
}
|
42
|
-
|
43
|
-
function hasDecimalPart(input: string): boolean {
|
44
|
-
const num = parseFloat(input)
|
45
|
-
if (Number.isNaN(num)) return false
|
46
|
-
return num % 1 > 0
|
47
|
-
}
|
48
|
-
|
49
|
-
export const LessonItem: React.FC<LessonItemProps> = ({
|
50
|
-
lesson,
|
51
|
-
onChange,
|
52
|
-
onRemove,
|
53
|
-
onToggleLock,
|
54
|
-
isNew,
|
55
|
-
}) => {
|
56
|
-
const mode = useStore((state) => state.mode)
|
57
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
58
|
-
const [isEditing, setIsEditing] = useState(false)
|
59
|
-
|
60
|
-
return (
|
61
|
-
<div
|
62
|
-
className={`flex items-center space-x-2 relative rounded-md p-3 ${
|
63
|
-
isNew ? "border border-learnpack-blue appear" : "border border-gray-200"
|
64
|
-
} ${hasDecimalPart(lesson.id.toString() || "0") ? "ml-6" : ""}`}
|
65
|
-
>
|
66
|
-
{isNew && <span className="yellow-ball"></span>}
|
67
|
-
|
68
|
-
{mode === "teacher" && (
|
69
|
-
<>
|
70
|
-
<span className="index-circle">{cleanFloatString(lesson.id)}</span>
|
71
|
-
</>
|
72
|
-
)}
|
73
|
-
|
74
|
-
<span className="text-gray-500 text-sm">
|
75
|
-
{typeToEmoji[lesson.type.toLowerCase()]}
|
76
|
-
</span>
|
77
|
-
{isEditing ? (
|
78
|
-
<input
|
79
|
-
defaultValue={lesson.title}
|
80
|
-
ref={inputRef}
|
81
|
-
onBlur={() => {
|
82
|
-
if (inputRef.current) {
|
83
|
-
onChange(lesson.uid, inputRef.current.value)
|
84
|
-
}
|
85
|
-
}}
|
86
|
-
autoFocus
|
87
|
-
className="flex-1 bg-white border border-gray-300 rounded-md px-2 py-1 text-sm"
|
88
|
-
/>
|
89
|
-
) : (
|
90
|
-
<span className="flex-1 text-sm text-gray-800">{lesson.title}</span>
|
91
|
-
)}
|
92
|
-
|
93
|
-
<span className="text-sm text-gray-600 bg-blue-100 px-2 py-1 rounded-full">
|
94
|
-
{lesson.duration} min
|
95
|
-
</span>
|
96
|
-
|
97
|
-
{mode === "teacher" && (
|
98
|
-
<>
|
99
|
-
<button
|
100
|
-
onClick={() => onToggleLock?.(lesson.uid)}
|
101
|
-
className={`cursor-pointer ${
|
102
|
-
lesson.locked
|
103
|
-
? "text-yellow-600 hover:text-yellow-700"
|
104
|
-
: "text-gray-400 hover:text-yellow-600"
|
105
|
-
}`}
|
106
|
-
title={lesson.locked ? "Unlock lesson" : "Lock lesson"}
|
107
|
-
>
|
108
|
-
<Icon
|
109
|
-
name={lesson.locked ? "Lock" : "Unlock"}
|
110
|
-
size={16}
|
111
|
-
/>
|
112
|
-
</button>
|
113
|
-
<button
|
114
|
-
onClick={() => {
|
115
|
-
if (!lesson.locked) {
|
116
|
-
setIsEditing(!isEditing)
|
117
|
-
if (inputRef.current) {
|
118
|
-
onChange(lesson.uid, inputRef.current.value)
|
119
|
-
}
|
120
|
-
}
|
121
|
-
}}
|
122
|
-
className={`${
|
123
|
-
lesson.locked
|
124
|
-
? "text-gray-300 cursor-not-allowed"
|
125
|
-
: "text-gray-500 hover:text-blue-500 cursor-pointer"
|
126
|
-
}`}
|
127
|
-
disabled={lesson.locked}
|
128
|
-
title={lesson.locked ? "Cannot edit locked lesson" : "Edit lesson"}
|
129
|
-
>
|
130
|
-
<Icon name="Edit" size={16} />
|
131
|
-
</button>
|
132
|
-
<button
|
133
|
-
onClick={() => {
|
134
|
-
if (!lesson.locked) {
|
135
|
-
onRemove()
|
136
|
-
}
|
137
|
-
}}
|
138
|
-
className={`${
|
139
|
-
lesson.locked
|
140
|
-
? "text-gray-300 cursor-not-allowed"
|
141
|
-
: "text-red-500 hover:text-red-700 cursor-pointer"
|
142
|
-
}`}
|
143
|
-
disabled={lesson.locked}
|
144
|
-
title={lesson.locked ? "Cannot delete locked lesson" : "Delete lesson"}
|
145
|
-
>
|
146
|
-
{SVGS.trash}
|
147
|
-
</button>
|
148
|
-
</>
|
149
|
-
)}
|
150
|
-
</div>
|
151
|
-
)
|
152
|
-
}
|
1
|
+
// import { useState, useRef } from "react"
|
2
|
+
// import { SVGS } from "../assets/svgs"
|
3
|
+
|
4
|
+
import { useState } from "react"
|
5
|
+
import { useRef } from "react"
|
6
|
+
import { SVGS } from "../assets/svgs"
|
7
|
+
import useStore from "../utils/store"
|
8
|
+
import { Icon } from "./Icon"
|
9
|
+
|
10
|
+
export interface Lesson {
|
11
|
+
id: string
|
12
|
+
uid: string
|
13
|
+
title: string
|
14
|
+
type: "READ" | "CODE" | "QUIZ"
|
15
|
+
description: string
|
16
|
+
duration?: number
|
17
|
+
locked?: boolean
|
18
|
+
}
|
19
|
+
|
20
|
+
const typeToEmoji: Record<string, string> = {
|
21
|
+
read: "📖",
|
22
|
+
code: "💻",
|
23
|
+
quiz: "🧠",
|
24
|
+
}
|
25
|
+
|
26
|
+
interface LessonItemProps {
|
27
|
+
lesson: Lesson
|
28
|
+
isNew: boolean
|
29
|
+
onChange: (uid: string, newTitle: string) => void
|
30
|
+
onRemove: () => void
|
31
|
+
onToggleLock?: (uid: string) => void
|
32
|
+
}
|
33
|
+
|
34
|
+
function cleanFloatString(input: string): string {
|
35
|
+
const num = parseFloat(input)
|
36
|
+
const isInteger = Number.isInteger(num)
|
37
|
+
|
38
|
+
return isInteger
|
39
|
+
? Math.floor(num).toString().padStart(2, "0")
|
40
|
+
: num.toString()
|
41
|
+
}
|
42
|
+
|
43
|
+
function hasDecimalPart(input: string): boolean {
|
44
|
+
const num = parseFloat(input)
|
45
|
+
if (Number.isNaN(num)) return false
|
46
|
+
return num % 1 > 0
|
47
|
+
}
|
48
|
+
|
49
|
+
export const LessonItem: React.FC<LessonItemProps> = ({
|
50
|
+
lesson,
|
51
|
+
onChange,
|
52
|
+
onRemove,
|
53
|
+
onToggleLock,
|
54
|
+
isNew,
|
55
|
+
}) => {
|
56
|
+
const mode = useStore((state) => state.mode)
|
57
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
58
|
+
const [isEditing, setIsEditing] = useState(false)
|
59
|
+
|
60
|
+
return (
|
61
|
+
<div
|
62
|
+
className={`flex items-center space-x-2 relative rounded-md p-3 ${
|
63
|
+
isNew ? "border border-learnpack-blue appear" : "border border-gray-200"
|
64
|
+
} ${hasDecimalPart(lesson.id.toString() || "0") ? "ml-6" : ""}`}
|
65
|
+
>
|
66
|
+
{isNew && <span className="yellow-ball"></span>}
|
67
|
+
|
68
|
+
{mode === "teacher" && (
|
69
|
+
<>
|
70
|
+
<span className="index-circle">{cleanFloatString(lesson.id)}</span>
|
71
|
+
</>
|
72
|
+
)}
|
73
|
+
|
74
|
+
<span className="text-gray-500 text-sm">
|
75
|
+
{typeToEmoji[lesson.type.toLowerCase()]}
|
76
|
+
</span>
|
77
|
+
{isEditing ? (
|
78
|
+
<input
|
79
|
+
defaultValue={lesson.title}
|
80
|
+
ref={inputRef}
|
81
|
+
onBlur={() => {
|
82
|
+
if (inputRef.current) {
|
83
|
+
onChange(lesson.uid, inputRef.current.value)
|
84
|
+
}
|
85
|
+
}}
|
86
|
+
autoFocus
|
87
|
+
className="flex-1 bg-white border border-gray-300 rounded-md px-2 py-1 text-sm"
|
88
|
+
/>
|
89
|
+
) : (
|
90
|
+
<span className="flex-1 text-sm text-gray-800">{lesson.title}</span>
|
91
|
+
)}
|
92
|
+
|
93
|
+
<span className="text-sm text-gray-600 bg-blue-100 px-2 py-1 rounded-full">
|
94
|
+
{lesson.duration} min
|
95
|
+
</span>
|
96
|
+
|
97
|
+
{mode === "teacher" && (
|
98
|
+
<>
|
99
|
+
<button
|
100
|
+
onClick={() => onToggleLock?.(lesson.uid)}
|
101
|
+
className={`cursor-pointer ${
|
102
|
+
lesson.locked
|
103
|
+
? "text-yellow-600 hover:text-yellow-700"
|
104
|
+
: "text-gray-400 hover:text-yellow-600"
|
105
|
+
}`}
|
106
|
+
title={lesson.locked ? "Unlock lesson" : "Lock lesson"}
|
107
|
+
>
|
108
|
+
<Icon
|
109
|
+
name={lesson.locked ? "Lock" : "Unlock"}
|
110
|
+
size={16}
|
111
|
+
/>
|
112
|
+
</button>
|
113
|
+
<button
|
114
|
+
onClick={() => {
|
115
|
+
if (!lesson.locked) {
|
116
|
+
setIsEditing(!isEditing)
|
117
|
+
if (inputRef.current) {
|
118
|
+
onChange(lesson.uid, inputRef.current.value)
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}}
|
122
|
+
className={`${
|
123
|
+
lesson.locked
|
124
|
+
? "text-gray-300 cursor-not-allowed"
|
125
|
+
: "text-gray-500 hover:text-blue-500 cursor-pointer"
|
126
|
+
}`}
|
127
|
+
disabled={lesson.locked}
|
128
|
+
title={lesson.locked ? "Cannot edit locked lesson" : "Edit lesson"}
|
129
|
+
>
|
130
|
+
<Icon name="Edit" size={16} />
|
131
|
+
</button>
|
132
|
+
<button
|
133
|
+
onClick={() => {
|
134
|
+
if (!lesson.locked) {
|
135
|
+
onRemove()
|
136
|
+
}
|
137
|
+
}}
|
138
|
+
className={`${
|
139
|
+
lesson.locked
|
140
|
+
? "text-gray-300 cursor-not-allowed"
|
141
|
+
: "text-red-500 hover:text-red-700 cursor-pointer"
|
142
|
+
}`}
|
143
|
+
disabled={lesson.locked}
|
144
|
+
title={lesson.locked ? "Cannot delete locked lesson" : "Delete lesson"}
|
145
|
+
>
|
146
|
+
{SVGS.trash}
|
147
|
+
</button>
|
148
|
+
</>
|
149
|
+
)}
|
150
|
+
</div>
|
151
|
+
)
|
152
|
+
}
|