├── app
├── favicon.ico
├── globals.css
├── layout.js
├── student-onboarding
│ └── page.jsx
└── page.js
├── jsconfig.json
├── next.config.js
├── postcss.config.js
├── redux
├── Providers.jsx
├── store.js
└── slices
│ └── onboardingStudentsSlice.js
├── components
├── MultiStepForm
│ ├── Steps.jsx
│ ├── Step.jsx
│ ├── StepForms
│ │ ├── FormConfirmation.jsx
│ │ ├── TechnicalSkillsForm.jsx
│ │ ├── PreferredLanguageForm.jsx
│ │ ├── EducationBackgroundForm.jsx
│ │ ├── ProgrammingExperienceForm.jsx
│ │ └── PersonalInfoForm.jsx
│ └── StepForm.jsx
└── FormInputs
│ ├── SelectInput.jsx
│ ├── TextAreaInput.jsx
│ ├── TextInput.jsx
│ ├── QuillEditor.jsx
│ ├── ToggleInput.jsx
│ ├── NavButtons.jsx
│ ├── ImageInput.jsx
│ ├── SubmitButton.jsx
│ └── ArrayItemsInput.jsx
├── .gitignore
├── tailwind.config.js
├── public
├── vercel.svg
└── next.svg
├── package.json
└── README.md
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MUKE-coder/multi-step-form-with-redux-and-nextjs/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {}
3 |
4 | module.exports = nextConfig
5 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | min-height: 100vh;
7 | }
8 |
--------------------------------------------------------------------------------
/redux/Providers.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { Provider } from "react-redux";
4 | import { store } from "./store";
5 |
6 | export default function Providers({ children }) {
7 | return {children} ;
8 | }
9 |
--------------------------------------------------------------------------------
/redux/store.js:
--------------------------------------------------------------------------------
1 | import onboardingStudentsSlice from "./slices/onboardingStudentsSlice";
2 |
3 | const { configureStore } = require("@reduxjs/toolkit");
4 |
5 | //create a store and give it slices
6 | export const store = configureStore({
7 | reducer: {
8 | onboarding: onboardingStudentsSlice,
9 | },
10 | });
11 |
12 | //Store =whole state
13 |
--------------------------------------------------------------------------------
/components/MultiStepForm/Steps.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Step from "./Step";
3 |
4 | export default function Steps({ steps }) {
5 | return (
6 |
7 | {steps.map((step, i) => {
8 | return ;
9 | })}
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Inter } from "next/font/google";
2 | import "./globals.css";
3 | import Providers from "@/redux/Providers";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({ children }) {
13 | return (
14 |
15 |
16 | {children}
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
12 | "gradient-conic":
13 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
14 | },
15 | },
16 | },
17 | plugins: [require("@tailwindcss/forms")],
18 | };
19 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "onboarding-students",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@reduxjs/toolkit": "^2.0.1",
13 | "lucide-react": "^0.309.0",
14 | "next": "14.0.4",
15 | "react": "^18",
16 | "react-dom": "^18",
17 | "react-hook-form": "^7.49.3",
18 | "react-hot-toast": "^2.4.1",
19 | "react-redux": "^9.1.0",
20 | "redux": "^5.0.1"
21 | },
22 | "devDependencies": {
23 | "@tailwindcss/forms": "^0.5.7",
24 | "autoprefixer": "^10.0.1",
25 | "postcss": "^8",
26 | "tailwindcss": "^3.3.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/redux/slices/onboardingStudentsSlice.js:
--------------------------------------------------------------------------------
1 | // Create a slice
2 |
3 | const { createSlice } = require("@reduxjs/toolkit");
4 |
5 | const initialState = {
6 | currentStep: 1,
7 | formData: {},
8 | };
9 | const onboardingStudentsSlice = createSlice({
10 | name: "onboarding",
11 | initialState,
12 | reducers: {
13 | setCurrentStep: (state, action) => {
14 | state.currentStep = action.payload;
15 | },
16 | updateFormData: (state, action) => {
17 | state.formData = {
18 | ...state.formData,
19 | ...action.payload,
20 | };
21 | },
22 | },
23 | });
24 | export const { setCurrentStep, updateFormData } =
25 | onboardingStudentsSlice.actions;
26 | export default onboardingStudentsSlice.reducer;
27 |
--------------------------------------------------------------------------------
/components/MultiStepForm/Step.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { useSelector } from "react-redux";
4 |
5 | export default function Step({ step }) {
6 | const { number, title } = step;
7 | const currentStep = useSelector((store) => store.onboarding.currentStep);
8 | return (
9 |
10 |
15 | {number}
16 |
17 |
18 |
Step {number}
19 | {title}
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/components/MultiStepForm/StepForms/FormConfirmation.jsx:
--------------------------------------------------------------------------------
1 | import NavButtons from "@/components/FormInputs/NavButtons";
2 | import React from "react";
3 | import { useSelector } from "react-redux";
4 |
5 | export default function FormConfirmation() {
6 | const formData = useSelector((store) => store.onboarding.formData);
7 | async function processData(data) {
8 | console.log(formData);
9 | }
10 | return (
11 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/components/FormInputs/SelectInput.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function SelectInput({
4 | label,
5 | name,
6 | register,
7 | className = "sm:col-span-2",
8 | options = [],
9 | multiple = false,
10 | }) {
11 | return (
12 |
13 |
17 | {label}
18 |
19 |
20 |
27 | {options.map((option, i) => {
28 | return (
29 |
30 | {option.title}
31 |
32 | );
33 | })}
34 |
35 |
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/components/MultiStepForm/StepForm.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useSelector } from "react-redux";
4 | import EducationBackgroundForm from "./StepForms/EducationBackgroundForm";
5 | import FormConfirmation from "./StepForms/FormConfirmation";
6 | import PersonalInfoForm from "./StepForms/PersonalInfoForm";
7 | import PreferredLanguageForm from "./StepForms/PreferredLanguageForm";
8 | import ProgrammingExperienceForm from "./StepForms/ProgrammingExperienceForm";
9 | import TechnicalSkillsForm from "./StepForms/TechnicalSkillsForm";
10 |
11 | export default function StepForm() {
12 | const currentStep = useSelector((store) => store.onboarding.currentStep);
13 | function renderFormByStep(step) {
14 | if (step === 1) {
15 | return ;
16 | } else if (step === 2) {
17 | return ;
18 | } else if (step === 3) {
19 | return ;
20 | } else if (step === 4) {
21 | return ;
22 | } else if (step === 5) {
23 | return ;
24 | } else if (step === 6) {
25 | return ;
26 | }
27 | }
28 | return <>{renderFormByStep(currentStep)}>;
29 | }
30 |
--------------------------------------------------------------------------------
/components/FormInputs/TextAreaInput.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | export default function TextareaInput({
3 | label,
4 | name,
5 | register,
6 | errors,
7 | isRequired = true,
8 | className = "sm:col-span-2",
9 | }) {
10 | return (
11 |
12 |
16 | {label}
17 |
18 |
19 |
28 | {errors[`${name}`] && (
29 | {label} is required
30 | )}
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/app/student-onboarding/page.jsx:
--------------------------------------------------------------------------------
1 | import StepForm from "@/components/MultiStepForm/StepForm";
2 | import Steps from "@/components/MultiStepForm/Steps";
3 | import React from "react";
4 |
5 | export default function StudentOnboardingPage() {
6 | const steps = [
7 | {
8 | number: 1,
9 | title: "Personal Info",
10 | },
11 | {
12 | number: 2,
13 | title: "Education",
14 | },
15 | {
16 | number: 3,
17 | title: "Programming Experience",
18 | },
19 | {
20 | number: 4,
21 | title: "Preferred Programming Languages",
22 | },
23 | {
24 | number: 5,
25 | title: "Technical Skills Assessment",
26 | },
27 | {
28 | number: 6,
29 | title: "Submit and Confirmation",
30 | },
31 | ];
32 | return (
33 |
34 |
35 | {/* Steps */}
36 |
37 | {/* Form */}
38 |
39 |
40 |
41 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/components/FormInputs/TextInput.jsx:
--------------------------------------------------------------------------------
1 | export default function TextInput({
2 | label,
3 | name,
4 | register,
5 | errors,
6 | isRequired = true,
7 | type = "text",
8 | className = "sm:col-span-2",
9 | defaultValue = "",
10 | }) {
11 | return (
12 |
13 |
17 | {label}
18 |
19 |
20 |
30 | {errors[`${name}`] && (
31 | {label} is required
32 | )}
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/FormInputs/QuillEditor.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import ReactQuill from "react-quill";
4 | import "react-quill/dist/quill.snow.css";
5 | export default function QuillEditor({
6 | label,
7 | className = "sm:col-span-2",
8 | value,
9 | onChange,
10 | }) {
11 | const modules = {
12 | toolbar: [
13 | [{ header: [1, 2, false] }],
14 | ["bold", "italic", "underline", "strike", "blockquote"],
15 | [{ list: "ordered" }, { list: "bullet" }],
16 | ["link", "color", "image"],
17 | [{ "code-block": true }],
18 | ["clean"],
19 | ],
20 | };
21 | const formats = [
22 | "header",
23 | "bold",
24 | "italic",
25 | "underline",
26 | "strike",
27 | "blockquote",
28 | "list",
29 | "bullet",
30 | "link",
31 | "indent",
32 | "image",
33 | "code-block",
34 | "color",
35 | ];
36 | return (
37 |
38 |
42 | {label}
43 |
44 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/components/FormInputs/ToggleInput.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function ToggleInput({
4 | label,
5 | name,
6 | trueTitle,
7 | falseTitle,
8 | register,
9 | className = "sm:col-span-2 flex flex-wrap",
10 | }) {
11 | return (
12 |
13 |
14 |
15 | {label}
16 |
17 |
18 |
19 |
20 |
25 |
26 |
27 | {name ? `${trueTitle}` : `${falseTitle}`}
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/components/FormInputs/NavButtons.jsx:
--------------------------------------------------------------------------------
1 | import { setCurrentStep } from "@/redux/slices/onboardingStudentsSlice";
2 | import { ChevronLast, ChevronLeft, ChevronRight } from "lucide-react";
3 | import React from "react";
4 | import { useDispatch, useSelector } from "react-redux";
5 | export default function NavButtons() {
6 | const currentStep = useSelector((store) => store.onboarding.currentStep);
7 | const dispatch = useDispatch();
8 | function handlePrevious() {
9 | dispatch(setCurrentStep(currentStep - 1));
10 | }
11 | return (
12 |
13 | {currentStep > 1 && (
14 |
19 |
20 | Previous
21 |
22 | )}
23 |
27 |
28 | {currentStep === 6 ? "Confirm and Submit" : "Save and Continue"}
29 |
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/components/FormInputs/ImageInput.jsx:
--------------------------------------------------------------------------------
1 | import { UploadDropzone } from "@/lib/uploadthing";
2 | import { Pencil } from "lucide-react";
3 | import Image from "next/image";
4 | import React from "react";
5 | import toast from "react-hot-toast";
6 |
7 | export default function ImageInput({
8 | label,
9 | imageUrl = "",
10 | setImageUrl,
11 | className = "col-span-full",
12 | endpoint = "",
13 | }) {
14 | return (
15 |
16 |
17 |
21 | {label}
22 |
23 | {imageUrl && (
24 |
setImageUrl("")}
26 | type="button"
27 | className="flex space-x-2 bg-slate-900 rounded-md shadow text-slate-50 py-2 px-4"
28 | >
29 |
30 | Change Image
31 |
32 | )}
33 |
34 | {imageUrl ? (
35 |
42 | ) : (
43 |
{
46 | setImageUrl(res[0].url);
47 | // Do something with the response
48 | toast.success("Image Upload complete");
49 | console.log("Files: ", res);
50 | console.log("Upload Completed");
51 | }}
52 | onUploadError={(error) => {
53 | toast.error("Image Upload Failed, Try Again");
54 | // Do something with the error.
55 | console.log(`ERROR! ${error.message}`, error);
56 | }}
57 | />
58 | )}
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/components/MultiStepForm/StepForms/TechnicalSkillsForm.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import NavButtons from "@/components/FormInputs/NavButtons";
3 | import SelectInput from "@/components/FormInputs/SelectInput";
4 | import TextareaInput from "@/components/FormInputs/TextAreaInput";
5 | import TextInput from "@/components/FormInputs/TextInput";
6 | import {
7 | setCurrentStep,
8 | updateFormData,
9 | } from "@/redux/slices/onboardingStudentsSlice";
10 | import React, { useState } from "react";
11 | import { useForm } from "react-hook-form";
12 | import { useDispatch, useSelector } from "react-redux";
13 |
14 | export default function PersonalInfoForm() {
15 | const currentStep = useSelector((store) => store.onboarding.currentStep);
16 | const formData = useSelector((store) => store.onboarding.formData);
17 | console.log(formData, currentStep);
18 | const [loading, setLoading] = useState(false);
19 | const {
20 | register,
21 | reset,
22 | watch,
23 | handleSubmit,
24 | formState: { errors },
25 | } = useForm({
26 | defaultValues: {
27 | ...formData,
28 | },
29 | });
30 | const dispatch = useDispatch();
31 | async function processData(data) {
32 | // All Data is Valid
33 | //Collect all the data
34 | //Update Data in the Global state
35 | dispatch(updateFormData(data));
36 | //Make API Request to Save the Data also in the DB
37 |
38 | //Update the Current Step
39 | dispatch(setCurrentStep(currentStep + 1));
40 | // console.log(data);
41 | }
42 | return (
43 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/components/MultiStepForm/StepForms/PreferredLanguageForm.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import NavButtons from "@/components/FormInputs/NavButtons";
3 | import SelectInput from "@/components/FormInputs/SelectInput";
4 | import TextInput from "@/components/FormInputs/TextInput";
5 | import {
6 | setCurrentStep,
7 | updateFormData,
8 | } from "@/redux/slices/onboardingStudentsSlice";
9 | import React, { useState } from "react";
10 | import { useForm } from "react-hook-form";
11 | import { useDispatch, useSelector } from "react-redux";
12 |
13 | export default function PreferredLanguageForm() {
14 | const currentStep = useSelector((store) => store.onboarding.currentStep);
15 | const formData = useSelector((store) => store.onboarding.formData);
16 | console.log(formData, currentStep);
17 | const [loading, setLoading] = useState(false);
18 | const languages = [
19 | {
20 | id: "javascript",
21 | title: "JavaScript",
22 | },
23 | {
24 | id: "python",
25 | title: "Python",
26 | },
27 | {
28 | id: "php",
29 | title: "PHP",
30 | },
31 | {
32 | id: "c++",
33 | title: "C++",
34 | },
35 | {
36 | id: "java",
37 | title: "Java",
38 | },
39 | ];
40 | const {
41 | register,
42 | reset,
43 | watch,
44 | handleSubmit,
45 | formState: { errors },
46 | } = useForm({
47 | defaultValues: {
48 | ...formData,
49 | },
50 | });
51 | const dispatch = useDispatch();
52 | async function processData(data) {
53 | // All Data is Valid
54 | //Collect all the data
55 | //Update Data in the Global state
56 | dispatch(updateFormData(data));
57 | //Make API Request to Save the Data also in the DB
58 |
59 | //Update the Current Step
60 | dispatch(setCurrentStep(currentStep + 1));
61 | // console.log(data);
62 | }
63 | return (
64 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/components/MultiStepForm/StepForms/EducationBackgroundForm.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import NavButtons from "@/components/FormInputs/NavButtons";
3 | import SelectInput from "@/components/FormInputs/SelectInput";
4 | import TextInput from "@/components/FormInputs/TextInput";
5 | import {
6 | setCurrentStep,
7 | updateFormData,
8 | } from "@/redux/slices/onboardingStudentsSlice";
9 | import React, { useState } from "react";
10 | import { useForm } from "react-hook-form";
11 | import { useDispatch, useSelector } from "react-redux";
12 |
13 | export default function EducationBackgroundForm() {
14 | const currentStep = useSelector((store) => store.onboarding.currentStep);
15 | const formData = useSelector((store) => store.onboarding.formData);
16 | console.log(formData, currentStep);
17 | const [loading, setLoading] = useState(false);
18 | const {
19 | register,
20 | reset,
21 | watch,
22 | handleSubmit,
23 | formState: { errors },
24 | } = useForm({
25 | defaultValues: {
26 | ...formData,
27 | },
28 | });
29 | const dispatch = useDispatch();
30 | async function processData(data) {
31 | // All Data is Valid
32 | //Collect all the data
33 | //Update Data in the Global state
34 | dispatch(updateFormData(data));
35 | //Make API Request to Save the Data also in the DB
36 |
37 | //Update the Current Step
38 | dispatch(setCurrentStep(currentStep + 1));
39 | // console.log(data);
40 | }
41 | return (
42 |
73 | );
74 | }
75 |
--------------------------------------------------------------------------------
/components/FormInputs/SubmitButton.jsx:
--------------------------------------------------------------------------------
1 | import { Plus } from "lucide-react";
2 | import React from "react";
3 |
4 | export default function SubmitButton({
5 | isLoading = false,
6 | buttonTitle,
7 | loadingButtonTitle,
8 | }) {
9 | return (
10 |
11 | {isLoading ? (
12 |
17 |
25 |
29 |
33 |
34 | {loadingButtonTitle}
35 |
36 | ) : (
37 |
41 |
42 | {buttonTitle}
43 |
44 | )}
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/components/MultiStepForm/StepForms/ProgrammingExperienceForm.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import NavButtons from "@/components/FormInputs/NavButtons";
3 | import SelectInput from "@/components/FormInputs/SelectInput";
4 | import TextInput from "@/components/FormInputs/TextInput";
5 | import {
6 | setCurrentStep,
7 | updateFormData,
8 | } from "@/redux/slices/onboardingStudentsSlice";
9 | import React, { useState } from "react";
10 | import { useForm } from "react-hook-form";
11 | import { useDispatch, useSelector } from "react-redux";
12 |
13 | export default function ProgrammingExperienceForm() {
14 | const currentStep = useSelector((store) => store.onboarding.currentStep);
15 | const formData = useSelector((store) => store.onboarding.formData);
16 | console.log(formData, currentStep);
17 | const [loading, setLoading] = useState(false);
18 | const languages = [
19 | {
20 | id: "javascript",
21 | title: "JavaScript",
22 | },
23 | {
24 | id: "python",
25 | title: "Python",
26 | },
27 | {
28 | id: "php",
29 | title: "PHP",
30 | },
31 | ];
32 | const {
33 | register,
34 | reset,
35 | watch,
36 | handleSubmit,
37 | formState: { errors },
38 | } = useForm({
39 | defaultValues: {
40 | ...formData,
41 | },
42 | });
43 | const dispatch = useDispatch();
44 | async function processData(data) {
45 | // All Data is Valid
46 | //Collect all the data
47 | //Update Data in the Global state
48 | dispatch(updateFormData(data));
49 | //Make API Request to Save the Data also in the DB
50 |
51 | //Update the Current Step
52 | dispatch(setCurrentStep(currentStep + 1));
53 | // console.log(data);
54 | //Update the url
55 | }
56 | return (
57 |
89 | );
90 | }
91 |
--------------------------------------------------------------------------------
/components/MultiStepForm/StepForms/PersonalInfoForm.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import NavButtons from "@/components/FormInputs/NavButtons";
3 | import SelectInput from "@/components/FormInputs/SelectInput";
4 | import TextInput from "@/components/FormInputs/TextInput";
5 | import {
6 | setCurrentStep,
7 | updateFormData,
8 | } from "@/redux/slices/onboardingStudentsSlice";
9 | import React, { useState } from "react";
10 | import { useForm } from "react-hook-form";
11 | import { useDispatch, useSelector } from "react-redux";
12 |
13 | export default function PersonalInfoForm() {
14 | const currentStep = useSelector((store) => store.onboarding.currentStep);
15 | const formData = useSelector((store) => store.onboarding.formData);
16 | console.log(formData, currentStep);
17 | const [loading, setLoading] = useState(false);
18 | const gender = [
19 | {
20 | id: "male",
21 | title: "Male",
22 | },
23 | {
24 | id: "female",
25 | title: "Female",
26 | },
27 | ];
28 | const {
29 | register,
30 | reset,
31 | watch,
32 | handleSubmit,
33 | formState: { errors },
34 | } = useForm({
35 | defaultValues: {
36 | ...formData,
37 | },
38 | });
39 | const dispatch = useDispatch();
40 | async function processData(data) {
41 | // All Data is Valid
42 | //Collect all the data
43 | //Update Data in the Global state
44 | dispatch(updateFormData(data));
45 | //Make API Request to Save the Data also in the DB
46 |
47 | //Update the Current Step
48 | dispatch(setCurrentStep(currentStep + 1));
49 | // console.log(data);
50 | }
51 | return (
52 |
109 | );
110 | }
111 |
--------------------------------------------------------------------------------
/components/FormInputs/ArrayItemsInput.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { Plus, X } from "lucide-react";
3 | import React, { useState } from "react";
4 |
5 | export default function ArrayItemsInput({ setItems, items = [], itemTitle }) {
6 | const [item, setItem] = useState("");
7 | const [showTagForm, setShowTagForm] = useState(false);
8 | function addItem() {
9 | if (!item) return;
10 | setItems([...items, item]);
11 | setItem("");
12 | }
13 | function removeItem(index) {
14 | const newItems = [...items];
15 | newItems.splice(index, 1);
16 | setItems(newItems);
17 | }
18 | // items END
19 | return (
20 |
21 | {showTagForm ? (
22 |
23 |
24 |
41 |
setItem(e.target.value)}
44 | type="text"
45 | id="voice-search"
46 | className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-lime-500 focus:border-lime-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-lime-500 dark:focus:border-lime-500"
47 | placeholder={`Create a ${itemTitle}`}
48 | />
49 |
50 |
55 |
56 | Add
57 |
58 |
setShowTagForm(false)}
61 | className="ml-3 shrink-0 w-8 h-8 bg-red-400 rounded-full flex items-center justify-center"
62 | >
63 |
64 |
65 |
66 | ) : (
67 |
setShowTagForm(true)}
69 | type="button"
70 | className="flex items-center space-x-2 text-slate-800 dark:text-slate-300 py-2 px-4 "
71 | >
72 |
73 | Add {itemTitle}
74 |
75 | )}
76 |
77 | {items.map((item, i) => {
78 | return (
79 |
removeItem(i)}
81 | key={i}
82 | className="bg-slate-200 flex space-x-2 items-center dark:bg-slate-600 px-4 py-2 rounded-lg cursor-pointer dark:text-slate-300 text-slate-800"
83 | >
84 |
{item}
85 |
86 |
87 | );
88 | })}
89 |
90 |
91 | );
92 | }
93 |
--------------------------------------------------------------------------------
/app/page.js:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
8 | Get started by editing
9 | app/page.js
10 |
11 |
29 |
30 |
31 |
32 |
40 |
41 |
42 |
111 |
112 | )
113 | }
114 |
--------------------------------------------------------------------------------