@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.
Files changed (30) hide show
  1. package/README.md +13 -13
  2. package/lib/commands/init.js +1 -1
  3. package/lib/commands/serve.js +12 -3
  4. package/lib/creatorDist/assets/{index-C7bLE5wU.js → index-Dm2fdeOs.js} +9076 -8991
  5. package/lib/creatorDist/assets/{index-C_Rp91QE.css → index-k_eF99Sf.css} +103 -45
  6. package/lib/creatorDist/index.html +2 -2
  7. package/lib/creatorDist/rigo-float.gif +0 -0
  8. package/oclif.manifest.json +1 -1
  9. package/package.json +1 -1
  10. package/src/commands/init.ts +1 -1
  11. package/src/commands/serve.ts +13 -5
  12. package/src/creator/public/rigo-float.gif +0 -0
  13. package/src/creator/src/App.tsx +203 -163
  14. package/src/creator/src/assets/svgs.tsx +2 -2
  15. package/src/creator/src/components/LessonItem.tsx +10 -3
  16. package/src/creator/src/components/Login.tsx +14 -18
  17. package/src/creator/src/components/Message.tsx +12 -2
  18. package/src/creator/src/components/Redirector.tsx +12 -0
  19. package/src/creator/src/components/StepWizard.tsx +32 -18
  20. package/src/creator/src/components/syllabus/ContentIndex.tsx +47 -20
  21. package/src/creator/src/components/syllabus/Sidebar.tsx +114 -0
  22. package/src/creator/src/components/syllabus/SyllabusEditor.tsx +20 -125
  23. package/src/creator/src/index.css +2 -6
  24. package/src/creator/src/main.tsx +0 -1
  25. package/src/creator/src/utils/store.ts +14 -3
  26. package/src/creatorDist/assets/{index-C7bLE5wU.js → index-Dm2fdeOs.js} +9076 -8991
  27. package/src/creatorDist/assets/{index-C_Rp91QE.css → index-k_eF99Sf.css} +103 -45
  28. package/src/creatorDist/index.html +2 -2
  29. package/src/creatorDist/rigo-float.gif +0 -0
  30. package/src/utils/creds.json +13 -0
@@ -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,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: 0,
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={SVGS.rigoSoftBlue}
269
+ icon={<img src={"creator/rigo-float.gif"} alt="rigo" className="w-20 h-20" />}
239
270
  />
240
271
  ) : (
241
- <StepWizard initialStep={formState.currentStep} steps={steps} />
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="#C7F3FD"
193
+ fill="var(--four-geeks-blue)"
194
194
  />
195
195
  </svg>
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"
@@ -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>
@@ -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={SVGS.rigoSoftBlue} />
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
- <span className="mt-1">{isAI ? SVGS.rigoSoftBlue : SVGS.user}</span>
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, { 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 ➡