@learnpack/learnpack 5.0.196 → 5.0.202
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 +13 -13
- package/lib/commands/serve.d.ts +5 -28
- package/lib/commands/serve.js +45 -20
- package/lib/creatorDist/assets/index-C_HbkVCg.js +38491 -0
- package/lib/creatorDist/index.html +1 -1
- package/lib/models/creator.d.ts +30 -0
- package/lib/models/creator.js +2 -0
- package/lib/utils/creatorUtilities.js +3 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/serve.ts +59 -59
- package/src/creator/package-lock.json +97 -1
- package/src/creator/package.json +3 -0
- package/src/creator/src/App.tsx +91 -32
- package/src/creator/src/components/FileCard.tsx +2 -2
- package/src/creator/src/components/FileUploader.tsx +6 -5
- package/src/creator/src/components/LinkUploader.tsx +3 -1
- package/src/creator/src/components/Login.tsx +33 -27
- package/src/creator/src/components/PurposeSelector.tsx +32 -25
- package/src/creator/src/components/StepWizard.tsx +8 -4
- package/src/creator/src/components/Uploader.tsx +8 -5
- package/src/creator/src/components/syllabus/ContentIndex.tsx +17 -11
- package/src/creator/src/components/syllabus/Sidebar.tsx +7 -7
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +79 -76
- package/src/creator/src/i18n.ts +28 -0
- package/src/creator/src/locales/en.json +110 -0
- package/src/creator/src/locales/es.json +110 -0
- package/src/creator/src/main.tsx +1 -0
- package/src/creator/src/utils/creatorUtils.ts +7 -3
- package/src/creator/src/utils/lib.ts +17 -1
- package/src/creator/src/utils/store.ts +37 -10
- package/src/creatorDist/assets/index-C_HbkVCg.js +38491 -0
- package/src/creatorDist/index.html +1 -1
- package/src/models/creator.ts +32 -0
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +55 -55
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/creatorUtilities.ts +4 -4
- package/lib/creatorDist/assets/index-CXaPa6wN.js +0 -35382
- package/src/creatorDist/assets/index-CXaPa6wN.js +0 -35382
@@ -1,6 +1,7 @@
|
|
1
1
|
import React from "react"
|
2
2
|
import { SVGS } from "../assets/svgs"
|
3
3
|
import { toast } from "react-hot-toast"
|
4
|
+
import { useTranslation } from "react-i18next"
|
4
5
|
|
5
6
|
export type Step = {
|
6
7
|
title: string
|
@@ -27,6 +28,7 @@ const StepWizard: React.FC<Props> = ({
|
|
27
28
|
onFinish,
|
28
29
|
hideLastButton = false,
|
29
30
|
}) => {
|
31
|
+
const { t } = useTranslation()
|
30
32
|
const currentStep = formState.currentStep
|
31
33
|
const index = steps.findIndex((step) => step.slug === currentStep)
|
32
34
|
const totalSteps = steps.length
|
@@ -34,7 +36,7 @@ const StepWizard: React.FC<Props> = ({
|
|
34
36
|
const goNext = () => {
|
35
37
|
const index = steps.findIndex((step) => step.slug === currentStep)
|
36
38
|
if (steps[index].required && !steps[index].isCompleted) {
|
37
|
-
toast.error("
|
39
|
+
toast.error(t("stepWizard.requiredFields"))
|
38
40
|
return
|
39
41
|
}
|
40
42
|
if (index < totalSteps - 1)
|
@@ -55,7 +57,7 @@ const StepWizard: React.FC<Props> = ({
|
|
55
57
|
<div className=" rounded-xl p-8 w-full max-w-xl">
|
56
58
|
<h5 className="text-sm text-blue-400 uppercase mb-2">
|
57
59
|
{steps.find((step) => step.slug === currentStep)?.subtitle ||
|
58
|
-
"
|
60
|
+
t("stepWizard.subtitle")}
|
59
61
|
</h5>
|
60
62
|
<h2 className="text-lg font-medium mb-6">
|
61
63
|
<span className="text-blue-600 mr-1">{index + 1}.</span>
|
@@ -97,7 +99,7 @@ const StepWizard: React.FC<Props> = ({
|
|
97
99
|
index === 0 ? "cursor-not-allowed" : "cursor-pointer"
|
98
100
|
}`}
|
99
101
|
>
|
100
|
-
⬅
|
102
|
+
⬅ {t("stepWizard.back")}
|
101
103
|
</button>
|
102
104
|
{!(hideLastButton && index === totalSteps - 1) && (
|
103
105
|
<button
|
@@ -106,7 +108,9 @@ const StepWizard: React.FC<Props> = ({
|
|
106
108
|
index === totalSteps - 1 ? "bg-blue-600 text-white" : ""
|
107
109
|
}`}
|
108
110
|
>
|
109
|
-
{index === totalSteps - 1
|
111
|
+
{index === totalSteps - 1
|
112
|
+
? t("stepWizard.finish")
|
113
|
+
: t("stepWizard.next")}
|
110
114
|
</button>
|
111
115
|
)}
|
112
116
|
</div>
|
@@ -3,15 +3,17 @@ import { SVGS } from "../assets/svgs"
|
|
3
3
|
import { ContentCard } from "./ContentCard"
|
4
4
|
import LinkUploader from "./LinkUploader"
|
5
5
|
import FileUploader from "./FileUploader"
|
6
|
+
import { useTranslation } from "react-i18next"
|
6
7
|
|
7
8
|
const TextUploader = ({ onFinish }: { onFinish: (text: string) => void }) => {
|
8
9
|
const [text, setText] = useState("")
|
10
|
+
const { t } = useTranslation()
|
9
11
|
|
10
12
|
return (
|
11
13
|
<div className="flex flex-col gap-2 w-full">
|
12
14
|
<textarea
|
13
15
|
className="mt-4 p-2 border rounded w-full bg-white border-heavy-blue"
|
14
|
-
placeholder="
|
16
|
+
placeholder={t("uploader.text.placeholder")}
|
15
17
|
value={text}
|
16
18
|
onChange={(e) => setText(e.target.value)}
|
17
19
|
/>
|
@@ -19,7 +21,7 @@ const TextUploader = ({ onFinish }: { onFinish: (text: string) => void }) => {
|
|
19
21
|
className="mt-4 p-2 border rounded w-full bg-learnpack text-white"
|
20
22
|
onClick={() => onFinish(text)}
|
21
23
|
>
|
22
|
-
|
24
|
+
{t("uploader.text.finish")}
|
23
25
|
</button>
|
24
26
|
</div>
|
25
27
|
)
|
@@ -31,6 +33,7 @@ export const Uploader = ({
|
|
31
33
|
onFinish: (text: string) => void
|
32
34
|
}) => {
|
33
35
|
const [selectedOption, setSelectedOption] = useState<string | null>(null)
|
36
|
+
const { t } = useTranslation()
|
34
37
|
|
35
38
|
const handleSelectOption = (option: string) => {
|
36
39
|
setSelectedOption(option)
|
@@ -41,18 +44,18 @@ export const Uploader = ({
|
|
41
44
|
{!selectedOption && (
|
42
45
|
<>
|
43
46
|
<ContentCard
|
44
|
-
description="
|
47
|
+
description={t("uploader.text.description")}
|
45
48
|
icon={SVGS.contentTable}
|
46
49
|
onClick={() => handleSelectOption("text")}
|
47
50
|
/>
|
48
51
|
<ContentCard
|
49
|
-
description="
|
52
|
+
description={t("uploader.files.description")}
|
50
53
|
icon={SVGS.pdf}
|
51
54
|
onClick={() => handleSelectOption("files")}
|
52
55
|
/>
|
53
56
|
|
54
57
|
<ContentCard
|
55
|
-
description="
|
58
|
+
description={t("uploader.youtube.description")}
|
56
59
|
icon={SVGS.youtube}
|
57
60
|
onClick={() => handleSelectOption("youtube")}
|
58
61
|
/>
|
@@ -8,6 +8,7 @@ import { motion, AnimatePresence } from "framer-motion"
|
|
8
8
|
// import { randomUUID } from "../../utils/creatorUtils"
|
9
9
|
import toast from "react-hot-toast"
|
10
10
|
import { randomUUID } from "../../utils/creatorUtils"
|
11
|
+
import { useTranslation } from "react-i18next"
|
11
12
|
|
12
13
|
const ContentIndexHeader = ({
|
13
14
|
messages,
|
@@ -16,13 +17,16 @@ const ContentIndexHeader = ({
|
|
16
17
|
messages: TMessage[]
|
17
18
|
syllabus: Syllabus
|
18
19
|
}) => {
|
20
|
+
const { t } = useTranslation()
|
19
21
|
const isFirst =
|
20
22
|
messages.filter((m) => m.type === "assistant" && m.content.length > 0)
|
21
23
|
.length === 2
|
22
24
|
|
23
25
|
const headerText = isFirst
|
24
|
-
?
|
25
|
-
|
26
|
+
? t("contentIndexHeader.firstMessage", {
|
27
|
+
title: syllabus.courseInfo.title,
|
28
|
+
})
|
29
|
+
: t("contentIndexHeader.secondMessage")
|
26
30
|
|
27
31
|
return (
|
28
32
|
<div className="mt-2 ">
|
@@ -47,15 +51,14 @@ const ContentSecondaryHeader = ({
|
|
47
51
|
{
|
48
52
|
messages: TMessage[]
|
49
53
|
}) => {
|
54
|
+
const { t } = useTranslation()
|
50
55
|
const isFirst =
|
51
56
|
messages.filter((m) => m.type === "assistant" && m.content.length > 0)
|
52
57
|
.length === 2
|
53
58
|
|
54
59
|
const subText = isFirst
|
55
|
-
?
|
56
|
-
|
57
|
-
there are any changes you'd like to make.`
|
58
|
-
: "Based on your input, here is the new syllabus, updates are highlighted in yellow"
|
60
|
+
? t("contentIndex.subText.first")
|
61
|
+
: t("contentIndex.subText.second")
|
59
62
|
|
60
63
|
return (
|
61
64
|
<AnimatePresence mode="wait">
|
@@ -80,6 +83,7 @@ export const GenerateButton = ({
|
|
80
83
|
handleSubmit: () => void
|
81
84
|
openLogin: () => void
|
82
85
|
}) => {
|
86
|
+
const { t } = useTranslation()
|
83
87
|
const history = useStore((state) => state.history)
|
84
88
|
const undo = useStore((state) => state.undo)
|
85
89
|
const auth = useStore((state) => state.auth)
|
@@ -92,12 +96,12 @@ export const GenerateButton = ({
|
|
92
96
|
<button
|
93
97
|
onClick={() => {
|
94
98
|
undo()
|
95
|
-
toast.success("
|
99
|
+
toast.success(t("contentIndex.changesReverted"))
|
96
100
|
}}
|
97
101
|
className="w-full sm:w-auto text-gray-500 bg-gray-200 rounded px-4 py-2 hover:bg-gray-300 flex items-center justify-center gap-2 cursor-pointer"
|
98
102
|
>
|
99
103
|
{SVGS.undo}
|
100
|
-
|
104
|
+
{t("contentIndex.revertChanges")}
|
101
105
|
</button>
|
102
106
|
)}
|
103
107
|
|
@@ -105,7 +109,7 @@ export const GenerateButton = ({
|
|
105
109
|
onClick={handleSubmit}
|
106
110
|
className="w-full sm:w-auto bg-blue-600 text-white rounded px-4 py-2 hover:bg-blue-700 flex items-center justify-center gap-2 cursor-pointer"
|
107
111
|
>
|
108
|
-
<span>
|
112
|
+
<span>{t("contentIndex.readyToCreate")}</span>
|
109
113
|
{SVGS.rigoSoftBlue}
|
110
114
|
</button>
|
111
115
|
</div>
|
@@ -116,7 +120,9 @@ export const GenerateButton = ({
|
|
116
120
|
className="text-sm text-gray-500 rounded px-4 py-2 flex items-center justify-center gap-1 "
|
117
121
|
>
|
118
122
|
<span>
|
119
|
-
|
123
|
+
{t("contentIndex.creatingCourseAs", {
|
124
|
+
name: auth.user.first_name,
|
125
|
+
})}
|
120
126
|
</span>
|
121
127
|
<button
|
122
128
|
className="text-sm rounded items-center justify-center cursor-pointer text-blue-600"
|
@@ -131,7 +137,7 @@ export const GenerateButton = ({
|
|
131
137
|
openLogin()
|
132
138
|
}}
|
133
139
|
>
|
134
|
-
|
140
|
+
{t("contentIndex.loginAsSomeoneElse")}
|
135
141
|
</button>
|
136
142
|
</div>
|
137
143
|
)}
|
@@ -1,24 +1,27 @@
|
|
1
1
|
import { useEffect, useRef, useState } from "react"
|
2
2
|
import useStore from "../../utils/store"
|
3
|
-
import { TMessage } from "../Message"
|
4
3
|
import FileUploader from "../FileUploader"
|
5
4
|
import { SVGS } from "../../assets/svgs"
|
6
5
|
import { Message } from "../Message"
|
7
6
|
import { FileCard } from "../FileCard"
|
7
|
+
import { useTranslation } from "react-i18next"
|
8
8
|
|
9
9
|
export const Sidebar = ({
|
10
|
-
messages,
|
10
|
+
// messages,
|
11
11
|
sendPrompt,
|
12
12
|
handleSubmit,
|
13
13
|
}: {
|
14
|
-
messages: TMessage[]
|
14
|
+
// messages: TMessage[]
|
15
15
|
sendPrompt: (prompt: string) => void
|
16
16
|
handleSubmit: () => void
|
17
17
|
}) => {
|
18
|
+
const { t } = useTranslation()
|
19
|
+
|
18
20
|
const sidebarRef = useRef<HTMLDivElement>(null)
|
19
21
|
const inputRef = useRef<HTMLTextAreaElement>(null)
|
20
22
|
const uploadedFiles = useStore((state) => state.uploadedFiles)
|
21
23
|
const setUploadedFiles = useStore((state) => state.setUploadedFiles)
|
24
|
+
const messages = useStore((state) => state.messages)
|
22
25
|
const [showBubble, setShowBubble] = useState(true)
|
23
26
|
|
24
27
|
const [isOpen, setIsOpen] = useState(false)
|
@@ -44,9 +47,7 @@ export const Sidebar = ({
|
|
44
47
|
<div
|
45
48
|
className={`flex flex-row gap-3 cloudy bg-white rounded-md p-2 shadow-md bg-learnpack-blue duration-500 border-2 border-blue-600 mb-4`}
|
46
49
|
>
|
47
|
-
<span className="w-[280px]">
|
48
|
-
Chat with me to update the course content
|
49
|
-
</span>
|
50
|
+
<span className="w-[280px]">{t("sidebar.chatWithMe")}</span>
|
50
51
|
<button
|
51
52
|
className=" text-red-500 cursor-pointer bg-learnpack-blue p-2 rounded-md"
|
52
53
|
onClick={() => setShowBubble(false)}
|
@@ -110,7 +111,6 @@ export const Sidebar = ({
|
|
110
111
|
style={{ resize: "none" }}
|
111
112
|
className="w-full h-full p-2 outline-blue rounded"
|
112
113
|
placeholder="How can Learnpack help you?"
|
113
|
-
autoFocus
|
114
114
|
onKeyUp={(e) => {
|
115
115
|
if (e.key === "Enter" && !e.shiftKey) {
|
116
116
|
e.preventDefault()
|
@@ -11,6 +11,9 @@ import {
|
|
11
11
|
createCourse,
|
12
12
|
isSlugAvailable,
|
13
13
|
reWriteTitle,
|
14
|
+
useConsumableCall,
|
15
|
+
isValidRigoToken,
|
16
|
+
fixTitleLength,
|
14
17
|
} from "../../utils/lib"
|
15
18
|
|
16
19
|
import Loader from "../Loader"
|
@@ -25,41 +28,26 @@ import { useNavigate } from "react-router"
|
|
25
28
|
import { ParamsChecker } from "../ParamsChecker"
|
26
29
|
import { slugify } from "../../utils/creatorUtils"
|
27
30
|
import { RIGO_FLOAT_GIF } from "../../utils/constants"
|
31
|
+
import { useTranslation } from "react-i18next"
|
28
32
|
|
29
33
|
const SyllabusEditor: React.FC = () => {
|
30
34
|
const navigate = useNavigate()
|
35
|
+
const { t, i18n } = useTranslation()
|
31
36
|
|
32
|
-
const {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
push: state.push,
|
46
|
-
// uploadedFiles: state.uploadedFiles,
|
47
|
-
cleanHistory: state.cleanHistory,
|
48
|
-
formState: state.formState,
|
49
|
-
}))
|
50
|
-
)
|
37
|
+
const { history, auth, setAuth, push, cleanAll, messages, setMessages } =
|
38
|
+
useStore(
|
39
|
+
useShallow((state) => ({
|
40
|
+
history: state.history,
|
41
|
+
auth: state.auth,
|
42
|
+
setAuth: state.setAuth,
|
43
|
+
push: state.push,
|
44
|
+
cleanAll: state.cleanAll,
|
45
|
+
messages: state.messages,
|
46
|
+
// formState: state.formState,
|
47
|
+
setMessages: state.setMessages,
|
48
|
+
}))
|
49
|
+
)
|
51
50
|
|
52
|
-
const [messages, setMessages] = useState<TMessage[]>([
|
53
|
-
{
|
54
|
-
type: "assistant",
|
55
|
-
content: "If you're satisfied, type 'OK' in the chat.",
|
56
|
-
},
|
57
|
-
{
|
58
|
-
type: "assistant",
|
59
|
-
content:
|
60
|
-
"If not, what would you like me to change? You can sat things like:\n - Add more exercises\n - Make it more difficult\n - Remove step 1.1 and replace it with a new step that explains the concept of X ",
|
61
|
-
},
|
62
|
-
])
|
63
51
|
const [isGenerating, setIsGenerating] = useState(false)
|
64
52
|
const [showLoginModal, setShowLoginModal] = useState(false)
|
65
53
|
const [isThinking, setIsThinking] = useState(false)
|
@@ -72,26 +60,6 @@ const SyllabusEditor: React.FC = () => {
|
|
72
60
|
}
|
73
61
|
}, [syllabus, navigate])
|
74
62
|
|
75
|
-
useEffect(() => {
|
76
|
-
console.log(formState.description, "DESCRIPTION")
|
77
|
-
|
78
|
-
if (formState.description) {
|
79
|
-
setMessages((prevMessages) => {
|
80
|
-
if (prevMessages.length === 0 || prevMessages[0].type !== "user") {
|
81
|
-
// Si no hay mensajes o el primero no es de usuario, lo agrego al inicio
|
82
|
-
return [
|
83
|
-
{ type: "user", content: formState.description },
|
84
|
-
...prevMessages,
|
85
|
-
]
|
86
|
-
} else {
|
87
|
-
// Si el primero ya es de usuario, lo reemplazo y mantengo el resto
|
88
|
-
const [, ...rest] = prevMessages
|
89
|
-
return [{ type: "user", content: formState.description }, ...rest]
|
90
|
-
}
|
91
|
-
})
|
92
|
-
}
|
93
|
-
}, [formState.description])
|
94
|
-
|
95
63
|
useEffect(() => {
|
96
64
|
;(async () => {
|
97
65
|
const { token } = checkParams(["token"])
|
@@ -139,6 +107,17 @@ const SyllabusEditor: React.FC = () => {
|
|
139
107
|
{ type: "assistant", content: "" },
|
140
108
|
])
|
141
109
|
|
110
|
+
const isValid = await isValidRigoToken(auth.rigoToken)
|
111
|
+
if (!isValid) {
|
112
|
+
setAuth({
|
113
|
+
...auth,
|
114
|
+
rigoToken: "",
|
115
|
+
bcToken: "",
|
116
|
+
userId: "",
|
117
|
+
user: null,
|
118
|
+
})
|
119
|
+
}
|
120
|
+
|
142
121
|
const res = await publicInteractiveCreation(
|
143
122
|
{
|
144
123
|
courseInfo: JSON.stringify(syllabus.courseInfo),
|
@@ -147,11 +126,13 @@ const SyllabusEditor: React.FC = () => {
|
|
147
126
|
.map((message) => `${message.type}: ${message.content}`)
|
148
127
|
.join("\n") + `\nUSER: ${prompt}`,
|
149
128
|
},
|
150
|
-
auth.rigoToken ? auth.rigoToken : auth.publicToken,
|
129
|
+
auth.rigoToken && isValid ? auth.rigoToken : auth.publicToken,
|
151
130
|
syllabus?.courseInfo?.purpose || "learnpack-lesson-writer",
|
152
|
-
auth.rigoToken ? false : true
|
131
|
+
auth.rigoToken && isValid ? false : true
|
153
132
|
)
|
154
133
|
|
134
|
+
console.log(res, "RES from rigobot")
|
135
|
+
|
155
136
|
const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
|
156
137
|
parseLesson(step, syllabus.lessons)
|
157
138
|
)
|
@@ -160,25 +141,39 @@ const SyllabusEditor: React.FC = () => {
|
|
160
141
|
lessons: lessons,
|
161
142
|
courseInfo: {
|
162
143
|
...syllabus.courseInfo,
|
163
|
-
title: res.parsed.title || syllabus.courseInfo.title,
|
144
|
+
title: fixTitleLength(res.parsed.title) || syllabus.courseInfo.title,
|
164
145
|
description:
|
165
146
|
res.parsed.description || syllabus.courseInfo.description,
|
147
|
+
language:
|
148
|
+
res.parsed.languageCode || syllabus.courseInfo.language || "en",
|
149
|
+
technologies:
|
150
|
+
res.parsed.technologies || syllabus.courseInfo.technologies || [],
|
166
151
|
},
|
167
152
|
})
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
153
|
+
|
154
|
+
if (res.parsed.languageCode) {
|
155
|
+
i18n.changeLanguage(res.parsed.languageCode)
|
156
|
+
}
|
157
|
+
|
158
|
+
const newMessages: TMessage[] = [
|
159
|
+
...messages,
|
160
|
+
{
|
161
|
+
type: "user",
|
162
|
+
content: prompt,
|
163
|
+
},
|
164
|
+
{
|
165
|
+
type: "assistant",
|
166
|
+
content: res.parsed.aiMessage,
|
167
|
+
},
|
168
|
+
]
|
169
|
+
setMessages(newMessages)
|
173
170
|
setIsThinking(false)
|
174
171
|
} catch (error) {
|
175
172
|
console.error(error)
|
176
173
|
// Remove the last message
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
return newMessages
|
181
|
-
})
|
174
|
+
const newMessages = [...messages]
|
175
|
+
newMessages.pop()
|
176
|
+
setMessages(newMessages as TMessage[])
|
182
177
|
setIsThinking(false)
|
183
178
|
}
|
184
179
|
}
|
@@ -207,31 +202,39 @@ const SyllabusEditor: React.FC = () => {
|
|
207
202
|
setShowLoginModal(true)
|
208
203
|
return
|
209
204
|
}
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
205
|
+
const success = await useConsumableCall(
|
206
|
+
auth.bcToken,
|
207
|
+
"ai-course-generation"
|
208
|
+
)
|
209
|
+
if (!success) {
|
210
|
+
toast.error(
|
211
|
+
"You don't have enough credits to generate a course! Please add more credits to your account."
|
212
|
+
)
|
213
|
+
toast.error("You will be redirected to the settings page in 5 seconds.")
|
214
|
+
setTimeout(() => {
|
215
|
+
window.location.href =
|
216
|
+
"https://learnpack.co/settings?token=" + auth.bcToken
|
217
|
+
}, 5000)
|
218
|
+
return
|
219
|
+
}
|
220
|
+
console.log(syllabus, "SYLLABUS")
|
220
221
|
|
221
222
|
setIsGenerating(true)
|
222
223
|
createCourse(syllabus, tokenToUse, auth.bcToken)
|
223
224
|
|
224
225
|
setTimeout(() => {
|
225
|
-
|
226
|
+
cleanAll()
|
226
227
|
setIsGenerating(false)
|
227
228
|
window.location.href = `/preview/${slugify(
|
228
229
|
syllabus.courseInfo.title || ""
|
229
|
-
)}?token=${auth.bcToken}`
|
230
|
+
)}?token=${auth.bcToken}&language=${syllabus.courseInfo.language}`
|
230
231
|
}, 3000)
|
231
232
|
}
|
232
233
|
|
233
234
|
if (!syllabus) return null
|
234
235
|
|
236
|
+
console.log(syllabus, "SYLLABUS")
|
237
|
+
|
235
238
|
return isGenerating ? (
|
236
239
|
<>
|
237
240
|
<Loader
|
@@ -256,7 +259,7 @@ It may take a moment..."
|
|
256
259
|
<ConsumablesManager />
|
257
260
|
|
258
261
|
<Sidebar
|
259
|
-
messages={messages}
|
262
|
+
// messages={messages}
|
260
263
|
sendPrompt={sendPrompt}
|
261
264
|
handleSubmit={handleSubmit}
|
262
265
|
/>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
// src/i18n.ts
|
2
|
+
import i18n from "i18next"
|
3
|
+
import { initReactI18next } from "react-i18next"
|
4
|
+
import LanguageDetector from "i18next-browser-languagedetector"
|
5
|
+
|
6
|
+
import enTranslation from "./locales/en.json"
|
7
|
+
import esTranslation from "./locales/es.json"
|
8
|
+
|
9
|
+
i18n
|
10
|
+
.use(LanguageDetector)
|
11
|
+
.use(initReactI18next)
|
12
|
+
.init({
|
13
|
+
resources: {
|
14
|
+
en: { translation: enTranslation },
|
15
|
+
es: { translation: esTranslation },
|
16
|
+
},
|
17
|
+
fallbackLng: "en",
|
18
|
+
interpolation: {
|
19
|
+
escapeValue: false,
|
20
|
+
},
|
21
|
+
detection: {
|
22
|
+
// Puedes personalizar el orden de detección si lo deseas
|
23
|
+
order: ["querystring", "localStorage", "navigator"],
|
24
|
+
caches: ["localStorage"],
|
25
|
+
},
|
26
|
+
})
|
27
|
+
|
28
|
+
export default i18n
|
@@ -0,0 +1,110 @@
|
|
1
|
+
{
|
2
|
+
"contentIndexHeader": {
|
3
|
+
"firstMessage": "I've created a detailed structure for your course: {{title}}",
|
4
|
+
"secondMessage": "I've updated the structure based on your feedback."
|
5
|
+
},
|
6
|
+
"stepWizard": {
|
7
|
+
"requiredFields": "Please fill out all required fields!",
|
8
|
+
"subtitle": "Setting up your tutorial",
|
9
|
+
"back": "Back",
|
10
|
+
"next": "Next",
|
11
|
+
"finish": "Finish 🚀",
|
12
|
+
"description": "What do you want to learn?",
|
13
|
+
"descriptionPlaceholder": "Describe your course",
|
14
|
+
"duration": "What is the estimated duration for this tutorial?",
|
15
|
+
"durationCard": {
|
16
|
+
"30": "Around 30 minutes",
|
17
|
+
"60": "Around 1 hour",
|
18
|
+
"120": "Around 2 hours"
|
19
|
+
},
|
20
|
+
"purpose": "How would you like to use learnpack?",
|
21
|
+
"verifyHuman": "Please verify you are a human",
|
22
|
+
"humanSuccess": "You are a human! 👌🏻",
|
23
|
+
"hasContentIndex": "Any materials to get this course started?",
|
24
|
+
"hasContentIndexCard": {
|
25
|
+
"no": "❌ No, help me create one",
|
26
|
+
"yes": "✅ Yes"
|
27
|
+
},
|
28
|
+
"contentIndex": "Any materials to get this course started?",
|
29
|
+
"contentIndexHelpText": "It could be just text, paste an URL or even upload a document."
|
30
|
+
},
|
31
|
+
"purposeSelector": {
|
32
|
+
"learnpack-lesson-writer": {
|
33
|
+
"label": "Understand a new topic",
|
34
|
+
"description": "Learn about a new concept (e.g., ISO 27001 or exponential decay)."
|
35
|
+
},
|
36
|
+
"homework-and-exam-preparation-aid": {
|
37
|
+
"label": "Practice for an exam or homework",
|
38
|
+
"description": "Solve math problems or certification questions."
|
39
|
+
},
|
40
|
+
"skill-building-facilitator": {
|
41
|
+
"label": "Build real-world skills",
|
42
|
+
"description": "Apply concepts to projects, data science, or security audits."
|
43
|
+
},
|
44
|
+
"certification-preparation-specialist": {
|
45
|
+
"label": "Prepare for a certification",
|
46
|
+
"description": "Get ready for exams like ISO 27001 Lead Auditor."
|
47
|
+
}
|
48
|
+
},
|
49
|
+
"uploader": {
|
50
|
+
"text": {
|
51
|
+
"description": "Write or paste a content table",
|
52
|
+
"placeholder": "Add your text here...",
|
53
|
+
"finish": "Finish"
|
54
|
+
},
|
55
|
+
"files": {
|
56
|
+
"description": "Upload files",
|
57
|
+
"descriptionLong": "Upload a PDF or DOCX file or drag it here",
|
58
|
+
"processing": "Processing...",
|
59
|
+
"drop": "Drop it here",
|
60
|
+
"finish": "🚀 Finish"
|
61
|
+
},
|
62
|
+
"youtube": {
|
63
|
+
"description": "Share a Youtube link",
|
64
|
+
"placeholder": "Paste your link here…"
|
65
|
+
}
|
66
|
+
},
|
67
|
+
"loader": {
|
68
|
+
"text": "Learnpack is setting up your tutorial. It may take a moment..."
|
69
|
+
},
|
70
|
+
"sidebar": {
|
71
|
+
"chatWithMe": "Chat with me to update the course content"
|
72
|
+
},
|
73
|
+
"contentIndex": {
|
74
|
+
"subText": {
|
75
|
+
"first": "It includes a mix of reading, coding exercises, and quizzes. Give it a look and let me know if it aligns with your expectations or if there are any changes you'd like to make.",
|
76
|
+
"second": "Based on your input, here is the new syllabus, updates are highlighted in yellow"
|
77
|
+
},
|
78
|
+
"revertChanges": "Revert changes",
|
79
|
+
"changesReverted": "Changes reverted!",
|
80
|
+
"readyToCreate": "I'm ready. Create the course for me!",
|
81
|
+
"creatingCourseAs": "Creating the course as {{name}}",
|
82
|
+
"loginAsSomeoneElse": "Login as someone else"
|
83
|
+
},
|
84
|
+
"login": {
|
85
|
+
"creatingAccount": "Creating account…",
|
86
|
+
"loggedInSuccessfully": "Logged in successfully",
|
87
|
+
"invalidCredentials": "Invalid credentials",
|
88
|
+
"pleaseFillAllFields": "Please fill all fields",
|
89
|
+
"loggingIn": "Logging in…",
|
90
|
+
"accountCreated": "Account created! Check your email.",
|
91
|
+
"registrationFailed": "Registration failed. Try again.",
|
92
|
+
"createYourAccount": "Create your account",
|
93
|
+
"firstName": "First name",
|
94
|
+
"lastName": "Last name",
|
95
|
+
"alreadyHaveAnAccount": "Already have an account?",
|
96
|
+
"logInHere": "Log in here",
|
97
|
+
"youNeedToHaveAnAccount": "You need to have a 4Geeks account with a creator plan to create a tutorial.",
|
98
|
+
"loginWithGithub": "LOGIN WITH GITHUB",
|
99
|
+
"loginWithGoogle": "LOGIN WITH GOOGLE",
|
100
|
+
"or": "or",
|
101
|
+
"password": "Password",
|
102
|
+
"logIn": "Log in",
|
103
|
+
"skip": "Skip",
|
104
|
+
"forgotPassword": "Forgot your password?",
|
105
|
+
"recoverItHere": "Recover it here",
|
106
|
+
"loginWithEmail": "Login with Email",
|
107
|
+
"youDontHaveAnAccount": "You don't have an account?",
|
108
|
+
"registerHere": "Register here."
|
109
|
+
}
|
110
|
+
}
|