diff --git a/apps/web/app/orgs/[orgslug]/layout.tsx b/apps/web/app/orgs/[orgslug]/layout.tsx
index de924c3d..56735360 100644
--- a/apps/web/app/orgs/[orgslug]/layout.tsx
+++ b/apps/web/app/orgs/[orgslug]/layout.tsx
@@ -3,6 +3,7 @@ import { OrgProvider } from '@components/Contexts/OrgContext'
import NextTopLoader from 'nextjs-toploader';
import Toast from '@components/StyledElements/Toast/Toast'
import '@styles/globals.css'
+import Onboarding from '@components/Onboarding/Onboarding';
export default function RootLayout({
children,
@@ -16,6 +17,7 @@ export default function RootLayout({
+
{children}
diff --git a/apps/web/components/Contexts/CourseContext.tsx b/apps/web/components/Contexts/CourseContext.tsx
index f407cd00..d33bf38d 100644
--- a/apps/web/components/Contexts/CourseContext.tsx
+++ b/apps/web/components/Contexts/CourseContext.tsx
@@ -4,7 +4,6 @@ import { swrFetcher } from '@services/utils/ts/requests'
import React, { createContext, useContext, useEffect, useReducer } from 'react'
import useSWR from 'swr'
import { useLHSession } from '@components/Contexts/LHSessionContext'
-import PageLoading from '@components/Objects/Loaders/PageLoading'
export const CourseContext = createContext(null)
export const CourseDispatchContext = createContext(null)
diff --git a/apps/web/components/Onboarding/Onboarding.tsx b/apps/web/components/Onboarding/Onboarding.tsx
new file mode 100644
index 00000000..626eb83a
--- /dev/null
+++ b/apps/web/components/Onboarding/Onboarding.tsx
@@ -0,0 +1,277 @@
+import Modal from '@components/StyledElements/Modal/Modal';
+import Image, { StaticImageData } from 'next/image';
+import React, { useEffect, useState } from 'react';
+import OnBoardWelcome from '@public/onboarding/OnBoardWelcome.png';
+import OnBoardCourses from '@public/onboarding/OnBoardCourses.png';
+import OnBoardActivities from '@public/onboarding/OnBoardActivities.png';
+import OnBoardEditor from '@public/onboarding/OnBoardEditor.png';
+import OnBoardAI from '@public/onboarding/OnBoardAI.png';
+import OnBoardUGs from '@public/onboarding/OnBoardUGs.png';
+import OnBoardAccess from '@public/onboarding/OnBoardAccess.png';
+import OnBoardMore from '@public/onboarding/OnBoardMore.png';
+import { ArrowRight, Book, Check, Globe, Info, PictureInPicture, Sparkle, Sprout, SquareUser, Users } from 'lucide-react';
+import { useRouter } from 'next/navigation';
+import { getUriWithOrg } from '@services/config/config';
+import { useOrg } from '@components/Contexts/OrgContext';
+import useAdminStatus from '@components/Hooks/useAdminStatus';
+
+interface OnboardingStep {
+ imageSrc: StaticImageData;
+ title: string;
+ description: string;
+ buttons?: {
+ label: string;
+ action: () => void;
+ icon?: React.ReactNode;
+ }[];
+}
+
+const Onboarding: React.FC = () => {
+ const [currentStep, setCurrentStep] = useState(0);
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [isOnboardingComplete, setIsOnboardingComplete] = useState(true);
+ const router = useRouter();
+ const org = useOrg() as any;
+ const isUserAdmin = useAdminStatus() as any;
+
+ const onboardingData: OnboardingStep[] = [
+ {
+ imageSrc: OnBoardWelcome,
+ title: 'Teach the world!',
+ description: 'Welcome to LearnHouse, a LMS engineered for simplicity, ease of use and performance, meet the new way to create, share, and engage with educational content.',
+ },
+ {
+ imageSrc: OnBoardCourses,
+ title: 'Create Courses',
+ description: 'Courses are the main building blocks of LearnHouse, they always contain Chapters and Chapters contain Activities.',
+ buttons: [
+ {
+ label: 'Create New Course',
+ action: () => router.push(getUriWithOrg(org?.slug, '/courses?new=true')),
+ icon: ,
+ },
+ ],
+ },
+ {
+ imageSrc: OnBoardActivities,
+ title: 'Activities',
+ description: 'Activities are elements you can add to your Courses via Chapters, they can be : Dynamic Pages, Videos, Documents, Quizz and more soon.',
+ buttons: [
+ {
+ label: 'Learn more about activities',
+ action: () => window.open('https://university.learnhouse.io/course/be89716c-9992-44bb-81df-ef3d76e355ba', '_blank'),
+ icon: ,
+ },
+ ],
+ },
+ {
+ imageSrc: OnBoardEditor,
+ title: 'Dynamic pages and The Editor',
+ description: 'Dynamic pages are pages with dynamic content, like Notion pages they can contain various components like Quizzes, Images, Videos, Documents etc',
+ buttons: [
+ {
+ label: 'Learn more about Dynamic Pages and The Editor',
+ action: () => window.open('https://university.learnhouse.io/course/be89716c-9992-44bb-81df-ef3d76e355ba', '_blank'),
+ icon: ,
+ },
+ ],
+ },
+ {
+ imageSrc: OnBoardAI,
+ title: 'Artificial Intelligence',
+ description: 'Tools for tought made for teachers and students alike, context aware it can reply based on your courses and the unique content you create on LearnHouse',
+ buttons: [
+ {
+ label: 'Learn more about LearnHouse AI',
+ action: () => window.open('https://docs.learnhouse.app/features/ai/students', '_blank'),
+ icon: ,
+ },
+ ],
+ },
+ {
+ imageSrc: OnBoardUGs,
+ title: 'Group students and streamline access ',
+ description: 'With UserGroups you can separate students by Groups and give access to Courses depending on their needs',
+ buttons: [
+ {
+ label: 'Create UserGroups',
+ action: () => router.push(getUriWithOrg(org?.slug, '/dash/users/settings/usergroups')),
+ icon: ,
+ },
+ ],
+ },
+ {
+ imageSrc: OnBoardAccess,
+ title: 'Choose whether to make Courses available on the Web or not ',
+ description: 'You can choose to make your Courses discoverable from search engines and accesible to non authenticated users or to only give it to authenticated Users',
+ buttons: [
+
+ ],
+ },
+ {
+ imageSrc: OnBoardMore,
+ title: 'To infinity and beyond',
+ description: "To Learn more about LearnHouse, you're welcome to follow our Original courses on the LearnHouse University",
+ buttons: [
+ {
+ label: 'LearnHouse University',
+ action: () => window.open('https://university.learnhouse.io', '_blank'),
+ icon: ,
+ },
+ ],
+ },
+ ];
+
+ useEffect(() => {
+ // Check if onboarding is already completed in local storage
+ const isOnboardingCompleted = localStorage.getItem('isOnboardingCompleted');
+ setIsOnboardingComplete(isOnboardingCompleted === 'true');
+ setIsModalOpen(!isOnboardingCompleted); // Show modal if onboarding is not completed
+ }, []);
+
+ const nextStep = () => {
+ if (currentStep < onboardingData.length - 1) {
+ setCurrentStep(currentStep + 1);
+ } else {
+ // Mark onboarding as completed in local storage
+ localStorage.setItem('isOnboardingCompleted', 'true');
+ setIsModalOpen(false); // Close modal after completion
+ setIsOnboardingComplete(true); // Show success message
+ console.log('Onboarding completed');
+ }
+ };
+
+ const skipOnboarding = () => {
+ // Mark onboarding as completed in local storage
+ localStorage.setItem('isOnboardingCompleted', 'true');
+ setIsModalOpen(false); // Close modal after skipping
+ console.log('Onboarding skipped');
+ };
+
+ const goToStep = (index: number) => {
+ if (index >= 0 && index < onboardingData.length) {
+ setCurrentStep(index);
+ }
+ };
+
+ return (
+
+ {isUserAdmin.isAdmin && !isUserAdmin.loading && !isOnboardingComplete &&
+ }
+ dialogTrigger={
+
+
+ }
+ />}
+
+ );
+};
+
+interface OnboardingScreenProps {
+ step: OnboardingStep;
+ currentStep: number;
+ nextStep: () => void;
+ skipOnboarding: () => void;
+ goToStep: (index: number) => void;
+ setIsModalOpen: (value: boolean) => void;
+ onboardingData: OnboardingStep[];
+}
+
+const OnboardingScreen: React.FC = ({
+ step,
+ currentStep,
+ nextStep,
+ skipOnboarding,
+ goToStep,
+ onboardingData,
+ setIsModalOpen,
+}) => {
+ const isLastStep = currentStep === onboardingData.length - 1;
+
+ return (
+
+
+
+
+
+
+ {onboardingData.map((_, index) => (
+
goToStep(index)}
+ className={`h-[7px] w-auto ${index === currentStep ? 'bg-black' : 'bg-gray-300'} hover:bg-gray-700 rounded-lg shadow-md cursor-pointer`}
+ >
+ ))}
+
+
+
+
{step.title}
+
{step.description}
+
+
+
+
+
setIsModalOpen(false)}
+ >
+
+
+
+
+ {step.buttons?.map((button, index) => (
+
+
{button.label}
+ {button.icon}
+
+ ))}
+ {isLastStep ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+};
+
+export default Onboarding;
\ No newline at end of file
diff --git a/apps/web/components/StyledElements/Modal/Modal.tsx b/apps/web/components/StyledElements/Modal/Modal.tsx
index 51e12678..0b1ca9c7 100644
--- a/apps/web/components/StyledElements/Modal/Modal.tsx
+++ b/apps/web/components/StyledElements/Modal/Modal.tsx
@@ -6,8 +6,8 @@ import { blackA, mauve } from '@radix-ui/colors'
import { ButtonBlack } from '../Form/Form'
type ModalParams = {
- dialogTitle: string
- dialogDescription: string
+ dialogTitle?: string
+ dialogDescription?: string
dialogContent: React.ReactNode
dialogClose?: React.ReactNode | null
dialogTrigger?: React.ReactNode
@@ -16,6 +16,8 @@ type ModalParams = {
isDialogOpen?: boolean
minHeight?: 'sm' | 'md' | 'lg' | 'xl' | 'no-min'
minWidth?: 'sm' | 'md' | 'lg' | 'xl' | 'no-min'
+ customHeight?: string
+ customWidth?: string
}
const Modal = (params: ModalParams) => (
@@ -30,11 +32,14 @@ const Modal = (params: ModalParams) => (
className="overflow-auto scrollbar-w-2 scrollbar-h-2 scrollbar scrollbar-thumb-black/20 scrollbar-thumb-rounded-full scrollbar-track-rounded-full"
minHeight={params.minHeight}
minWidth={params.minWidth}
+
>
-
- {params.dialogTitle}
- {params.dialogDescription}
-
+ {params.dialogTitle && params.dialogDescription &&
+
+ {params.dialogTitle}
+ {params.dialogDescription}
+
+ }
{params.dialogContent}
{params.dialogClose ? (
diff --git a/apps/web/public/onboarding/OnBoardAI.png b/apps/web/public/onboarding/OnBoardAI.png
new file mode 100644
index 00000000..8db2dbe3
Binary files /dev/null and b/apps/web/public/onboarding/OnBoardAI.png differ
diff --git a/apps/web/public/onboarding/OnBoardAccess.png b/apps/web/public/onboarding/OnBoardAccess.png
new file mode 100644
index 00000000..e24be428
Binary files /dev/null and b/apps/web/public/onboarding/OnBoardAccess.png differ
diff --git a/apps/web/public/onboarding/OnBoardActivities.png b/apps/web/public/onboarding/OnBoardActivities.png
new file mode 100644
index 00000000..2456da88
Binary files /dev/null and b/apps/web/public/onboarding/OnBoardActivities.png differ
diff --git a/apps/web/public/onboarding/OnBoardCourses.png b/apps/web/public/onboarding/OnBoardCourses.png
new file mode 100644
index 00000000..7ac2e7ff
Binary files /dev/null and b/apps/web/public/onboarding/OnBoardCourses.png differ
diff --git a/apps/web/public/onboarding/OnBoardEditor.png b/apps/web/public/onboarding/OnBoardEditor.png
new file mode 100644
index 00000000..fd986665
Binary files /dev/null and b/apps/web/public/onboarding/OnBoardEditor.png differ
diff --git a/apps/web/public/onboarding/OnBoardMore.png b/apps/web/public/onboarding/OnBoardMore.png
new file mode 100644
index 00000000..37db9869
Binary files /dev/null and b/apps/web/public/onboarding/OnBoardMore.png differ
diff --git a/apps/web/public/onboarding/OnBoardUGs.png b/apps/web/public/onboarding/OnBoardUGs.png
new file mode 100644
index 00000000..a2895c07
Binary files /dev/null and b/apps/web/public/onboarding/OnBoardUGs.png differ
diff --git a/apps/web/public/onboarding/OnBoardWelcome.png b/apps/web/public/onboarding/OnBoardWelcome.png
new file mode 100644
index 00000000..efe46a0d
Binary files /dev/null and b/apps/web/public/onboarding/OnBoardWelcome.png differ