@learnpack/learnpack 5.0.168 → 5.0.176
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.js +42 -2
- package/{src/creatorDist/assets/index-C_YTggyk.css → lib/creatorDist/assets/index-CrWESWmj.css} +43 -11
- package/lib/creatorDist/assets/index-T7usmMYO.js +32991 -0
- package/lib/creatorDist/index.html +2 -2
- package/lib/utils/api.d.ts +1 -1
- package/lib/utils/api.js +2 -1
- package/lib/utils/readDocuments.d.ts +0 -0
- package/lib/utils/readDocuments.js +1 -0
- package/oclif.manifest.json +1 -1
- package/package.json +3 -1
- package/src/commands/serve.ts +56 -2
- package/src/creator/src/App.tsx +51 -34
- package/src/creator/src/components/ConsumablesManager.tsx +1 -0
- package/src/creator/src/components/FileUploader.tsx +64 -52
- package/src/creator/src/components/Login.tsx +172 -82
- package/src/creator/src/components/ResumeCourseModal.tsx +38 -0
- package/src/creator/src/components/StepWizard.tsx +12 -10
- package/src/creator/src/components/TurnstileChallenge.tsx +2 -7
- package/src/creator/src/components/syllabus/ContentIndex.tsx +1 -0
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +63 -29
- package/src/creator/src/utils/constants.ts +2 -1
- package/src/creator/src/utils/lib.ts +55 -0
- package/src/creator/src/utils/rigo.ts +12 -5
- package/src/creator/src/utils/store.ts +22 -1
- package/{lib/creatorDist/assets/index-C_YTggyk.css → src/creatorDist/assets/index-CrWESWmj.css} +43 -11
- package/src/creatorDist/assets/index-T7usmMYO.js +32991 -0
- package/src/creatorDist/index.html +2 -2
- package/src/ui/_app/app.js +286 -286
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/api.ts +2 -1
- package/src/utils/readDocuments.ts +0 -0
- package/lib/creatorDist/assets/index-4XkqESUr.js +0 -83719
- package/lib/creatorDist/assets/pdf.worker-DSVOJ9H9.js +0 -56037
- package/src/creatorDist/assets/index-4XkqESUr.js +0 -83719
- package/src/creatorDist/assets/pdf.worker-DSVOJ9H9.js +0 -56037
@@ -4,61 +4,88 @@ import { SVGS } from "../assets/svgs"
|
|
4
4
|
import toast from "react-hot-toast"
|
5
5
|
import useStore from "../utils/store"
|
6
6
|
import { useShallow } from "zustand/react/shallow"
|
7
|
-
import { login4Geeks } from "../utils/lib"
|
7
|
+
import { login4Geeks, registerUserWithFormData } from "../utils/lib"
|
8
8
|
|
9
9
|
export default function Login({ onFinish }: { onFinish: () => void }) {
|
10
|
+
// Login states
|
10
11
|
const [email, setEmail] = useState("")
|
11
12
|
const [password, setPassword] = useState("")
|
12
13
|
const [isLoading, setIsLoading] = useState(false)
|
13
14
|
const [showForm, setShowForm] = useState(false)
|
14
|
-
|
15
|
-
|
15
|
+
// Signup states
|
16
|
+
const [showSignup, setShowSignup] = useState(false)
|
17
|
+
const [signupData, setSignupData] = useState({
|
18
|
+
firstName: "",
|
19
|
+
lastName: "",
|
20
|
+
email: "",
|
21
|
+
})
|
22
|
+
// const planToRedirect = useStore((state) => state.planToRedirect)
|
16
23
|
const { setAuth } = useStore(
|
17
24
|
useShallow((state) => ({ setAuth: state.setAuth }))
|
18
25
|
)
|
19
26
|
|
20
|
-
|
27
|
+
// Login handler
|
28
|
+
const login = async (e: React.FormEvent<HTMLFormElement>) => {
|
21
29
|
e.preventDefault()
|
22
30
|
setIsLoading(true)
|
23
31
|
const tid = toast.loading("Logging in…")
|
24
|
-
|
25
32
|
if (!email || !password) {
|
26
33
|
setIsLoading(false)
|
27
34
|
toast.error("Please fill all fields", { id: tid })
|
28
35
|
return
|
29
36
|
}
|
30
|
-
|
31
37
|
const resp = await login4Geeks({ email, password })
|
32
38
|
if (!resp) {
|
33
39
|
setIsLoading(false)
|
34
40
|
toast.error("Invalid credentials", { id: tid })
|
35
41
|
return
|
36
42
|
}
|
37
|
-
|
38
43
|
toast.success("Logged in successfully", { id: tid })
|
39
44
|
setAuth({
|
40
45
|
bcToken: resp.token,
|
41
46
|
userId: resp.user.id,
|
42
47
|
rigoToken: resp.rigobot.key,
|
43
48
|
user: resp.user,
|
49
|
+
publicToken: "",
|
44
50
|
})
|
45
51
|
setIsLoading(false)
|
46
52
|
onFinish()
|
47
53
|
}
|
48
54
|
|
55
|
+
// Signup handler
|
56
|
+
const handleSignup = async (e: React.FormEvent<HTMLFormElement>) => {
|
57
|
+
e.preventDefault()
|
58
|
+
setIsLoading(true)
|
59
|
+
const tid = toast.loading("Creating account…")
|
60
|
+
const { firstName, lastName, email } = signupData
|
61
|
+
if (!firstName || !lastName || !email) {
|
62
|
+
setIsLoading(false)
|
63
|
+
toast.error("Please fill all fields", { id: tid })
|
64
|
+
return
|
65
|
+
}
|
66
|
+
try {
|
67
|
+
await registerUserWithFormData(firstName, lastName, email)
|
68
|
+
toast.success("Account created! Check your email.", { id: tid })
|
69
|
+
setShowSignup(false)
|
70
|
+
setShowForm(true)
|
71
|
+
setEmail(email)
|
72
|
+
} catch (err) {
|
73
|
+
toast.error("Registration failed. Try again.", { id: tid })
|
74
|
+
} finally {
|
75
|
+
setIsLoading(false)
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
49
79
|
function stringToBase64(str: string) {
|
50
80
|
return btoa(unescape(encodeURIComponent(str)))
|
51
81
|
}
|
52
|
-
|
53
82
|
function getCurrentUrlWithQueryParams() {
|
54
83
|
return window.location.href
|
55
84
|
}
|
56
|
-
|
57
85
|
const redirectGithub = () => {
|
58
86
|
const url = stringToBase64(getCurrentUrlWithQueryParams())
|
59
87
|
window.location.href = `${BREATHECODE_HOST}/v1/auth/github/?url=${url}`
|
60
88
|
}
|
61
|
-
|
62
89
|
const redirectGoogle = () => {
|
63
90
|
const url = stringToBase64(getCurrentUrlWithQueryParams())
|
64
91
|
window.location.href = `${BREATHECODE_HOST}/v1/auth/google?url=${url}`
|
@@ -73,89 +100,152 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
|
|
73
100
|
className="bg-white p-8 rounded-xl shadow-md max-w-sm w-full"
|
74
101
|
onClick={(e) => e.stopPropagation()}
|
75
102
|
>
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
placeholder="Password"
|
110
|
-
className="w-full border border-gray-300 px-3 py-2 rounded-md"
|
111
|
-
onChange={(e) => setPassword(e.target.value)}
|
112
|
-
/>
|
113
|
-
<div className="flex gap-2 mt-4">
|
103
|
+
{showSignup ? (
|
104
|
+
<>
|
105
|
+
<h2 className="mb-4 text-xl font-semibold text-center">
|
106
|
+
Create your account
|
107
|
+
</h2>
|
108
|
+
<form className="space-y-3" onSubmit={handleSignup}>
|
109
|
+
<input
|
110
|
+
type="text"
|
111
|
+
placeholder="First name"
|
112
|
+
className="w-full border border-gray-300 px-3 py-2 rounded-md"
|
113
|
+
value={signupData.firstName}
|
114
|
+
onChange={(e) =>
|
115
|
+
setSignupData({ ...signupData, firstName: e.target.value })
|
116
|
+
}
|
117
|
+
/>
|
118
|
+
<input
|
119
|
+
type="text"
|
120
|
+
placeholder="Last name"
|
121
|
+
className="w-full border border-gray-300 px-3 py-2 rounded-md"
|
122
|
+
value={signupData.lastName}
|
123
|
+
onChange={(e) =>
|
124
|
+
setSignupData({ ...signupData, lastName: e.target.value })
|
125
|
+
}
|
126
|
+
/>
|
127
|
+
<input
|
128
|
+
type="email"
|
129
|
+
placeholder="Email"
|
130
|
+
className="w-full border border-gray-300 px-3 py-2 rounded-md"
|
131
|
+
value={signupData.email}
|
132
|
+
onChange={(e) =>
|
133
|
+
setSignupData({ ...signupData, email: e.target.value })
|
134
|
+
}
|
135
|
+
/>
|
114
136
|
<button
|
115
137
|
type="submit"
|
116
|
-
className="
|
138
|
+
className="w-full bg-blue-600 text-white py-2 rounded-md font-semibold cursor-pointer"
|
139
|
+
disabled={isLoading}
|
117
140
|
>
|
118
|
-
{isLoading ? "
|
141
|
+
{isLoading ? "Creating..." : "Sign up"}
|
119
142
|
</button>
|
143
|
+
</form>
|
144
|
+
<div className="mt-4 text-sm text-center">
|
145
|
+
Already have an account?{" "}
|
120
146
|
<button
|
121
|
-
|
122
|
-
onClick={() =>
|
123
|
-
|
147
|
+
className="text-blue-600 font-medium"
|
148
|
+
onClick={() => {
|
149
|
+
setShowSignup(false)
|
150
|
+
setShowForm(true)
|
151
|
+
}}
|
124
152
|
>
|
125
|
-
|
153
|
+
Log in here
|
126
154
|
</button>
|
127
155
|
</div>
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
156
|
+
</>
|
157
|
+
) : (
|
158
|
+
<>
|
159
|
+
<p className="mb-4 text-center text-gray-700">
|
160
|
+
You need to have a 4Geeks account with a creator plan to create a
|
161
|
+
tutorial.
|
162
|
+
</p>
|
163
|
+
<button
|
164
|
+
onClick={redirectGithub}
|
165
|
+
className="w-full border border-gray-300 py-2 rounded-md font-semibold flex items-center justify-center gap-2 mb-4 cursor-pointer"
|
166
|
+
>
|
167
|
+
{SVGS.github} LOGIN WITH GITHUB
|
168
|
+
</button>
|
169
|
+
<button
|
170
|
+
onClick={redirectGoogle}
|
171
|
+
className="w-full border border-gray-300 py-2 rounded-md font-semibold flex items-center justify-center gap-2 mb-4 cursor-pointer"
|
172
|
+
>
|
173
|
+
{SVGS.google} LOGIN WITH GOOGLE
|
174
|
+
</button>
|
175
|
+
<div className="flex items-center mb-4">
|
176
|
+
<hr className="flex-grow border-gray-300" />
|
177
|
+
<span className="mx-2 text-gray-400 text-sm">or</span>
|
178
|
+
<hr className="flex-grow border-gray-300" />
|
179
|
+
</div>
|
180
|
+
{showForm ? (
|
181
|
+
<form className="space-y-3" onSubmit={login}>
|
182
|
+
<input
|
183
|
+
type="email"
|
184
|
+
placeholder="Email"
|
185
|
+
className="w-full border border-gray-300 px-3 py-2 rounded-md"
|
186
|
+
value={email}
|
187
|
+
onChange={(e) => setEmail(e.target.value)}
|
188
|
+
/>
|
189
|
+
<input
|
190
|
+
type="password"
|
191
|
+
placeholder="Password"
|
192
|
+
className="w-full border border-gray-300 px-3 py-2 rounded-md"
|
193
|
+
value={password}
|
194
|
+
onChange={(e) => setPassword(e.target.value)}
|
195
|
+
/>
|
196
|
+
<div className="flex gap-2 mt-4">
|
197
|
+
<button
|
198
|
+
type="submit"
|
199
|
+
className="flex-1 bg-blue-500 text-white py-2 rounded-md font-semibold cursor-pointer"
|
200
|
+
>
|
201
|
+
{isLoading ? "Logging in..." : "Log in"}
|
202
|
+
</button>
|
203
|
+
<button
|
204
|
+
type="button"
|
205
|
+
onClick={() => setShowForm(false)}
|
206
|
+
className="flex-1 border border-blue-500 text-blue-600 py-2 rounded-md font-semibold cursor-pointer"
|
207
|
+
>
|
208
|
+
Skip
|
209
|
+
</button>
|
210
|
+
</div>
|
211
|
+
<div className="text-sm text-gray-600 mt-2">
|
212
|
+
Forgot your password?{" "}
|
213
|
+
<a
|
214
|
+
href={`${BREATHECODE_HOST}/v1/auth/password/reset?url=${getCurrentUrlWithQueryParams()}`}
|
215
|
+
className="text-blue-600 font-medium"
|
216
|
+
>
|
217
|
+
Recover it here.
|
218
|
+
</a>
|
219
|
+
</div>
|
220
|
+
</form>
|
221
|
+
) : (
|
222
|
+
<button
|
223
|
+
onClick={() => setShowForm(true)}
|
224
|
+
className="w-full bg-blue-600 text-white py-2 rounded-md font-semibold cursor-pointer"
|
133
225
|
>
|
134
|
-
|
135
|
-
</
|
226
|
+
Login with Email
|
227
|
+
</button>
|
228
|
+
)}
|
229
|
+
<div className="flex justify-between items-center mt-4">
|
230
|
+
<div className="bg-blue-50 text-sm p-2 rounded text-left">
|
231
|
+
<p className="text-gray-700 m-0">You don't have an account?</p>
|
232
|
+
{/* <button
|
233
|
+
className="text-blue-600 font-medium cursor-pointer"
|
234
|
+
onClick={() => setShowSignup(true)}
|
235
|
+
>
|
236
|
+
Register here.
|
237
|
+
</button> */}
|
238
|
+
<a
|
239
|
+
href={`https://www.learnpack.co/register`}
|
240
|
+
target="_blank"
|
241
|
+
className="text-blue-600 font-medium"
|
242
|
+
>
|
243
|
+
Register here.
|
244
|
+
</a>
|
245
|
+
</div>
|
136
246
|
</div>
|
137
|
-
|
138
|
-
) : (
|
139
|
-
<button
|
140
|
-
onClick={() => setShowForm(true)}
|
141
|
-
className="w-full bg-blue-600 text-white py-2 rounded-md font-semibold cursor-pointer"
|
142
|
-
>
|
143
|
-
Login with Email
|
144
|
-
</button>
|
247
|
+
</>
|
145
248
|
)}
|
146
|
-
|
147
|
-
<div className="flex justify-between items-center mt-4">
|
148
|
-
<div className="bg-blue-50 text-sm p-2 rounded text-left">
|
149
|
-
<p className="text-gray-700 m-0">You don't have an account?</p>
|
150
|
-
<a
|
151
|
-
href={`https://4geeks.com/checkout?plan=${planToRedirect}`}
|
152
|
-
target="_blank"
|
153
|
-
className="text-blue-600 font-medium"
|
154
|
-
>
|
155
|
-
Register here.
|
156
|
-
</a>
|
157
|
-
</div>
|
158
|
-
</div>
|
159
249
|
</div>
|
160
250
|
</div>
|
161
251
|
)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
interface ResumeCourseModalProps {
|
2
|
+
onContinue: () => void
|
3
|
+
onStartOver: () => void
|
4
|
+
}
|
5
|
+
|
6
|
+
export default function ResumeCourseModal({
|
7
|
+
onContinue,
|
8
|
+
onStartOver,
|
9
|
+
}: ResumeCourseModalProps) {
|
10
|
+
return (
|
11
|
+
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
|
12
|
+
<div className="bg-white shadow-xl rounded-lg p-8 max-w-md w-full animate-fade-in">
|
13
|
+
<h2 className="text-xl font-bold text-gray-800 mb-3 text-center">
|
14
|
+
You have an unfinished course
|
15
|
+
</h2>
|
16
|
+
<p className="text-gray-600 mb-6 text-center">
|
17
|
+
You didn't finish creating your previous course.
|
18
|
+
<br />
|
19
|
+
What would you like to do?
|
20
|
+
</p>
|
21
|
+
<div className="flex flex-col gap-4">
|
22
|
+
<button
|
23
|
+
onClick={onContinue}
|
24
|
+
className="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded-md font-semibold transition cursor-pointer"
|
25
|
+
>
|
26
|
+
Continue previous course
|
27
|
+
</button>
|
28
|
+
<button
|
29
|
+
onClick={onStartOver}
|
30
|
+
className="w-full border border-blue-600 text-blue-700 py-2 rounded-md font-semibold hover:bg-blue-50 transition cursor-pointer"
|
31
|
+
>
|
32
|
+
Erase previous answers and start a new course
|
33
|
+
</button>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
)
|
38
|
+
}
|
@@ -17,6 +17,7 @@ type Props = {
|
|
17
17
|
formState: any
|
18
18
|
setFormState: (formState: any) => void
|
19
19
|
onFinish: () => void
|
20
|
+
hideLastButton?: boolean
|
20
21
|
}
|
21
22
|
|
22
23
|
const StepWizard: React.FC<Props> = ({
|
@@ -24,6 +25,7 @@ const StepWizard: React.FC<Props> = ({
|
|
24
25
|
formState,
|
25
26
|
setFormState,
|
26
27
|
onFinish,
|
28
|
+
hideLastButton = false,
|
27
29
|
}) => {
|
28
30
|
const currentStep = formState.currentStep
|
29
31
|
const index = steps.findIndex((step) => step.slug === currentStep)
|
@@ -48,8 +50,6 @@ const StepWizard: React.FC<Props> = ({
|
|
48
50
|
setFormState({ ...formState, currentStep: steps[index - 1].slug })
|
49
51
|
}
|
50
52
|
|
51
|
-
console.log(index, totalSteps, steps)
|
52
|
-
|
53
53
|
return (
|
54
54
|
<div className="min-h-screen flex flex-col items-center justify-center text-center px-4">
|
55
55
|
<div className=" rounded-xl p-8 w-full max-w-xl">
|
@@ -99,14 +99,16 @@ const StepWizard: React.FC<Props> = ({
|
|
99
99
|
>
|
100
100
|
⬅ Back
|
101
101
|
</button>
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
102
|
+
{!(hideLastButton && index === totalSteps - 1) && (
|
103
|
+
<button
|
104
|
+
onClick={index === totalSteps - 1 ? onFinish : goNext}
|
105
|
+
className={`text-sm text-blue-600 hover:text-blue-800 cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed p-2 rounded-md ${
|
106
|
+
index === totalSteps - 1 ? "bg-blue-600 text-white" : ""
|
107
|
+
}`}
|
108
|
+
>
|
109
|
+
{index === totalSteps - 1 ? "Finish 🚀" : "Next ➡"}
|
110
|
+
</button>
|
111
|
+
)}
|
110
112
|
</div>
|
111
113
|
</div>
|
112
114
|
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
// components/TurnstileChallenge.tsx
|
2
2
|
import { useEffect, useRef, useState } from "react"
|
3
|
-
import toast from "react-hot-toast"
|
3
|
+
// import toast from "react-hot-toast"
|
4
4
|
|
5
5
|
interface TurnstileChallengeProps {
|
6
6
|
siteKey: string
|
@@ -34,8 +34,6 @@ const TurnstileChallenge = ({
|
|
34
34
|
const id = window.turnstile.render(`#${containerId}`, {
|
35
35
|
sitekey: siteKey,
|
36
36
|
callback: (token: string) => {
|
37
|
-
console.log("token to send", token)
|
38
|
-
|
39
37
|
onSuccess(token)
|
40
38
|
},
|
41
39
|
"error-callback": () => {
|
@@ -43,7 +41,7 @@ const TurnstileChallenge = ({
|
|
43
41
|
},
|
44
42
|
"refresh-callback": () => {
|
45
43
|
console.log("refresh callback CALLEd")
|
46
|
-
toast.error("Refresh callback called")
|
44
|
+
// toast.error("Refresh callback called")
|
47
45
|
},
|
48
46
|
})
|
49
47
|
|
@@ -61,11 +59,8 @@ const TurnstileChallenge = ({
|
|
61
59
|
return () => clearInterval(interval)
|
62
60
|
}, [siteKey, autoStart, onSuccess, onError])
|
63
61
|
|
64
|
-
|
65
62
|
return (
|
66
|
-
|
67
63
|
<div className="">
|
68
|
-
|
69
64
|
<div id={containerId} data-refresh-timeout="auto" />
|
70
65
|
{!ready && <p className="">...</p>}
|
71
66
|
{/* <button onClick={reset}>RESET</button> */}
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import React, { useState, useEffect } from "react"
|
2
2
|
import { useShallow } from "zustand/react/shallow"
|
3
3
|
import useStore from "../../utils/store"
|
4
|
-
import {
|
4
|
+
import { publicInteractiveCreation } from "../../utils/rigo"
|
5
5
|
import {
|
6
6
|
parseLesson,
|
7
|
-
useConsumableCall,
|
7
|
+
// useConsumableCall,
|
8
8
|
validateTokens,
|
9
9
|
checkParams,
|
10
10
|
loginWithToken,
|
@@ -28,6 +28,27 @@ import { RIGO_FLOAT_GIT } from "../../utils/constants"
|
|
28
28
|
|
29
29
|
const SyllabusEditor: React.FC = () => {
|
30
30
|
const navigate = useNavigate()
|
31
|
+
|
32
|
+
const {
|
33
|
+
history,
|
34
|
+
auth,
|
35
|
+
setAuth,
|
36
|
+
push,
|
37
|
+
uploadedFiles,
|
38
|
+
cleanHistory,
|
39
|
+
formState,
|
40
|
+
} = useStore(
|
41
|
+
useShallow((state) => ({
|
42
|
+
history: state.history,
|
43
|
+
auth: state.auth,
|
44
|
+
setAuth: state.setAuth,
|
45
|
+
push: state.push,
|
46
|
+
uploadedFiles: state.uploadedFiles,
|
47
|
+
cleanHistory: state.cleanHistory,
|
48
|
+
formState: state.formState,
|
49
|
+
}))
|
50
|
+
)
|
51
|
+
|
31
52
|
const [messages, setMessages] = useState<TMessage[]>([
|
32
53
|
{
|
33
54
|
type: "assistant",
|
@@ -43,26 +64,35 @@ const SyllabusEditor: React.FC = () => {
|
|
43
64
|
const [showLoginModal, setShowLoginModal] = useState(false)
|
44
65
|
const [isThinking, setIsThinking] = useState(false)
|
45
66
|
|
46
|
-
const { history, auth, setAuth, push, uploadedFiles, cleanHistory } =
|
47
|
-
useStore(
|
48
|
-
useShallow((state) => ({
|
49
|
-
history: state.history,
|
50
|
-
auth: state.auth,
|
51
|
-
setAuth: state.setAuth,
|
52
|
-
push: state.push,
|
53
|
-
uploadedFiles: state.uploadedFiles,
|
54
|
-
cleanHistory: state.cleanHistory,
|
55
|
-
}))
|
56
|
-
)
|
57
|
-
|
58
67
|
const syllabus = history[history.length - 1]
|
59
68
|
|
60
69
|
useEffect(() => {
|
61
70
|
if (!syllabus) {
|
62
71
|
navigate("/creator", { replace: true })
|
72
|
+
} else {
|
63
73
|
}
|
64
74
|
}, [syllabus, navigate])
|
65
75
|
|
76
|
+
useEffect(() => {
|
77
|
+
console.log(formState.description, "DESCRIPTION")
|
78
|
+
|
79
|
+
if (formState.description) {
|
80
|
+
setMessages((prevMessages) => {
|
81
|
+
if (prevMessages.length === 0 || prevMessages[0].type !== "user") {
|
82
|
+
// Si no hay mensajes o el primero no es de usuario, lo agrego al inicio
|
83
|
+
return [
|
84
|
+
{ type: "user", content: formState.description },
|
85
|
+
...prevMessages,
|
86
|
+
]
|
87
|
+
} else {
|
88
|
+
// Si el primero ya es de usuario, lo reemplazo y mantengo el resto
|
89
|
+
const [, ...rest] = prevMessages
|
90
|
+
return [{ type: "user", content: formState.description }, ...rest]
|
91
|
+
}
|
92
|
+
})
|
93
|
+
}
|
94
|
+
}, [formState.description])
|
95
|
+
|
66
96
|
useEffect(() => {
|
67
97
|
;(async () => {
|
68
98
|
const { token } = checkParams(["token"])
|
@@ -74,6 +104,7 @@ const SyllabusEditor: React.FC = () => {
|
|
74
104
|
userId: user.id,
|
75
105
|
rigoToken: user.rigobot.key,
|
76
106
|
user,
|
107
|
+
publicToken: "",
|
77
108
|
})
|
78
109
|
}
|
79
110
|
}
|
@@ -108,19 +139,22 @@ const SyllabusEditor: React.FC = () => {
|
|
108
139
|
{ type: "assistant", content: "" },
|
109
140
|
])
|
110
141
|
|
111
|
-
const res = await
|
112
|
-
|
142
|
+
const res = await publicInteractiveCreation(
|
143
|
+
{
|
144
|
+
courseInfo: `${JSON.stringify(syllabus.courseInfo)}
|
113
145
|
${
|
114
146
|
uploadedFiles.length > 0 &&
|
115
147
|
"The user has uploaded files, take them in consideration while generating the course structure." +
|
116
148
|
JSON.stringify(uploadedFiles)
|
117
149
|
}
|
118
150
|
`,
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
151
|
+
prevInteractions:
|
152
|
+
messages
|
153
|
+
.map((message) => `${message.type}: ${message.content}`)
|
154
|
+
.join("\n") + `\nUSER: ${prompt}`,
|
155
|
+
},
|
156
|
+
auth.publicToken
|
157
|
+
)
|
124
158
|
|
125
159
|
const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
|
126
160
|
parseLesson(step, syllabus.lessons)
|
@@ -166,14 +200,14 @@ const SyllabusEditor: React.FC = () => {
|
|
166
200
|
setShowLoginModal(true)
|
167
201
|
return
|
168
202
|
}
|
169
|
-
const success = await useConsumableCall(
|
170
|
-
|
171
|
-
|
172
|
-
)
|
173
|
-
if (!success) {
|
174
|
-
|
175
|
-
|
176
|
-
}
|
203
|
+
// const success = await useConsumableCall(
|
204
|
+
// auth.bcToken,
|
205
|
+
// "ai-course-generation"
|
206
|
+
// )
|
207
|
+
// if (!success) {
|
208
|
+
// toast.error("You don't have enough credits to generate a course!")
|
209
|
+
// return
|
210
|
+
// }
|
177
211
|
syllabus.sources = uploadedFiles
|
178
212
|
console.log(syllabus)
|
179
213
|
|
@@ -1,4 +1,5 @@
|
|
1
|
-
export const RIGOBOT_HOST = "https://rigobot.herokuapp.com"
|
1
|
+
// export const RIGOBOT_HOST = "https://rigobot.herokuapp.com"
|
2
|
+
export const RIGOBOT_HOST = "https://rigobot-test-cca7d841c9d8.herokuapp.com"
|
2
3
|
export const BREATHECODE_HOST = "https://breathecode.herokuapp.com"
|
3
4
|
|
4
5
|
export const RIGO_FLOAT_GIT =
|
@@ -364,3 +364,58 @@ export const reWriteTitle = async (title: string, token: string) => {
|
|
364
364
|
return `${title} ${Math.random().toString(36).substring(2, 6)}`
|
365
365
|
}
|
366
366
|
}
|
367
|
+
|
368
|
+
export async function registerUserWithFormData(
|
369
|
+
firstName: string,
|
370
|
+
lastName: string,
|
371
|
+
email: string
|
372
|
+
): Promise<any> {
|
373
|
+
const formData = new FormData()
|
374
|
+
formData.append("1_first_name", firstName)
|
375
|
+
formData.append("1_last_name", lastName)
|
376
|
+
formData.append("1_email", email)
|
377
|
+
|
378
|
+
// Puedes modificar este objeto para agregar info real de tracking si la tienes.
|
379
|
+
const conversionArray = [
|
380
|
+
"$K1",
|
381
|
+
{
|
382
|
+
user_agent: navigator.userAgent,
|
383
|
+
landing_url: "www.learnpack.co/my-tutorials",
|
384
|
+
conversion_url: "app.learnpack.co/login",
|
385
|
+
translations: "$undefined",
|
386
|
+
utm_placement: "$undefined",
|
387
|
+
utm_referrer: "$undefined",
|
388
|
+
utm_medium: "$undefined",
|
389
|
+
utm_source: "$undefined",
|
390
|
+
utm_term: "$undefined",
|
391
|
+
utm_content: "$undefined",
|
392
|
+
utm_campaign: "$undefined",
|
393
|
+
internal_cta_placement: "$undefined",
|
394
|
+
internal_cta_content: "$undefined",
|
395
|
+
internal_cta_campaign: "$undefined",
|
396
|
+
},
|
397
|
+
]
|
398
|
+
|
399
|
+
formData.append("0", JSON.stringify(conversionArray))
|
400
|
+
|
401
|
+
try {
|
402
|
+
const response = await axios.post(
|
403
|
+
"https://www.learnpack.co/register",
|
404
|
+
formData,
|
405
|
+
{
|
406
|
+
headers: {
|
407
|
+
"Content-Type": "multipart/form-data",
|
408
|
+
},
|
409
|
+
}
|
410
|
+
)
|
411
|
+
return response.data
|
412
|
+
} catch (error: any) {
|
413
|
+
// Manejo de errores básico
|
414
|
+
console.error(error, "ERROR REGISTERING IN LEARNPACK")
|
415
|
+
return {
|
416
|
+
success: false,
|
417
|
+
message: error?.response?.data?.detail || "Registration error",
|
418
|
+
data: error?.response?.data || null,
|
419
|
+
}
|
420
|
+
}
|
421
|
+
}
|