@learnpack/learnpack 5.0.69 → 5.0.71
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/init.js +1 -1
- package/lib/commands/serve.js +12 -3
- package/lib/creatorDist/assets/{index-C7bLE5wU.js → index-Dm2fdeOs.js} +9076 -8991
- package/lib/creatorDist/assets/{index-C_Rp91QE.css → index-k_eF99Sf.css} +103 -45
- package/lib/creatorDist/index.html +2 -2
- package/lib/creatorDist/rigo-float.gif +0 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/init.ts +1 -1
- package/src/commands/serve.ts +13 -5
- package/src/creator/public/rigo-float.gif +0 -0
- package/src/creator/src/App.tsx +203 -163
- package/src/creator/src/assets/svgs.tsx +2 -2
- package/src/creator/src/components/LessonItem.tsx +10 -3
- package/src/creator/src/components/Login.tsx +14 -18
- package/src/creator/src/components/Message.tsx +12 -2
- package/src/creator/src/components/Redirector.tsx +12 -0
- package/src/creator/src/components/StepWizard.tsx +32 -18
- package/src/creator/src/components/syllabus/ContentIndex.tsx +47 -20
- package/src/creator/src/components/syllabus/Sidebar.tsx +114 -0
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +20 -125
- package/src/creator/src/index.css +2 -6
- package/src/creator/src/main.tsx +0 -1
- package/src/creator/src/utils/store.ts +14 -3
- package/src/creatorDist/assets/{index-C7bLE5wU.js → index-Dm2fdeOs.js} +9076 -8991
- package/src/creatorDist/assets/{index-C_Rp91QE.css → index-k_eF99Sf.css} +103 -45
- package/src/creatorDist/index.html +2 -2
- package/src/creatorDist/rigo-float.gif +0 -0
- package/src/utils/creds.json +13 -0
package/src/creator/src/App.tsx
CHANGED
@@ -1,20 +1,18 @@
|
|
1
|
-
import { useEffect
|
2
|
-
import StepWizard
|
1
|
+
import { useEffect } from "react"
|
2
|
+
import StepWizard from "./components/StepWizard"
|
3
3
|
import SelectableCard from "./components/SelectableCard"
|
4
4
|
import Loader from "./components/Loader"
|
5
|
-
import { SVGS } from "./assets/svgs"
|
6
5
|
import { useNavigate } from "react-router"
|
7
6
|
import Login, { loginWithToken } from "./components/Login"
|
8
7
|
import { useShallow } from "zustand/react/shallow"
|
9
8
|
import useStore from "./utils/store"
|
10
9
|
import { interactiveCreation } from "./utils/rigo"
|
11
10
|
import { checkParams, parseLesson } from "./utils/lib"
|
11
|
+
import FileUploader from "./components/FileUploader"
|
12
12
|
|
13
|
-
//
|
14
|
-
|
15
|
-
|
16
|
-
-Introduction to Machine Learning: Explain What is machine learning and its aplications
|
17
|
-
-What is an AI Model: Explain what is an AI model an its applications`
|
13
|
+
// const exampleContentIndex = `-Introduction to AI: Explain what is AI and its applications
|
14
|
+
// -Introduction to Machine Learning: Explain What is machine learning and its aplications
|
15
|
+
// -What is an AI Model: Explain what is an AI model an its applications`
|
18
16
|
|
19
17
|
function App() {
|
20
18
|
const navigate = useNavigate()
|
@@ -29,157 +27,8 @@ function App() {
|
|
29
27
|
}))
|
30
28
|
)
|
31
29
|
|
32
|
-
// const [currentStep, setCurrentStep] = useState(0)
|
33
|
-
const [steps, setSteps] = useState<Array<Step>>([
|
34
|
-
{
|
35
|
-
title: "Provide a description for your tutorial",
|
36
|
-
slug: "description",
|
37
|
-
content: (
|
38
|
-
<textarea
|
39
|
-
placeholder="Describe your tutorial"
|
40
|
-
className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
|
41
|
-
defaultValue={formState.description}
|
42
|
-
onBlur={(e) => {
|
43
|
-
setFormState({
|
44
|
-
description: e.target.value,
|
45
|
-
currentStep: 1,
|
46
|
-
})
|
47
|
-
}}
|
48
|
-
/>
|
49
|
-
),
|
50
|
-
},
|
51
|
-
{
|
52
|
-
title:
|
53
|
-
"First you need to login with 4Geeks.com to use AI Generation tool for creators. ",
|
54
|
-
slug: "login",
|
55
|
-
content: (
|
56
|
-
<Login
|
57
|
-
onFinish={() => {
|
58
|
-
setFormState({
|
59
|
-
currentStep: 2,
|
60
|
-
})
|
61
|
-
}}
|
62
|
-
/>
|
63
|
-
),
|
64
|
-
},
|
65
|
-
{
|
66
|
-
title: "What is the estimated duration for this tutorial?",
|
67
|
-
slug: "duration",
|
68
|
-
content: (
|
69
|
-
<div className="flex flex-row gap-4">
|
70
|
-
<SelectableCard
|
71
|
-
title="Around 30 minutes"
|
72
|
-
// subtitle="This is a tutorial that will take 30 minutes to complete"
|
73
|
-
onClick={() => {
|
74
|
-
setFormState({
|
75
|
-
duration: 30,
|
76
|
-
currentStep: 3,
|
77
|
-
})
|
78
|
-
}}
|
79
|
-
selected={false}
|
80
|
-
/>
|
81
|
-
<SelectableCard
|
82
|
-
title="Around 1 hour"
|
83
|
-
// subtitle="This is a tutorial that will take 1 hour to complete"
|
84
|
-
onClick={() => {
|
85
|
-
setFormState({
|
86
|
-
duration: 60,
|
87
|
-
currentStep: 3,
|
88
|
-
})
|
89
|
-
}}
|
90
|
-
selected={false}
|
91
|
-
/>
|
92
|
-
<SelectableCard
|
93
|
-
title="Around 2 hours"
|
94
|
-
// subtitle="This is a tutorial that will take 2 hours to complete"
|
95
|
-
onClick={() => {
|
96
|
-
setFormState({
|
97
|
-
duration: 120,
|
98
|
-
currentStep: 3,
|
99
|
-
})
|
100
|
-
}}
|
101
|
-
selected={false}
|
102
|
-
/>
|
103
|
-
</div>
|
104
|
-
),
|
105
|
-
},
|
106
|
-
{
|
107
|
-
title: "What is the target audience for this tutorial?",
|
108
|
-
slug: "target-audience",
|
109
|
-
content: (
|
110
|
-
<div className="flex flex-row gap-4">
|
111
|
-
<textarea
|
112
|
-
placeholder="Describe the target audience for this tutorial"
|
113
|
-
className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
|
114
|
-
defaultValue={formState.targetAudience}
|
115
|
-
onBlur={(e) => {
|
116
|
-
setFormState({
|
117
|
-
targetAudience: e.target.value,
|
118
|
-
currentStep: 3,
|
119
|
-
})
|
120
|
-
}}
|
121
|
-
/>
|
122
|
-
</div>
|
123
|
-
),
|
124
|
-
},
|
125
|
-
{
|
126
|
-
title: "Do you have a content index for this tutorial?",
|
127
|
-
slug: "content-index",
|
128
|
-
content: (
|
129
|
-
<div className="flex flex-row gap-4 justify-center">
|
130
|
-
<SelectableCard
|
131
|
-
title="Yes"
|
132
|
-
onClick={() => {
|
133
|
-
setFormState({
|
134
|
-
hasContentIndex: true,
|
135
|
-
currentStep: 4,
|
136
|
-
})
|
137
|
-
setSteps([
|
138
|
-
...steps,
|
139
|
-
{
|
140
|
-
title:
|
141
|
-
"Write or paste your content index below, each topic should be defined on a new line, here is an example:",
|
142
|
-
slug: "content-index",
|
143
|
-
content: (
|
144
|
-
<textarea
|
145
|
-
placeholder="Provide a content index for this tutorial"
|
146
|
-
className="w-full h-24 border-2 border-gray-300 rounded-md p-2"
|
147
|
-
defaultValue={exampleContentIndex}
|
148
|
-
onBlur={(e) => {
|
149
|
-
setFormState({
|
150
|
-
contentIndex: e.target.value,
|
151
|
-
isCompleted: true,
|
152
|
-
})
|
153
|
-
}}
|
154
|
-
/>
|
155
|
-
),
|
156
|
-
},
|
157
|
-
])
|
158
|
-
}}
|
159
|
-
selected={false}
|
160
|
-
/>
|
161
|
-
<SelectableCard
|
162
|
-
title="No, help me create one"
|
163
|
-
onClick={() => {
|
164
|
-
setFormState({
|
165
|
-
hasContentIndex: false,
|
166
|
-
isCompleted: true,
|
167
|
-
})
|
168
|
-
|
169
|
-
// createTutorial()
|
170
|
-
// setCurrentStep(4)
|
171
|
-
}}
|
172
|
-
selected={false}
|
173
|
-
/>
|
174
|
-
</div>
|
175
|
-
),
|
176
|
-
},
|
177
|
-
])
|
178
|
-
|
179
30
|
useEffect(() => {
|
180
|
-
console.log(formState, "FORM STATE")
|
181
31
|
if (formState.isCompleted) {
|
182
|
-
// navigate("/syllabus")
|
183
32
|
handleCreateTutorial()
|
184
33
|
}
|
185
34
|
}, [formState])
|
@@ -192,14 +41,30 @@ function App() {
|
|
192
41
|
const { token } = checkParams()
|
193
42
|
if (token) {
|
194
43
|
const user = await loginWithToken(token)
|
195
|
-
console.log(user, "user at the beginning")
|
196
44
|
setAuth({
|
197
45
|
bcToken: token,
|
198
46
|
userId: user.id,
|
199
47
|
rigoToken: user.rigobot.key,
|
48
|
+
user: user,
|
49
|
+
})
|
50
|
+
setFormState({
|
51
|
+
variables: [
|
52
|
+
"description",
|
53
|
+
"duration",
|
54
|
+
"targetAudience",
|
55
|
+
"hasContentIndex",
|
56
|
+
],
|
57
|
+
})
|
58
|
+
} else {
|
59
|
+
setFormState({
|
60
|
+
variables: [
|
61
|
+
"description",
|
62
|
+
"login",
|
63
|
+
"duration",
|
64
|
+
"targetAudience",
|
65
|
+
"hasContentIndex",
|
66
|
+
],
|
200
67
|
})
|
201
|
-
|
202
|
-
setSteps(steps.filter((step) => step.slug !== "login"))
|
203
68
|
}
|
204
69
|
}
|
205
70
|
|
@@ -226,19 +91,194 @@ function App() {
|
|
226
91
|
hasContentIndex: false,
|
227
92
|
contentIndex: "",
|
228
93
|
isCompleted: false,
|
229
|
-
currentStep:
|
94
|
+
currentStep: "description",
|
230
95
|
})
|
231
96
|
}
|
232
97
|
|
98
|
+
const buildSteps = () => {
|
99
|
+
const steps = [
|
100
|
+
{
|
101
|
+
title: "Provide a description for your tutorial",
|
102
|
+
slug: "description",
|
103
|
+
isCompleted: false,
|
104
|
+
content: (
|
105
|
+
<textarea
|
106
|
+
placeholder="Describe your tutorial"
|
107
|
+
className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
|
108
|
+
value={formState.description}
|
109
|
+
onChange={(e) => {
|
110
|
+
setFormState({
|
111
|
+
description: e.target.value,
|
112
|
+
})
|
113
|
+
}}
|
114
|
+
/>
|
115
|
+
),
|
116
|
+
},
|
117
|
+
{
|
118
|
+
title:
|
119
|
+
"First you need to login with 4Geeks.com to use AI Generation tool for creators. ",
|
120
|
+
slug: "login",
|
121
|
+
isCompleted: false,
|
122
|
+
content: (
|
123
|
+
<Login
|
124
|
+
onFinish={() => {
|
125
|
+
setFormState({
|
126
|
+
currentStep: "duration",
|
127
|
+
})
|
128
|
+
}}
|
129
|
+
/>
|
130
|
+
),
|
131
|
+
},
|
132
|
+
{
|
133
|
+
title: "What is the estimated duration for this tutorial?",
|
134
|
+
slug: "duration",
|
135
|
+
isCompleted: false,
|
136
|
+
content: (
|
137
|
+
<div className="flex flex-col md:flex-row gap-2">
|
138
|
+
<SelectableCard
|
139
|
+
title="Around 30 minutes"
|
140
|
+
// subtitle="This is a tutorial that will take 30 minutes to complete"
|
141
|
+
onClick={() => {
|
142
|
+
setFormState({
|
143
|
+
duration: 30,
|
144
|
+
currentStep: "targetAudience",
|
145
|
+
})
|
146
|
+
}}
|
147
|
+
selected={formState.duration === 30}
|
148
|
+
/>
|
149
|
+
<SelectableCard
|
150
|
+
title="Around 1 hour"
|
151
|
+
// subtitle="This is a tutorial that will take 1 hour to complete"
|
152
|
+
onClick={() => {
|
153
|
+
setFormState({
|
154
|
+
duration: 60,
|
155
|
+
currentStep: "targetAudience",
|
156
|
+
})
|
157
|
+
}}
|
158
|
+
selected={formState.duration === 60}
|
159
|
+
/>
|
160
|
+
<SelectableCard
|
161
|
+
title="Around 2 hours"
|
162
|
+
// subtitle="This is a tutorial that will take 2 hours to complete"
|
163
|
+
onClick={() => {
|
164
|
+
setFormState({
|
165
|
+
duration: 120,
|
166
|
+
currentStep: "targetAudience",
|
167
|
+
})
|
168
|
+
}}
|
169
|
+
selected={formState.duration === 120}
|
170
|
+
/>
|
171
|
+
</div>
|
172
|
+
),
|
173
|
+
},
|
174
|
+
{
|
175
|
+
title: "What is the target audience for this tutorial?",
|
176
|
+
slug: "targetAudience",
|
177
|
+
isCompleted: false,
|
178
|
+
content: (
|
179
|
+
<div className="flex flex-row gap-4">
|
180
|
+
<textarea
|
181
|
+
placeholder="Describe the target audience for this tutorial"
|
182
|
+
className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
|
183
|
+
defaultValue={formState.targetAudience}
|
184
|
+
onBlur={(e) => {
|
185
|
+
setFormState({
|
186
|
+
targetAudience: e.target.value,
|
187
|
+
})
|
188
|
+
}}
|
189
|
+
/>
|
190
|
+
</div>
|
191
|
+
),
|
192
|
+
},
|
193
|
+
{
|
194
|
+
title: "Do you have a content index for this tutorial?",
|
195
|
+
slug: "hasContentIndex",
|
196
|
+
isCompleted: false,
|
197
|
+
content: (
|
198
|
+
<div className="flex flex-col md:flex-row gap-2 justify-center">
|
199
|
+
<SelectableCard
|
200
|
+
title="Yes"
|
201
|
+
onClick={() => {
|
202
|
+
setFormState({
|
203
|
+
hasContentIndex: true,
|
204
|
+
currentStep: "contentIndex",
|
205
|
+
variables: [...formState.variables, "contentIndex"],
|
206
|
+
})
|
207
|
+
}}
|
208
|
+
selected={false}
|
209
|
+
/>
|
210
|
+
<SelectableCard
|
211
|
+
title="No, help me create one"
|
212
|
+
onClick={() => {
|
213
|
+
setFormState({
|
214
|
+
hasContentIndex: false,
|
215
|
+
isCompleted: true,
|
216
|
+
})
|
217
|
+
}}
|
218
|
+
selected={false}
|
219
|
+
/>
|
220
|
+
</div>
|
221
|
+
),
|
222
|
+
},
|
223
|
+
{
|
224
|
+
title:
|
225
|
+
"Write or paste your content index below, each topic should be defined on a new line, here is an example:",
|
226
|
+
slug: "contentIndex",
|
227
|
+
isCompleted: false,
|
228
|
+
content: (
|
229
|
+
<>
|
230
|
+
<textarea
|
231
|
+
placeholder="Provide a content index for this tutorial"
|
232
|
+
className="w-full h-40 border-2 border-gray-300 rounded-md p-2"
|
233
|
+
value={formState.contentIndex}
|
234
|
+
onChange={(e) => {
|
235
|
+
setFormState({
|
236
|
+
contentIndex: e.target.value,
|
237
|
+
// isCompleted: true,
|
238
|
+
})
|
239
|
+
}}
|
240
|
+
/>
|
241
|
+
<FileUploader
|
242
|
+
onResult={(files) => {
|
243
|
+
// toast.success("File uploaded successfully")
|
244
|
+
let allFilesText = ``
|
245
|
+
files.forEach((file) => {
|
246
|
+
allFilesText += `<FILE NAME="${file.name}">${file.text}</FILE>\n`
|
247
|
+
})
|
248
|
+
setFormState({
|
249
|
+
contentIndex: allFilesText,
|
250
|
+
})
|
251
|
+
}}
|
252
|
+
/>
|
253
|
+
</>
|
254
|
+
),
|
255
|
+
},
|
256
|
+
]
|
257
|
+
|
258
|
+
return steps.filter(
|
259
|
+
(step) =>
|
260
|
+
formState.variables.includes(step.slug) ||
|
261
|
+
step.slug === formState.currentStep
|
262
|
+
)
|
263
|
+
}
|
233
264
|
return (
|
234
265
|
<>
|
235
266
|
{formState.isCompleted ? (
|
236
267
|
<Loader
|
237
268
|
text="Learnpack is setting up your tutorial. It may take a moment..."
|
238
|
-
icon={
|
269
|
+
icon={<img src={"creator/rigo-float.gif"} alt="rigo" className="w-20 h-20" />}
|
239
270
|
/>
|
240
271
|
) : (
|
241
|
-
<StepWizard
|
272
|
+
<StepWizard
|
273
|
+
formState={formState}
|
274
|
+
steps={buildSteps()}
|
275
|
+
setFormState={setFormState}
|
276
|
+
onFinish={() => {
|
277
|
+
setFormState({
|
278
|
+
isCompleted: true,
|
279
|
+
})
|
280
|
+
}}
|
281
|
+
/>
|
242
282
|
)}
|
243
283
|
</>
|
244
284
|
)
|
@@ -190,13 +190,13 @@ export const SVGS = {
|
|
190
190
|
>
|
191
191
|
<path
|
192
192
|
d="M6.12812 3.64814L0.384343 0.0378137C0.213539 -0.0696296 0 0.0647108 0 0.279307V5.34833C0 5.47075 0.0733163 5.57892 0.18082 5.61498L2.90314 6.52715L5.01075 7.23331C5.25193 7.31415 5.25193 7.68577 5.01075 7.76646L2.90314 8.47262L0.18082 9.38509C0.0733163 9.42114 0 9.52931 0 9.65173V14.7206C0 14.9354 0.213539 15.0695 0.384343 14.9622L6.12812 11.3519L11.8719 7.7416C12.0427 7.6343 12.0427 7.36576 11.8719 7.25847L6.12812 3.64814Z"
|
193
|
-
fill="
|
193
|
+
fill="var(--four-geeks-blue)"
|
194
194
|
/>
|
195
195
|
</svg>
|
196
196
|
),
|
197
197
|
clip: (
|
198
198
|
<svg
|
199
|
-
fill="
|
199
|
+
fill="var(--four-geeks-blue)"
|
200
200
|
width="20"
|
201
201
|
height="20"
|
202
202
|
viewBox="0 0 32 32"
|
@@ -11,15 +11,22 @@ export interface Lesson {
|
|
11
11
|
|
12
12
|
interface LessonItemProps {
|
13
13
|
lesson: Lesson
|
14
|
-
index: string
|
15
14
|
isNew: boolean
|
16
15
|
onChange: (id: string, newTitle: string) => void
|
17
16
|
onRemove: (id: string) => void
|
18
17
|
}
|
19
18
|
|
19
|
+
function cleanFloatString(input: string): string {
|
20
|
+
const num = parseFloat(input)
|
21
|
+
const isInteger = Number.isInteger(num)
|
22
|
+
|
23
|
+
return isInteger
|
24
|
+
? Math.floor(num).toString().padStart(2, "0")
|
25
|
+
: num.toString()
|
26
|
+
}
|
27
|
+
|
20
28
|
export const LessonItem: React.FC<LessonItemProps> = ({
|
21
29
|
lesson,
|
22
|
-
index,
|
23
30
|
onChange,
|
24
31
|
onRemove,
|
25
32
|
isNew,
|
@@ -32,7 +39,7 @@ export const LessonItem: React.FC<LessonItemProps> = ({
|
|
32
39
|
isNew ? "bg-yellow-50" : ""
|
33
40
|
}`}
|
34
41
|
>
|
35
|
-
<span className="index-circle">{
|
42
|
+
<span className="index-circle">{cleanFloatString(lesson.id)}</span>
|
36
43
|
<span className="text-gray-500 text-sm">
|
37
44
|
{lesson.type[0] + lesson.type.slice(1).toLowerCase()} ●
|
38
45
|
</span>
|
@@ -126,6 +126,7 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
|
|
126
126
|
bcToken: isLoggedId.token,
|
127
127
|
userId: isLoggedId.user.id,
|
128
128
|
rigoToken: isLoggedId.rigobot.key,
|
129
|
+
user: isLoggedId.user,
|
129
130
|
})
|
130
131
|
setIsLoading(false)
|
131
132
|
onFinish()
|
@@ -163,6 +164,7 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
|
|
163
164
|
bcToken: token,
|
164
165
|
userId: user.id,
|
165
166
|
rigoToken: user.rigobot.key,
|
167
|
+
user: user,
|
166
168
|
})
|
167
169
|
onFinish()
|
168
170
|
}
|
@@ -172,24 +174,6 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
|
|
172
174
|
return (
|
173
175
|
<>
|
174
176
|
<div className="max-w-sm mx-auto mt-10 bg-white p-8 rounded-xl shadow-md text-center">
|
175
|
-
<div className="flex justify-between items-center mb-4">
|
176
|
-
<h2 className="text-xl font-semibold">Login</h2>
|
177
|
-
<div className="bg-blue-50 text-sm p-2 rounded text-left">
|
178
|
-
<p className="text-gray-700 m-0">You don't have an account?</p>
|
179
|
-
<a
|
180
|
-
href="https://4geeks.com/pricing?plan=basic"
|
181
|
-
className="text-blue-600 font-medium"
|
182
|
-
>
|
183
|
-
Register here.
|
184
|
-
</a>
|
185
|
-
</div>
|
186
|
-
</div>
|
187
|
-
|
188
|
-
<p className="text-gray-600 mb-6">
|
189
|
-
Log in to 4Geeks to get performance statistics, access to our AI
|
190
|
-
mentor, and many other benefits
|
191
|
-
</p>
|
192
|
-
|
193
177
|
<button
|
194
178
|
onClick={redirectGithub}
|
195
179
|
className="w-full border border-gray-300 py-2 rounded-md font-semibold flex items-center justify-center gap-2 mb-4 cursor-pointer"
|
@@ -251,6 +235,18 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
|
|
251
235
|
Login with Email
|
252
236
|
</button>
|
253
237
|
)}
|
238
|
+
<div className="flex justify-between items-center mt-4">
|
239
|
+
<div className="bg-blue-50 text-sm p-2 rounded text-left">
|
240
|
+
<p className="text-gray-700 m-0">You don't have an account?</p>
|
241
|
+
<a
|
242
|
+
href="https://4geeks.com/checkout?plan=4geeks-creator"
|
243
|
+
target="_blank"
|
244
|
+
className="text-blue-600 font-medium"
|
245
|
+
>
|
246
|
+
Register here.
|
247
|
+
</a>
|
248
|
+
</div>
|
249
|
+
</div>
|
254
250
|
</div>
|
255
251
|
</>
|
256
252
|
)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { RigoLoader } from "./RigoLoader"
|
2
2
|
|
3
3
|
import { SVGS } from "../assets/svgs"
|
4
|
+
import useStore from "../utils/store"
|
4
5
|
|
5
6
|
export type TMessage = {
|
6
7
|
type: "user" | "assistant"
|
@@ -8,11 +9,14 @@ export type TMessage = {
|
|
8
9
|
}
|
9
10
|
|
10
11
|
export const Message: React.FC<TMessage> = ({ type, content }) => {
|
12
|
+
const user = useStore((state) => state.auth.user)
|
11
13
|
const isAI = type === "assistant"
|
12
14
|
|
13
15
|
const isLoading = isAI && !content
|
16
|
+
|
17
|
+
console.log("user", user)
|
14
18
|
return isLoading ? (
|
15
|
-
<RigoLoader text="Thinking..." svg={
|
19
|
+
<RigoLoader text="Thinking..." svg={<img src="rigo-float.gif" />} />
|
16
20
|
) : (
|
17
21
|
<div
|
18
22
|
className={`flex items-start space-x-2 p-3 rounded-md border ${
|
@@ -21,7 +25,13 @@ export const Message: React.FC<TMessage> = ({ type, content }) => {
|
|
21
25
|
: "bg-blue-50 border-blue-200 text-blue-900"
|
22
26
|
}`}
|
23
27
|
>
|
24
|
-
|
28
|
+
{isAI ? (
|
29
|
+
<span className="mt-1">{SVGS.rigoSoftBlue}</span>
|
30
|
+
) : user?.profile?.avatar_url ? (
|
31
|
+
<img src={user?.profile?.avatar_url} className="w-6 h-6 rounded-full" />
|
32
|
+
) : (
|
33
|
+
<span className="mt-1">{SVGS.user}</span>
|
34
|
+
)}
|
25
35
|
<p className="text-sm leading-relaxed">{content}</p>
|
26
36
|
</div>
|
27
37
|
)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { useEffect } from "react"
|
2
|
+
import toast from "react-hot-toast"
|
3
|
+
export const Redirector = ({ to }: { to: string }) => {
|
4
|
+
useEffect(() => {
|
5
|
+
window.location.href = to
|
6
|
+
window.location.reload()
|
7
|
+
toast.success("Redirecting to " + to)
|
8
|
+
console.log("Redirecting to " + to)
|
9
|
+
}, [])
|
10
|
+
|
11
|
+
return <h1>Redirecting to {to}...</h1>
|
12
|
+
}
|
@@ -1,45 +1,60 @@
|
|
1
|
-
import React
|
1
|
+
import React from "react"
|
2
2
|
|
3
3
|
export type Step = {
|
4
4
|
title: string
|
5
5
|
subtitle?: string
|
6
6
|
content: React.ReactNode
|
7
7
|
slug: string
|
8
|
+
isCompleted: boolean
|
8
9
|
}
|
9
10
|
|
10
11
|
type Props = {
|
11
12
|
steps: Step[]
|
12
|
-
|
13
|
+
formState: any
|
14
|
+
setFormState: (formState: any) => void
|
15
|
+
onFinish: () => void
|
13
16
|
}
|
14
17
|
|
15
|
-
const StepWizard: React.FC<Props> = ({
|
16
|
-
|
18
|
+
const StepWizard: React.FC<Props> = ({
|
19
|
+
steps,
|
20
|
+
formState,
|
21
|
+
setFormState,
|
22
|
+
onFinish,
|
23
|
+
}) => {
|
24
|
+
const currentStep = formState.currentStep
|
25
|
+
const index = steps.findIndex((step) => step.slug === currentStep)
|
17
26
|
const totalSteps = steps.length
|
18
27
|
|
19
28
|
const goNext = () => {
|
20
|
-
|
29
|
+
const index = steps.findIndex((step) => step.slug === currentStep)
|
30
|
+
if (index < totalSteps - 1)
|
31
|
+
setFormState({
|
32
|
+
...formState,
|
33
|
+
currentStep: steps[index + 1].slug,
|
34
|
+
})
|
21
35
|
}
|
22
36
|
|
23
37
|
const goBack = () => {
|
24
|
-
|
38
|
+
const index = steps.findIndex((step) => step.slug === currentStep)
|
39
|
+
if (index > 0)
|
40
|
+
setFormState({ ...formState, currentStep: steps[index - 1].slug })
|
25
41
|
}
|
26
42
|
|
27
|
-
useEffect(() => {
|
28
|
-
setCurrentStep(initialStep)
|
29
|
-
}, [initialStep])
|
30
|
-
|
31
43
|
return (
|
32
44
|
<div className="min-h-screen flex flex-col items-center justify-center text-center px-4">
|
33
45
|
<div className=" rounded-xl p-8 w-full max-w-xl">
|
34
46
|
<h5 className="text-sm text-blue-400 uppercase mb-2">
|
35
|
-
{steps
|
47
|
+
{steps.find((step) => step.slug === currentStep)?.subtitle ||
|
48
|
+
"Setting up your tutorial"}
|
36
49
|
</h5>
|
37
50
|
<h2 className="text-lg font-medium mb-6">
|
38
|
-
<span className="text-blue-600 mr-1">{
|
39
|
-
{steps
|
51
|
+
<span className="text-blue-600 mr-1">{index + 1}.</span>
|
52
|
+
{steps.find((step) => step.slug === currentStep)?.title}
|
40
53
|
</h2>
|
41
54
|
|
42
|
-
<div className="mb-6">
|
55
|
+
<div className="mb-6">
|
56
|
+
{steps.find((step) => step.slug === currentStep)?.content}
|
57
|
+
</div>
|
43
58
|
|
44
59
|
{/* Dot Indicators */}
|
45
60
|
<div className="flex justify-center mb-4 space-x-2">
|
@@ -47,7 +62,7 @@ const StepWizard: React.FC<Props> = ({ steps, initialStep = 0 }) => {
|
|
47
62
|
<span
|
48
63
|
key={i}
|
49
64
|
className={`h-2 w-2 rounded-full ${
|
50
|
-
i ===
|
65
|
+
i === index ? "bg-blue-600" : "bg-gray-300"
|
51
66
|
}`}
|
52
67
|
/>
|
53
68
|
))}
|
@@ -57,14 +72,13 @@ const StepWizard: React.FC<Props> = ({ steps, initialStep = 0 }) => {
|
|
57
72
|
<div className="flex justify-between">
|
58
73
|
<button
|
59
74
|
onClick={goBack}
|
60
|
-
disabled={
|
75
|
+
disabled={index === 0}
|
61
76
|
className="text-sm text-gray-500 hover:text-gray-900 disabled:opacity-40"
|
62
77
|
>
|
63
78
|
⬅ Back
|
64
79
|
</button>
|
65
80
|
<button
|
66
|
-
onClick={goNext}
|
67
|
-
disabled={currentStep === totalSteps - 1}
|
81
|
+
onClick={index === totalSteps - 1 ? onFinish : goNext}
|
68
82
|
className="text-sm text-blue-600 hover:text-blue-800 disabled:opacity-40"
|
69
83
|
>
|
70
84
|
Next ➡
|