@learnpack/learnpack 5.0.68 → 5.0.70

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.
@@ -1,20 +1,18 @@
1
- import { useEffect, useState } from "react"
2
- import StepWizard, { Step } from "./components/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
- // import SyllabusEditor from "./components/SyllabusEditor"
14
-
15
- const exampleContentIndex = `-Introduction to AI: Explain what is AI and its applications
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,29 @@ 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,
200
48
  })
201
-
202
- setSteps(steps.filter((step) => step.slug !== "login"))
49
+ setFormState({
50
+ variables: [
51
+ "description",
52
+ "duration",
53
+ "targetAudience",
54
+ "hasContentIndex",
55
+ ],
56
+ })
57
+ } else {
58
+ setFormState({
59
+ variables: [
60
+ "description",
61
+ "login",
62
+ "duration",
63
+ "targetAudience",
64
+ "hasContentIndex",
65
+ ],
66
+ })
203
67
  }
204
68
  }
205
69
 
@@ -226,19 +90,194 @@ function App() {
226
90
  hasContentIndex: false,
227
91
  contentIndex: "",
228
92
  isCompleted: false,
229
- currentStep: 0,
93
+ currentStep: "description",
230
94
  })
231
95
  }
232
96
 
97
+ const buildSteps = () => {
98
+ const steps = [
99
+ {
100
+ title: "Provide a description for your tutorial",
101
+ slug: "description",
102
+ isCompleted: false,
103
+ content: (
104
+ <textarea
105
+ placeholder="Describe your tutorial"
106
+ className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
107
+ value={formState.description}
108
+ onChange={(e) => {
109
+ setFormState({
110
+ description: e.target.value,
111
+ })
112
+ }}
113
+ />
114
+ ),
115
+ },
116
+ {
117
+ title:
118
+ "First you need to login with 4Geeks.com to use AI Generation tool for creators. ",
119
+ slug: "login",
120
+ isCompleted: false,
121
+ content: (
122
+ <Login
123
+ onFinish={() => {
124
+ setFormState({
125
+ currentStep: "duration",
126
+ })
127
+ }}
128
+ />
129
+ ),
130
+ },
131
+ {
132
+ title: "What is the estimated duration for this tutorial?",
133
+ slug: "duration",
134
+ isCompleted: false,
135
+ content: (
136
+ <div className="flex flex-col md:flex-row gap-2">
137
+ <SelectableCard
138
+ title="Around 30 minutes"
139
+ // subtitle="This is a tutorial that will take 30 minutes to complete"
140
+ onClick={() => {
141
+ setFormState({
142
+ duration: 30,
143
+ currentStep: "targetAudience",
144
+ })
145
+ }}
146
+ selected={formState.duration === 30}
147
+ />
148
+ <SelectableCard
149
+ title="Around 1 hour"
150
+ // subtitle="This is a tutorial that will take 1 hour to complete"
151
+ onClick={() => {
152
+ setFormState({
153
+ duration: 60,
154
+ currentStep: "targetAudience",
155
+ })
156
+ }}
157
+ selected={formState.duration === 60}
158
+ />
159
+ <SelectableCard
160
+ title="Around 2 hours"
161
+ // subtitle="This is a tutorial that will take 2 hours to complete"
162
+ onClick={() => {
163
+ setFormState({
164
+ duration: 120,
165
+ currentStep: "targetAudience",
166
+ })
167
+ }}
168
+ selected={formState.duration === 120}
169
+ />
170
+ </div>
171
+ ),
172
+ },
173
+ {
174
+ title: "What is the target audience for this tutorial?",
175
+ slug: "targetAudience",
176
+ isCompleted: false,
177
+ content: (
178
+ <div className="flex flex-row gap-4">
179
+ <textarea
180
+ placeholder="Describe the target audience for this tutorial"
181
+ className="w-full h-24 border-2 border-gray-300 rounded-md p-2 bg-white"
182
+ defaultValue={formState.targetAudience}
183
+ onBlur={(e) => {
184
+ setFormState({
185
+ targetAudience: e.target.value,
186
+ })
187
+ }}
188
+ />
189
+ </div>
190
+ ),
191
+ },
192
+ {
193
+ title: "Do you have a content index for this tutorial?",
194
+ slug: "hasContentIndex",
195
+ isCompleted: false,
196
+ content: (
197
+ <div className="flex flex-col md:flex-row gap-2 justify-center">
198
+ <SelectableCard
199
+ title="Yes"
200
+ onClick={() => {
201
+ setFormState({
202
+ hasContentIndex: true,
203
+ currentStep: "contentIndex",
204
+ variables: [...formState.variables, "contentIndex"],
205
+ })
206
+ }}
207
+ selected={false}
208
+ />
209
+ <SelectableCard
210
+ title="No, help me create one"
211
+ onClick={() => {
212
+ setFormState({
213
+ hasContentIndex: false,
214
+ isCompleted: true,
215
+ })
216
+ }}
217
+ selected={false}
218
+ />
219
+ </div>
220
+ ),
221
+ },
222
+ {
223
+ title:
224
+ "Write or paste your content index below, each topic should be defined on a new line, here is an example:",
225
+ slug: "contentIndex",
226
+ isCompleted: false,
227
+ content: (
228
+ <>
229
+ <textarea
230
+ placeholder="Provide a content index for this tutorial"
231
+ className="w-full h-40 border-2 border-gray-300 rounded-md p-2"
232
+ value={formState.contentIndex}
233
+ onChange={(e) => {
234
+ setFormState({
235
+ contentIndex: e.target.value,
236
+ // isCompleted: true,
237
+ })
238
+ }}
239
+ />
240
+ <FileUploader
241
+ onResult={(files) => {
242
+ // toast.success("File uploaded successfully")
243
+ let allFilesText = ``
244
+ files.forEach((file) => {
245
+ allFilesText += `<FILE NAME="${file.name}">${file.text}</FILE>\n`
246
+ })
247
+ setFormState({
248
+ contentIndex: allFilesText,
249
+ })
250
+ }}
251
+ />
252
+ </>
253
+ ),
254
+ },
255
+ ]
256
+
257
+ return steps.filter(
258
+ (step) =>
259
+ formState.variables.includes(step.slug) ||
260
+ step.slug === formState.currentStep
261
+ )
262
+ }
233
263
  return (
234
264
  <>
235
265
  {formState.isCompleted ? (
236
266
  <Loader
237
267
  text="Learnpack is setting up your tutorial. It may take a moment..."
238
- icon={SVGS.rigoSoftBlue}
268
+ icon={<img src={"rigo-float.gif"} alt="rigo" className="w-20 h-20" />}
239
269
  />
240
270
  ) : (
241
- <StepWizard initialStep={formState.currentStep} steps={steps} />
271
+ <StepWizard
272
+ formState={formState}
273
+ steps={buildSteps()}
274
+ setFormState={setFormState}
275
+ onFinish={() => {
276
+ setFormState({
277
+ isCompleted: true,
278
+ })
279
+ }}
280
+ />
242
281
  )}
243
282
  </>
244
283
  )
@@ -196,7 +196,7 @@ export const SVGS = {
196
196
  ),
197
197
  clip: (
198
198
  <svg
199
- fill="#C7F3FD"
199
+ fill="var(--four-geeks-blue)"
200
200
  width="20"
201
201
  height="20"
202
202
  viewBox="0 0 32 32"
@@ -223,4 +223,44 @@ export const SVGS = {
223
223
  />
224
224
  </svg>
225
225
  ),
226
+ downArrow: (
227
+ <svg
228
+ width="7"
229
+ height="6"
230
+ viewBox="0 0 7 6"
231
+ fill="none"
232
+ xmlns="http://www.w3.org/2000/svg"
233
+ >
234
+ <path
235
+ fillRule="evenodd"
236
+ clipRule="evenodd"
237
+ d="M0.100496 1.0705C0.22866 0.950272 0.430019 0.956708 0.550243 1.08487L3.5 4.2294L6.44976 1.08487C6.56998 0.956708 6.77134 0.950272 6.8995 1.0705C7.02767 1.19072 7.0341 1.39208 6.91388 1.52024L3.73206 4.91216C3.67191 4.97628 3.58792 5.01266 3.5 5.01266C3.41208 5.01266 3.32809 4.97628 3.26794 4.91216L0.086122 1.52024C-0.0341029 1.39208 -0.0276674 1.19072 0.100496 1.0705Z"
238
+ fill="#0084FF"
239
+ />
240
+ </svg>
241
+ ),
242
+ bottom: (
243
+ <svg
244
+ width="12"
245
+ height="12"
246
+ viewBox="0 0 24 24"
247
+ fill="none"
248
+ xmlns="http://www.w3.org/2000/svg"
249
+ >
250
+ <path
251
+ d="M11.9297 2V22"
252
+ stroke="#0084FF"
253
+ strokeWidth="1"
254
+ strokeLinecap="round"
255
+ strokeLinejoin="round"
256
+ />
257
+ <path
258
+ d="M19 16L14 21.1599C13.7437 21.4336 13.434 21.6519 13.0899 21.801C12.7459 21.9502 12.375 22.0271 12 22.0271C11.625 22.0271 11.2541 21.9502 10.9101 21.801C10.566 21.6519 10.2563 21.4336 10 21.1599L5 16"
259
+ stroke="#0084FF"
260
+ strokeWidth="1"
261
+ strokeLinecap="round"
262
+ strokeLinejoin="round"
263
+ />
264
+ </svg>
265
+ ),
226
266
  }
@@ -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">{index}</span>
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>
@@ -12,7 +12,7 @@ export const Message: React.FC<TMessage> = ({ type, content }) => {
12
12
 
13
13
  const isLoading = isAI && !content
14
14
  return isLoading ? (
15
- <RigoLoader text="Thinking..." svg={SVGS.rigoSoftBlue} />
15
+ <RigoLoader text="Thinking..." svg={<img src="rigo-float.gif" />} />
16
16
  ) : (
17
17
  <div
18
18
  className={`flex items-start space-x-2 p-3 rounded-md border ${
@@ -1,45 +1,60 @@
1
- import React, { useEffect, useState } from "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
- initialStep?: number
13
+ formState: any
14
+ setFormState: (formState: any) => void
15
+ onFinish: () => void
13
16
  }
14
17
 
15
- const StepWizard: React.FC<Props> = ({ steps, initialStep = 0 }) => {
16
- const [currentStep, setCurrentStep] = useState(initialStep)
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
- if (currentStep < totalSteps - 1) setCurrentStep((s) => s + 1)
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
- if (currentStep > 0) setCurrentStep((s) => s - 1)
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[currentStep].subtitle || "Setting up your tutorial"}
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">{currentStep + 1}.</span>
39
- {steps[currentStep].title}
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">{steps[currentStep].content}</div>
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 === currentStep ? "bg-blue-600" : "bg-gray-300"
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={currentStep === 0}
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 ➡