@learnpack/learnpack 5.0.261 → 5.0.265
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/lib/commands/serve.js +82 -50
- package/lib/commands/translate.js +1 -2
- package/lib/creatorDist/assets/{index-BvHkfJm4.js → index-6e9E-1qG.js} +1996 -1947
- package/lib/creatorDist/index.html +1 -1
- package/lib/managers/server/routes.js +1 -2
- package/lib/models/creator.d.ts +2 -0
- package/lib/utils/rigoActions.d.ts +6 -3
- package/lib/utils/rigoActions.js +15 -4
- package/package.json +1 -1
- package/src/commands/serve.ts +123 -72
- package/src/commands/translate.ts +8 -5
- package/src/creator/src/App.tsx +29 -6
- package/src/creator/src/components/PurposeSelector.tsx +6 -6
- package/src/creator/src/components/TurnstileModal.tsx +34 -19
- package/src/creator/src/locales/en.json +4 -0
- package/src/creator/src/locales/es.json +4 -0
- package/src/creator/src/utils/store.ts +5 -0
- package/src/creatorDist/assets/{index-BvHkfJm4.js → index-6e9E-1qG.js} +1996 -1947
- package/src/creatorDist/index.html +1 -1
- package/src/managers/server/routes.ts +9 -7
- package/src/models/creator.ts +4 -0
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +356 -348
- package/src/ui/_app/index.html +19 -2
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/rigoActions.ts +27 -5
@@ -10,7 +10,7 @@
|
|
10
10
|
/>
|
11
11
|
|
12
12
|
<title>Learnpack Creator: Craft tutorials in seconds!</title>
|
13
|
-
<script type="module" crossorigin src="/creator/assets/index-
|
13
|
+
<script type="module" crossorigin src="/creator/assets/index-6e9E-1qG.js"></script>
|
14
14
|
<link rel="stylesheet" crossorigin href="/creator/assets/index-DmpsXknz.css">
|
15
15
|
</head>
|
16
16
|
<body>
|
@@ -305,8 +305,7 @@ async function default_1(app, configObject, configManager) {
|
|
305
305
|
const response = await (0, rigoActions_1.translateExercise)(rigoToken, {
|
306
306
|
text_to_translate: readme.body,
|
307
307
|
output_language: language,
|
308
|
-
|
309
|
-
});
|
308
|
+
}, `${process.env.HOST}/webhooks/translate-exercise`);
|
310
309
|
await (0, creatorUtilities_1.saveTranslatedReadme)(slug, response.parsed.output_language_code, response.parsed.translation);
|
311
310
|
(0, sidebarGenerator_1.addExerciseToSidebar)(slug, response.parsed.output_language_code, response.parsed.translated_slug, configDirPath || "");
|
312
311
|
console_1.default.success(`Translated ${slug} to ${language} successfully`);
|
package/lib/models/creator.d.ts
CHANGED
@@ -11,10 +11,12 @@ export interface ParsedLink {
|
|
11
11
|
name: string;
|
12
12
|
text: string;
|
13
13
|
}
|
14
|
+
export type TDifficulty = "easy" | "beginner" | "intermediate" | "hard";
|
14
15
|
export type FormState = {
|
15
16
|
description: string;
|
16
17
|
duration: number;
|
17
18
|
hasContentIndex: boolean;
|
19
|
+
difficulty: TDifficulty;
|
18
20
|
contentIndex: string;
|
19
21
|
language?: string;
|
20
22
|
technologies?: string[];
|
@@ -19,9 +19,8 @@ export declare function downloadImage(imageUrl: string, savePath: string): Promi
|
|
19
19
|
type TTranslateInputs = {
|
20
20
|
text_to_translate: string;
|
21
21
|
output_language: string;
|
22
|
-
exercise_slug: string;
|
23
22
|
};
|
24
|
-
export declare const translateExercise: (token: string, inputs: TTranslateInputs) => Promise<any>;
|
23
|
+
export declare const translateExercise: (token: string, inputs: TTranslateInputs, webhookUrl: string) => Promise<any>;
|
25
24
|
type TGenerateCourseIntroductionInputs = {
|
26
25
|
course_title: string;
|
27
26
|
lessons_context: string;
|
@@ -63,7 +62,7 @@ export declare const createStructuredPreviewReadme: (token: string, inputs: TCre
|
|
63
62
|
export declare const translateCourseMetadata: (token: string, inputs: {
|
64
63
|
title: string;
|
65
64
|
description: string;
|
66
|
-
|
65
|
+
new_languages: string;
|
67
66
|
}) => Promise<any>;
|
68
67
|
export declare function createPreviewReadme(tutorialDir: string, packageInfo: PackageInfo, rigoToken: string, readmeContents: string[]): Promise<void>;
|
69
68
|
type TReduceReadmeInputs = {
|
@@ -88,4 +87,8 @@ export declare const isPackageAuthor: (token: string, packageSlug: string) => Pr
|
|
88
87
|
isAuthor: boolean;
|
89
88
|
status: number;
|
90
89
|
}>;
|
90
|
+
type TGetLanguageCodesInputs = {
|
91
|
+
raw_languages: string;
|
92
|
+
};
|
93
|
+
export declare const getLanguageCodes: (token: string, inputs: TGetLanguageCodesInputs) => Promise<any>;
|
91
94
|
export {};
|
package/lib/utils/rigoActions.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.isPackageAuthor = exports.fillSidebarJSON = exports.generateCourseShortName = exports.isValidRigoToken = exports.translateCourseMetadata = exports.createStructuredPreviewReadme = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
|
3
|
+
exports.getLanguageCodes = exports.isPackageAuthor = exports.fillSidebarJSON = exports.generateCourseShortName = exports.isValidRigoToken = exports.translateCourseMetadata = exports.createStructuredPreviewReadme = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
|
4
4
|
exports.downloadImage = downloadImage;
|
5
5
|
exports.createPreviewReadme = createPreviewReadme;
|
6
6
|
exports.makeReadmeReadable = makeReadmeReadable;
|
@@ -79,11 +79,12 @@ async function downloadImage(imageUrl, savePath) {
|
|
79
79
|
const buffer = Buffer.from(response.data, "binary");
|
80
80
|
await (0, promises_1.writeFile)(savePath, buffer);
|
81
81
|
}
|
82
|
-
const translateExercise = async (token, inputs) => {
|
83
|
-
const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/
|
82
|
+
const translateExercise = async (token, inputs, webhookUrl) => {
|
83
|
+
const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/translate-asset-markdown/`, {
|
84
84
|
inputs: inputs,
|
85
85
|
include_purpose_objective: false,
|
86
|
-
execute_async:
|
86
|
+
execute_async: true,
|
87
|
+
webhook_url: webhookUrl,
|
87
88
|
}, {
|
88
89
|
headers: {
|
89
90
|
"Content-Type": "application/json",
|
@@ -272,3 +273,13 @@ const isPackageAuthor = async (token, packageSlug) => {
|
|
272
273
|
}
|
273
274
|
};
|
274
275
|
exports.isPackageAuthor = isPackageAuthor;
|
276
|
+
const getLanguageCodes = async (token, inputs) => {
|
277
|
+
const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/get-language-codes/`, { inputs, include_purpose_objective: false, execute_async: false }, {
|
278
|
+
headers: {
|
279
|
+
"Content-Type": "application/json",
|
280
|
+
Authorization: "Token " + token,
|
281
|
+
},
|
282
|
+
});
|
283
|
+
return response.data;
|
284
|
+
};
|
285
|
+
exports.getLanguageCodes = getLanguageCodes;
|
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.
|
4
|
+
"version": "5.0.265",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
package/src/commands/serve.ts
CHANGED
@@ -31,6 +31,7 @@ import {
|
|
31
31
|
isPackageAuthor,
|
32
32
|
createStructuredPreviewReadme,
|
33
33
|
translateCourseMetadata,
|
34
|
+
getLanguageCodes,
|
34
35
|
} from "../utils/rigoActions"
|
35
36
|
import * as dotenv from "dotenv"
|
36
37
|
import {
|
@@ -519,11 +520,25 @@ export default class ServeCommand extends SessionCommand {
|
|
519
520
|
const { courseSlug } = req.params
|
520
521
|
const body = req.body
|
521
522
|
|
522
|
-
|
523
|
+
console.log("RECEIVING INITIAL README WEBHOOK", body)
|
524
|
+
|
525
|
+
const langCode =
|
526
|
+
body.parsed.language_code || body.parsed.output_language_code || ""
|
527
|
+
|
528
|
+
const content = body.parsed.content || body.parsed.translation || ""
|
529
|
+
|
530
|
+
if (!content || !langCode) {
|
531
|
+
console.log("No content or language code to save", body)
|
532
|
+
return res.status(400).json({
|
533
|
+
status: "ERROR",
|
534
|
+
message: "No content to save",
|
535
|
+
})
|
536
|
+
}
|
537
|
+
|
523
538
|
const filePath = `courses/${courseSlug}/README${getReadmeExtension(
|
524
|
-
|
539
|
+
langCode
|
525
540
|
)}`
|
526
|
-
await uploadFileToBucket(bucket,
|
541
|
+
await uploadFileToBucket(bucket, content, filePath)
|
527
542
|
|
528
543
|
res.json({ status: "SUCCESS" })
|
529
544
|
}
|
@@ -707,6 +722,25 @@ export default class ServeCommand extends SessionCommand {
|
|
707
722
|
}
|
708
723
|
)
|
709
724
|
|
725
|
+
// The following endpoint is used to store an incoming translation where it supposed to be
|
726
|
+
app.post(
|
727
|
+
"/webhooks/:courseSlug/:exSlug/save-translation",
|
728
|
+
async (req, res) => {
|
729
|
+
const { courseSlug, exSlug } = req.params
|
730
|
+
const body = req.body
|
731
|
+
|
732
|
+
console.log("RECEIVING TRANSLATION WEBHOOK", body)
|
733
|
+
|
734
|
+
const readmePath = `courses/${courseSlug}/exercises/${exSlug}/README${getReadmeExtension(
|
735
|
+
body.parsed.output_language_code
|
736
|
+
)}`
|
737
|
+
|
738
|
+
await uploadFileToBucket(bucket, body.parsed.translation, readmePath)
|
739
|
+
|
740
|
+
res.json({ status: "SUCCESS" })
|
741
|
+
}
|
742
|
+
)
|
743
|
+
|
710
744
|
app.get("/check-preview-image/:slug", async (req, res) => {
|
711
745
|
const { slug } = req.params
|
712
746
|
const file = bucket.file(`courses/${slug}/preview.png`)
|
@@ -948,13 +982,10 @@ export default class ServeCommand extends SessionCommand {
|
|
948
982
|
}
|
949
983
|
|
950
984
|
const languagesToTranslate: string[] = languages.split(",")
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
const courseJson = JSON.parse(course.toString())
|
957
|
-
const languageCodes = new Set()
|
985
|
+
const languageCodesRes = await getLanguageCodes(rigoToken, {
|
986
|
+
raw_languages: languagesToTranslate.join(","),
|
987
|
+
})
|
988
|
+
const languageCodes = languageCodesRes.parsed.language_codes
|
958
989
|
|
959
990
|
try {
|
960
991
|
await Promise.all(
|
@@ -962,59 +993,68 @@ export default class ServeCommand extends SessionCommand {
|
|
962
993
|
const readmePath = `courses/${courseSlug}/exercises/${slug}/README${getReadmeExtension(
|
963
994
|
currentLanguage
|
964
995
|
)}`
|
965
|
-
|
966
996
|
const readme = await bucket.file(readmePath).download()
|
967
997
|
|
968
998
|
await Promise.all(
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
)
|
999
|
+
languageCodes.map(async (language: string) => {
|
1000
|
+
// verify if the translation already exists
|
1001
|
+
const translationPath = `courses/${courseSlug}/exercises/${slug}/README${getReadmeExtension(
|
1002
|
+
language
|
1003
|
+
)}`
|
1004
|
+
|
1005
|
+
const [exists] = await bucket.file(translationPath).exists()
|
1006
|
+
if (exists) {
|
1007
|
+
console.log(
|
1008
|
+
`Translation in ${language} already exists for exercise ${slug}`
|
1009
|
+
)
|
1010
|
+
return
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
await translateExercise(
|
1014
|
+
rigoToken,
|
1015
|
+
{
|
1016
|
+
text_to_translate: readme.toString(),
|
1017
|
+
output_language: language,
|
1018
|
+
},
|
1019
|
+
`${process.env.HOST}/webhooks/${courseSlug}/${slug}/save-translation`
|
980
1020
|
)
|
981
|
-
await translatedReadme.save(response.parsed.translation)
|
982
|
-
|
983
|
-
languageCodes.add(response.parsed.output_language_code)
|
984
1021
|
})
|
985
1022
|
)
|
986
1023
|
})
|
987
1024
|
)
|
988
1025
|
|
989
|
-
const
|
990
|
-
|
991
|
-
|
992
|
-
|
1026
|
+
const course = await bucket
|
1027
|
+
.file(`courses/${courseSlug}/learn.json`)
|
1028
|
+
.download()
|
1029
|
+
|
1030
|
+
const courseJson = JSON.parse(course.toString())
|
1031
|
+
|
1032
|
+
const neededLanguages = new Set<string>()
|
1033
|
+
|
1034
|
+
let currentLanguages = Object.keys(courseJson.title)
|
1035
|
+
|
1036
|
+
for (const languageCode of languageCodes) {
|
1037
|
+
if (!currentLanguages.includes(languageCode)) {
|
1038
|
+
neededLanguages.add(languageCode)
|
993
1039
|
}
|
994
1040
|
}
|
995
1041
|
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
const result = await translateCourseMetadata(rigoToken, {
|
1000
|
-
title: courseJson.title[currentLanguage],
|
1001
|
-
description: courseJson.description[currentLanguage],
|
1002
|
-
destination_lang_code: languageCode as string,
|
1003
|
-
})
|
1042
|
+
const neededLanguagesList: string[] = [...neededLanguages]
|
1043
|
+
.map((lang: string) => lang.toLowerCase())
|
1044
|
+
.sort()
|
1004
1045
|
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
)
|
1046
|
+
if (neededLanguagesList.length > 0) {
|
1047
|
+
const result = await translateCourseMetadata(rigoToken, {
|
1048
|
+
title: JSON.stringify(courseJson.title),
|
1049
|
+
description: JSON.stringify(courseJson.description),
|
1050
|
+
new_languages: neededLanguagesList.join(","),
|
1051
|
+
})
|
1012
1052
|
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1053
|
+
const translateTitle = JSON.parse(result.parsed.title)
|
1054
|
+
const translateDescription = JSON.parse(result.parsed.description)
|
1055
|
+
|
1056
|
+
courseJson.title = translateTitle
|
1057
|
+
courseJson.description = translateDescription
|
1018
1058
|
|
1019
1059
|
await uploadFileToBucket(
|
1020
1060
|
bucket,
|
@@ -1022,34 +1062,45 @@ export default class ServeCommand extends SessionCommand {
|
|
1022
1062
|
`courses/${courseSlug}/learn.json`
|
1023
1063
|
)
|
1024
1064
|
|
1065
|
+
currentLanguages = Object.keys(courseJson.title)
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
// Check that all the READMEs exists
|
1069
|
+
|
1070
|
+
const missingReadmeTranslations = []
|
1071
|
+
let firstAvailable = ""
|
1072
|
+
for (const languageCode of currentLanguages) {
|
1073
|
+
// eslint-disable-next-line no-await-in-loop
|
1025
1074
|
const previewReadme = await bucket.file(
|
1026
|
-
`courses/${courseSlug}/README${getReadmeExtension(
|
1075
|
+
`courses/${courseSlug}/README${getReadmeExtension(languageCode)}`
|
1027
1076
|
)
|
1028
|
-
const [previewReadmeContent] = await previewReadme.download()
|
1029
|
-
const previewReadmeContentString = previewReadmeContent.toString()
|
1030
|
-
|
1031
|
-
await Promise.all(
|
1032
|
-
[...languageCodes].map(async languageCode => {
|
1033
|
-
const translatedPreviewReadme = await translateExercise(
|
1034
|
-
rigoToken,
|
1035
|
-
{
|
1036
|
-
text_to_translate: previewReadmeContentString,
|
1037
|
-
output_language: languageCode as string,
|
1038
|
-
exercise_slug: "preview-readme",
|
1039
|
-
}
|
1040
|
-
)
|
1041
1077
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1078
|
+
// eslint-disable-next-line no-await-in-loop
|
1079
|
+
const [exists] = await previewReadme.exists()
|
1080
|
+
if (!exists) {
|
1081
|
+
missingReadmeTranslations.push(languageCode)
|
1082
|
+
} else {
|
1083
|
+
// eslint-disable-next-line no-await-in-loop
|
1084
|
+
const [previewReadmeContent] = await previewReadme.download()
|
1085
|
+
const previewReadmeContentString = previewReadmeContent.toString()
|
1086
|
+
firstAvailable = previewReadmeContentString
|
1087
|
+
}
|
1051
1088
|
}
|
1052
1089
|
|
1090
|
+
await Promise.all(
|
1091
|
+
missingReadmeTranslations.map(async languageCode => {
|
1092
|
+
|
1093
|
+
await translateExercise(
|
1094
|
+
rigoToken,
|
1095
|
+
{
|
1096
|
+
text_to_translate: firstAvailable,
|
1097
|
+
output_language: languageCode as string,
|
1098
|
+
},
|
1099
|
+
`${process.env.HOST}/webhooks/${courseSlug}/initial-readme-processor`
|
1100
|
+
)
|
1101
|
+
})
|
1102
|
+
)
|
1103
|
+
|
1053
1104
|
return res.status(200).json({ message: "Translated exercises" })
|
1054
1105
|
} catch (error) {
|
1055
1106
|
console.log(error, "ERROR")
|
@@ -96,11 +96,14 @@ export default class BuildCommand extends SessionCommand {
|
|
96
96
|
.split(",")
|
97
97
|
.map(async (language: string) => {
|
98
98
|
const readme = await getReadmeForExercise(exercise)
|
99
|
-
const response = await translateExercise(
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
99
|
+
const response = await translateExercise(
|
100
|
+
rigoToken,
|
101
|
+
{
|
102
|
+
text_to_translate: readme,
|
103
|
+
output_language: language,
|
104
|
+
},
|
105
|
+
`${process.env.HOST}/webhooks/translate-exercise`
|
106
|
+
)
|
104
107
|
|
105
108
|
await saveTranslatedReadme(
|
106
109
|
exercise,
|
package/src/creator/src/App.tsx
CHANGED
@@ -4,7 +4,7 @@ import SelectableCard from "./components/SelectableCard"
|
|
4
4
|
import Loader from "./components/Loader"
|
5
5
|
import { useNavigate } from "react-router"
|
6
6
|
import { useShallow } from "zustand/react/shallow"
|
7
|
-
import useStore from "./utils/store"
|
7
|
+
import useStore, { TDifficulty } from "./utils/store"
|
8
8
|
|
9
9
|
import { publicInteractiveCreation, isHuman } from "./utils/rigo"
|
10
10
|
import {
|
@@ -106,6 +106,7 @@ function App() {
|
|
106
106
|
purpose,
|
107
107
|
language,
|
108
108
|
new: newParam,
|
109
|
+
difficulty,
|
109
110
|
} = checkParams([
|
110
111
|
"description",
|
111
112
|
"duration",
|
@@ -113,6 +114,7 @@ function App() {
|
|
113
114
|
"purpose",
|
114
115
|
"language",
|
115
116
|
"new",
|
117
|
+
"difficulty",
|
116
118
|
])
|
117
119
|
|
118
120
|
if (newParam && newParam.toLowerCase().trim() === "true") {
|
@@ -155,6 +157,15 @@ function App() {
|
|
155
157
|
})
|
156
158
|
}
|
157
159
|
|
160
|
+
if (
|
161
|
+
difficulty &&
|
162
|
+
["easy", "beginner", "intermediate", "hard"].includes(difficulty)
|
163
|
+
) {
|
164
|
+
setFormState({
|
165
|
+
difficulty: difficulty as TDifficulty,
|
166
|
+
})
|
167
|
+
}
|
168
|
+
|
158
169
|
if (description && duration && purpose) {
|
159
170
|
setFormState({
|
160
171
|
currentStep: "hasContentIndex",
|
@@ -363,11 +374,14 @@ function App() {
|
|
363
374
|
})
|
364
375
|
}
|
365
376
|
}}
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
377
|
+
onError={() => {
|
378
|
+
toast.error(t("turnstileModal.error"), {
|
379
|
+
duration: 10000,
|
380
|
+
})
|
381
|
+
setFormState({
|
382
|
+
currentStep: "purpose",
|
383
|
+
})
|
384
|
+
}}
|
371
385
|
/>
|
372
386
|
),
|
373
387
|
},
|
@@ -444,6 +458,15 @@ function App() {
|
|
444
458
|
setShowTurnstileModal(false)
|
445
459
|
handleCreateTutorial()
|
446
460
|
}}
|
461
|
+
onError={() => {
|
462
|
+
toast.error(t("turnstileModal.error"), {
|
463
|
+
duration: 10000,
|
464
|
+
})
|
465
|
+
setShowTurnstileModal(false)
|
466
|
+
setFormState({
|
467
|
+
currentStep: "purpose",
|
468
|
+
})
|
469
|
+
}}
|
447
470
|
/>
|
448
471
|
)}
|
449
472
|
{formState.isCompleted && history.length === 0 ? (
|
@@ -36,17 +36,17 @@ export const PurposeSelector: React.FC<PurposeSelectorProps> = ({
|
|
36
36
|
// description: t("purposeSelector.learnpack-lesson-writer.description"),
|
37
37
|
// },
|
38
38
|
{
|
39
|
-
slug: "
|
40
|
-
label: t("purposeSelector.
|
39
|
+
slug: "skill-building-facilitator",
|
40
|
+
label: t("purposeSelector.skill-building-facilitator.label"),
|
41
41
|
description: t(
|
42
|
-
"purposeSelector.
|
42
|
+
"purposeSelector.skill-building-facilitator.description"
|
43
43
|
),
|
44
44
|
},
|
45
45
|
{
|
46
|
-
slug: "
|
47
|
-
label: t("purposeSelector.
|
46
|
+
slug: "homework-and-exam-preparation-aid",
|
47
|
+
label: t("purposeSelector.homework-and-exam-preparation-aid.label"),
|
48
48
|
description: t(
|
49
|
-
"purposeSelector.
|
49
|
+
"purposeSelector.homework-and-exam-preparation-aid.description"
|
50
50
|
),
|
51
51
|
},
|
52
52
|
{
|
@@ -5,32 +5,47 @@ import { toast } from "react-hot-toast"
|
|
5
5
|
import useStore from "../utils/store"
|
6
6
|
import { useTranslation } from "react-i18next"
|
7
7
|
|
8
|
-
export default function TurnstileModal({
|
8
|
+
export default function TurnstileModal({
|
9
|
+
onClose,
|
10
|
+
onError,
|
11
|
+
}: {
|
12
|
+
onClose: () => void
|
13
|
+
onError: () => void
|
14
|
+
}) {
|
9
15
|
const auth = useStore((state) => state.auth)
|
10
16
|
const setAuth = useStore((state) => state.setAuth)
|
11
17
|
const { t } = useTranslation()
|
12
18
|
|
13
19
|
return (
|
14
20
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-1000">
|
15
|
-
<
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
toast.success(t("stepWizard.humanSuccess"))
|
23
|
-
setAuth({
|
24
|
-
...auth,
|
25
|
-
publicToken: jwtToken,
|
26
|
-
})
|
27
|
-
onClose()
|
28
|
-
} else {
|
29
|
-
toast.error(message)
|
30
|
-
onClose()
|
21
|
+
<div className="bg-white p-4 rounded-lg max-w-md w-full flex flex-col items-center justify-center gap-4">
|
22
|
+
<h2 className="text-xl font-semibold text-center">
|
23
|
+
{t("turnstileModal.title")}
|
24
|
+
</h2>
|
25
|
+
<TurnstileChallenge
|
26
|
+
siteKey={
|
27
|
+
DEV_MODE ? "0x4AAAAAABeKMBYYinMU4Ib0" : "0x4AAAAAABeZ9tjEevGBsJFU"
|
31
28
|
}
|
32
|
-
|
33
|
-
|
29
|
+
onSuccess={async (token) => {
|
30
|
+
const { human, message, token: jwtToken } = await isHuman(token)
|
31
|
+
if (human) {
|
32
|
+
toast.success(t("stepWizard.humanSuccess"))
|
33
|
+
setAuth({
|
34
|
+
...auth,
|
35
|
+
publicToken: jwtToken,
|
36
|
+
})
|
37
|
+
onClose()
|
38
|
+
} else {
|
39
|
+
console.error(message, "turnstileModal error")
|
40
|
+
onError()
|
41
|
+
}
|
42
|
+
}}
|
43
|
+
onError={() => {
|
44
|
+
console.error("turnstileModal error")
|
45
|
+
onError()
|
46
|
+
}}
|
47
|
+
/>
|
48
|
+
</div>
|
34
49
|
</div>
|
35
50
|
)
|
36
51
|
}
|
@@ -115,5 +115,9 @@
|
|
115
115
|
"loginWithEmail": "Login with Email",
|
116
116
|
"youDontHaveAnAccount": "You don't have an account?",
|
117
117
|
"registerHere": "Register here."
|
118
|
+
},
|
119
|
+
"turnstileModal": {
|
120
|
+
"title": "We need to verify you are a human, wait a moment...",
|
121
|
+
"error": "Error verifying you are a human, please try again, if the problem persists, use a different browser."
|
118
122
|
}
|
119
123
|
}
|
@@ -115,5 +115,9 @@
|
|
115
115
|
"loginWithEmail": "Iniciar sesión con Email",
|
116
116
|
"youDontHaveAnAccount": "¿No tienes una cuenta?",
|
117
117
|
"registerHere": "Regístrate aquí."
|
118
|
+
},
|
119
|
+
"turnstileModal": {
|
120
|
+
"title": "Necesitamos verificar que eres un humano, espera un momento...",
|
121
|
+
"error": "Error al verificar que eres un humano, por favor intenta de nuevo, si el problema persiste, usa un navegador diferente."
|
118
122
|
}
|
119
123
|
}
|
@@ -4,6 +4,7 @@ import { Lesson } from "../components/LessonItem"
|
|
4
4
|
import { ParsedFile } from "../components/FileUploader"
|
5
5
|
import { TMessage } from "../components/Message"
|
6
6
|
// import { ParsedLink } from "../components/LinkUploader"
|
7
|
+
export type TDifficulty = "easy" | "beginner" | "intermediate" | "hard"
|
7
8
|
|
8
9
|
export type FormState = {
|
9
10
|
description: string
|
@@ -11,6 +12,7 @@ export type FormState = {
|
|
11
12
|
hasContentIndex: boolean
|
12
13
|
contentIndex: string
|
13
14
|
purpose: string
|
15
|
+
difficulty: TDifficulty
|
14
16
|
slug: string
|
15
17
|
language?: string
|
16
18
|
isCompleted: boolean
|
@@ -92,6 +94,7 @@ const useStore = create<Store>()(
|
|
92
94
|
purpose: "",
|
93
95
|
language: "en",
|
94
96
|
technologies: [],
|
97
|
+
difficulty: "beginner",
|
95
98
|
// sources: [],
|
96
99
|
isCompleted: false,
|
97
100
|
currentStep: "description",
|
@@ -126,6 +129,7 @@ const useStore = create<Store>()(
|
|
126
129
|
description: "",
|
127
130
|
duration: 0,
|
128
131
|
language: "en",
|
132
|
+
difficulty: "beginner",
|
129
133
|
technologies: [],
|
130
134
|
hasContentIndex: false,
|
131
135
|
contentIndex: "",
|
@@ -173,6 +177,7 @@ const useStore = create<Store>()(
|
|
173
177
|
description: "",
|
174
178
|
duration: 0,
|
175
179
|
slug: "",
|
180
|
+
difficulty: "beginner",
|
176
181
|
language: "en",
|
177
182
|
technologies: [],
|
178
183
|
hasContentIndex: false,
|