PROJECT REPORT
ON
STUDIPAL
Submitted to
In Partial Fulfilment of the requirements for the degree of
Desh Bhagat University
TH
(BCA 6 Semester)
Project Guide: Submitted By
Mr Rahul Thour Name:- Dilpreet Singh
Roll No.
ACKNOWLEDGEMENT
The successful completion of this work is a great joy. We would
like to express my gratitude to all those who have been of great
help during this effort. We are grateful Department of Computer
Science & Applications, Desh Bhagat University, Mandi
Gobindgarh. For providing the opportunity to carry out this work.
We are extremely grateful and remain indebted to our guide Mr.
TEACHER NAME for being a source of inspiration and for his
constant support in the Design, Implementation and Evaluation
of the project. We are thankful to them for their constant
constructive criticism and invaluable suggestions, which
benefited us a lot while developing the project on “PROJECT
NAME ”. They have been a constant source of inspiration and
motivation for hard work. They have been a constant source of
inspiration and motivation for hard work. They have been very
co-operative throughout this project work. Through this column,
it would be our utmost pleasure to express our warm thanks to
him for their encouragement, co-operation and consent without
which we mightn’t be able to accomplish this project.
We also express our gratitude to Mr. HOD NAME (HOD) for
providing us the infrastructure to carry out the project and to all
staff members who were directly and indirectly instrument in
enabling us to stay committed for the project.
DILPREET SINGH
DATE: ___________
DECLARATION FROM STUDENTS
We hereby declare that the work, which is presented in this
report for the partial fulfillment for the award of the BCA , is an
authentic record of work carried by us during Fourth Semester.
No part of this has been submitted to any other University or
Institution for the award of any other degree or diploma
PLACE: LUDHIANA PROJECT GUIDE
Date:
Table Of Content
S. TITLE Page No.
No.
1. Introduction to project
2. Objectives of project
3. Feasibility study
4. Database design
5. Tools Used
6. Hardware and software requirements
7. Coding
8. Output
9. Future Scope
10. Bibliography
Introduction To Project
STUDIPAL is an online study courses website designed to
provide students with easy access to educational resources,
online courses, study materials, and learning support services.
The platform helps students improve their knowledge and skills
through a simple and user-friendly interface.
The website offers different categories of courses such as
programming, web development, computer science, and other
academic subjects. Students can explore courses, view
learning materials, and access educational content anytime and
anywhere through the internet.
STUDIPAL is developed to make online learning more
convenient, interactive, and accessible for students. The project
also helps in understanding modern web technologies,
database management, and frontend-backend integration used
in educational platforms.
This project aims to support digital learning and provide a
better online education experience for students through an
organized and efficient study platform.
Objective of Project
The main objective of the STUDIPAL project is to provide
students with an easy and effective online learning platform
where they can access study courses, educational materials,
and learning resources from anywhere.
This project aims to make education more accessible,
organized, and user-friendly for students. The website is
designed to help learners improve their skills and knowledge
through online courses and digital learning support.
The major objectives of the project are:
To provide online study courses in different subjects and
technologies.
To create a simple and interactive platform for students.
To help students access learning materials anytime and
anywhere.
To improve the online learning experience through a user-
friendly interface.
To develop practical knowledge of web development and
database management.
To encourage digital learning and modern education
methods.
The project also focuses on making the learning process more
convenient, efficient, and beneficial for students.
Feasibility Study
What Is a Feasibility Study?
A feasibility study is an assessment of the practicality of a
proposed plan or project. It analyse the viability of a project to
determine whether the project or venture is likely to succeed.
The study is also designed to identify potential issues and
problems that could arise while pursuing the project.
Key Takeaways:
A feasibility study assesses the potential success of a
proposed project by defining its expected costs and
projected benefits in detail.
A feasibility study helps determine whether an online
learning platform like STUDIPAL can successfully meet
student needs and educational goals.
It is useful to have a backup plan in case the original
project faces technical or operational difficulties.
How to Conduct a Feasibility Study?
The exact format of a feasibility study depends on the type of
project or organization. However, the following important
factors are generally involved:
Conduct a preliminary analysis and collect feedback from
students, teachers, and users regarding the online study
platform.
Analyse the collected data carefully to ensure the
information is accurate and useful.
Conduct market research to identify the demand for online
study courses and digital learning platforms.
Prepare an operational and project plan, including the
technologies, tools, and resources required for
development.
Estimate development cost, maintenance cost, and future
improvements of the website.
Prepare a projected report including expected benefits,
usability, and performance of the project.
Identify possible technical issues, security concerns, and
user-related challenges, along with solutions for handling
them.
Make a final decision regarding the successful
implementation of the project.
Main Objective of a Feasibility Study
The main objective of the feasibility study is to determine
whether the STUDIPAL online study courses website can be
developed successfully and used effectively by students. It
helps identify technical, financial, and operational requirements
while ensuring that the project can fulfil educational needs
efficiently.
Steps in a Feasibility Study
A feasibility study begins with a preliminary analysis. Feedback
is collected from users, market research is conducted, and the
project plan is prepared. All collected information is analyzed to
make an initial decision about the project.
If the project is feasible, the next stage includes identifying
technological requirements, studying user needs, preparing the
development strategy, outlining human resource requirements,
scheduling project activities, and estimating financial
requirements.
Who Conducts a Feasibility Study?
A feasibility study may be conducted by project developers,
faculty members, project guides, or technical experts. If
required, organizations may also hire professional consultants
for detailed analysis and guidance.
Types of Feasibility
A feasibility study considers the feasibility of four important
aspects of a project:
Technical Feasibility: Identifies the hardware, software,
internet requirements, and technical skills needed to
develop and maintain the STUDIPAL website.
Financial Feasibility: Estimates the overall project cost,
maintenance expenses, and expected benefits of the
platform.
Market Feasibility: Analyse the growing demand for online
learning platforms, educational websites, student users,
and digital learning opportunities.
Organizational Feasibility: Describes the management
structure, educational content handling, and support
system required for successful project operation.
Tools Used
The following tools were used to complete the STUDIPAL
project:
Visual Studio Code
HTML
CSS
JavaScript
Notepad
Database Management System
Visual Studio Code
Visual Studio Code is a powerful and lightweight code editor
used for developing websites and software applications. It
provides an efficient environment for writing, editing, debugging,
and managing code.
With support for multiple programming languages and
extensions, Visual Studio Code helps developers create
responsive and user-friendly web applications such as
STUDIPAL.
Why Use Visual Studio Code?
Easy and user-friendly interface
Fast and efficient code editing
Support for HTML, CSS, JavaScript, and many other
languages
Built-in debugging features
Extension support for additional functionality
Integrated terminal and version control support
Develop Your Code
Visual Studio Code provides useful features such as syntax
highlighting, auto-completion, code suggestions, and project
organization tools that help developers write clean and efficient
code.
Debug Your Code
The debugging tools in Visual Studio Code help identify and fix
errors quickly. Developers can test functionality, monitor
variables, and improve website performance effectively.
Test Your Code
Testing tools help ensure that the STUDIPAL website functions
correctly and provides a smooth user experience for students
and users.
Deploy Your App
After development and testing, the STUDIPAL website can be
deployed on web servers or hosting platforms so students can
access online study courses and educational resources
through the internet.
HTML BASICS
HTML Introduction- HTML stands for Hypertext Markup
Language. It is the standard language used to create and
structure content on the web. HTML is a markup language, not
a programming language, meaning it annotates text to define
how it is structured and displayed by web browsers. It forms
the building blocks of all websites and is complemented by CSS
for style and JavaScript for interactivity.
Why Learn HTML?-
Here are 5 common reasons to learn HTML:
1. Build Websites: HTML is the basic building block for
creating any website. Learning HTML can help you pursue a
career in web development.
2. Customize Content: Allows you to edit or tweak web
pages, emails, or templates to fit your needs.
3. Understand how the web works: Helps you grasp how the
internet works and how web pages are structured.
4. Employment Opportunities: According to Bureau of Labor
Statistics projects that employment for web developers will
grow 16% between 2022-2032, which is much faster than
the average across all occupations.
5. Learn Easily: HTML is beginner-friendly, making it a great
first step into the world of coding and technology
Features of HTML-
• It is easy to learn and easy to use.
• It is platform-independent.
• Images, videos, and audio can be added to a web
page.
• Hypertext can be added to the text.
• It is a markup language.
HTML Element and HTML Tag- HTML Element and HTML Tags
are related but distinct. An HTML element is the complete
structure, including the opening tag, content (if any), and the
closing tag (if applicable). On the other hand, A tag is the actual
keyword or name enclosed in angle brackets (< >) that tells the
browser what kind of content to expect.
HTML Page Structure- The basic structure of an HTML page is
shown below. It contains the essential building-block elements
(i.e. doctype declaration, HTML, head, title, and body elements)
upon which all web pages are created.
• <!DOCTYPE html> – This is the document type
declaration, not a tag. It declares that the document is an
HTML5 document.
• <html> – This is called the HTML root element. All
other elements are contained within it.
• <head> – The head tag contains the “behind the
scenes” elements for a webpage. Elements within the
head aren’t visible on the front end of a webpage.
• <title> – The title is what is displayed on the top of
your browser when you visit a website and contains the
title of the webpage that you are viewing.
• <p>– The <p> tag represents a paragraph of text.
• <body> – The body tag is used to enclose all the
visible content of a webpage. In other words, the body
content is what the browser will show on the front end.
Open Notepad: Search for "notepad" in the Windows
search bar and open the application.
An HTML document can be created using an HTML text editor.
Save the text file using the “.html” or “.html” extension. Once
saved as an HTML document, the file can be opened as a
webpage in the browser.
NOTEPAD
Notepad is a simple, built-in text editor that allows you to open,
edit, and save text files, including HTML files. One can
absolutely use Notepad, the basic text editor in Windows, to
edit and create HTML files, though it's a simple tool without
advanced features like syntax highlighting or live previews.
How to use it for HTML:
• Open Notepad: Search for "notepad" in the Windows
search bar and open the application.
• Type your HTML code: Enter your HTML code into
the Notepad window, using tags and other HTML
elements.
• Save as an HTML file: Go to "File" > "Save As" and
choose a location to save the file. In the "Save as type"
dropdown, select "All Files" and then enter a filename with
the ".html" extension (e.g., "my_page.html"). • Open in
a browser: Double-click the saved HTML file to open it in
your default web browser.
MICROSOFT WORD
Microsoft Word (MS Word) is a widely used word processor
software developed by Microsoft, part of the Microsoft Office
suite, used for creating and editing documents like letters,
reports, and resumes, with files typically saved with a .docx
extension.
Functionality: MS Word allows users to create,
•
format, and edit documents, offering features like spell
check, grammar check, thesaurus, tracking changes, and
mail merge.
• Features: MS Word offers advanced features for
formatting and editing documents, including specialized
tools for designing, spelling and grammar checks, and an
integrated thesaurus..
• Uses: Creating Documents, Writing and formatting
letters, reports, resumes, essays, and other documents.
MS Word is a popular tool for students and researchers.
CASCADING STYLE SHEETS(CSS)
Cascading Style Sheets (CSS) is a style sheet language used
for specifying the presentation and styling of a document
written in a markup language such as HTML or XML. CSS is a
cornerstone technology of the World Wide Web, alongside
HTML and JavaScript.
CSS is designed to enable the separation of content and
presentation, including layout, colours, and fonts. This
separation can improve content accessibility, since the content
can be written without concern for its presentation; provide
more flexibility and control in the specification of presentation
characteristics; enable multiple web pages to share formatting
by specifying the relevant CSS in a separate .CSS file, which
reduces complexity and repetition in the structural content; and
enable the .CSS file to be cached to improve the page load
speed between the pages that share the file and its formatting.
HYPERTEXT PRE-PROCESSING(PHP)
PHP (originally standing for "Personal Home Page" but now a
recursive acronym for "PHP: Hypertext Preprocessor") is a
widely used, open-source, server-side scripting language
primarily used for web development, allowing developers to
create dynamic web pages and applications.
Here's a more detailed look at PHP:
Key Features:
• Server-Side Scripting: PHP code is executed on the server
before being sent to the client's browser, allowing for dynamic
content generation and database interaction.
• Dynamic Content: PHP enables the creation of web pages
that change based on user input, database data, or other
factors.
• Database Integration: PHP provides built-in functions for
interacting with various databases, including MySQL,
PostgreSQL, and others.
HTML Embedding: PHP code can be easily embedded
•
within HTML, simplifying the process of creating dynamic web
pages.
HARDWARE AND SOFTWARE REQUIREMENTS
Hardware requirements:
Hardware Requirements
Processor Premium 2.6GHz
Random Access 1GB RAM
Memory
Hard Disk 180 GB
Display Direct X9 capable video and running
1024x768 or higher resolution display
Mouse Mouse or compatible devices
Keyboard Keyboard or any compatible devices
Printer Laser jet
Software requirements:
SOFTWARE REQUIREMENTS
Operating system Microsoft windows versions
23H2/ Windows 11
Front End Visual Studio Code 2022
Back End Microsoft Edge
SOURCE CODE
[Link]
import { useState } from "react";
import { Link, useLocation } from "react-router-dom";
import { Menu, X, ChevronDown } from "lucide-react";
import { useUser } from "../context/UserContext";
export default function Navbar() {
const [isOpen, setIsOpen] = useState(false);
const [loginOpen, setLoginOpen] = useState(false);
const location = useLocation();
const { user } = useUser(); //logged-in user from context
const toggleMenu = () => setIsOpen(!isOpen);
const handleCloseMenu = () => {
setIsOpen(false);
setLoginOpen(false);
};
const navLinks = [
{ to: "/", label: "Dashboard" },
{ to: "/courses", label: "Courses" },
{ to: "/about", label: "About" },
{ to: "/contact", label: "Contact" },
];
const isActive = (path) => [Link] === path;
// Hide Navbar
if (
[Link]("/user/dashboard") ||
[Link]("/mentor/dashboard") ||
[Link]("/admin/dashboard") ||
[Link]("/course-lessons") ||
[Link]("/course/") && [Link]("/lessons")
){
return null;
return (
<nav className="bg-white shadow-md sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16 items-center">
{/* Logo */}
<div className="flex items-center">
<Link to="/" className="text-2xl font-bold text-indigo-600">
Studipal
</Link>
</div>
{/* Desktop Menu */}
<div className="hidden md:flex space-x-8 items-center">
{[Link]((link) => (
<Link
key={[Link]}
to={[Link]}
className={`${
isActive([Link])
? "text-indigo-600 font-semibold"
: "text-gray-700 hover:text-indigo-600"
}`}
>
{[Link]}
</Link>
))}
{/* Show Login dropdown*/}
{!user && (
<div className="relative">
<button
onClick={() => setLoginOpen(!loginOpen)}
className="flex items-center px-4 py-2 bg-white text-indigo-600 rounded-
lg font-medium hover:bg-gray-100"
>
Login <ChevronDown className="ml-1 w-4 h-4" />
</button>
{loginOpen && (
<div className="absolute right-0 mt-2 bg-white text-black rounded-lg
shadow-lg w-40">
<Link
to="/user/login"
onClick={handleCloseMenu}
className="block px-4 py-2 hover:bg-gray-100"
>
User
</Link>
<Link
to="/mentor/login"
onClick={handleCloseMenu}
className="block px-4 py-2 hover:bg-gray-100"
>
Mentor
</Link>
<Link
to="/admin/login"
onClick={handleCloseMenu}
className="block px-4 py-2 hover:bg-gray-100"
>
Admin
</Link>
</div>
)}
</div>
)}
</div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center">
<button
onClick={toggleMenu}
className="text-gray-700 focus:outline-none"
>
{isOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
</div>
</div>
{/* Mobile Menu */}
{isOpen && (
<div className="md:hidden bg-white shadow-lg">
<div className="px-4 py-4 space-y-2">
{[Link]((link) => (
<Link
key={[Link]}
to={[Link]}
onClick={handleCloseMenu}
className={`block ${
isActive([Link])
? "text-indigo-600 font-semibold"
: "text-gray-700 hover:text-indigo-600"
}`}
>
{[Link]}
</Link>
))}
{/*Show Login dropdown*/}
{!user && (
<div>
<button
onClick={() => setLoginOpen(!loginOpen)}
className="w-full flex justify-between items-center bg-indigo-600 text-
white py-2 px-4 rounded-lg hover:bg-indigo-700"
>
Login
<ChevronDown
className={`ml-2 w-4 h-4 transform transition-transform ${
loginOpen ? "rotate-180" : ""
}`}
/>
</button>
{loginOpen && (
<div className="mt-2 bg-white border rounded-lg shadow-lg">
<Link
to="/user/login"
onClick={handleCloseMenu}
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
>
User
</Link>
<Link
to="/mentor/login"
onClick={handleCloseMenu}
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
>
Mentor
</Link>
<Link
to="/admin/login"
onClick={handleCloseMenu}
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
>
Admin
</Link>
</div>
)}
</div>
)}
</div>
</div>
)}
</nav>
);
CONTEXT
[Link]
// src/context/[Link]
import { createContext, useContext, useState, useEffect } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
const AdminContext = createContext();
export const AdminProvider = ({ children }) => {
const [admin, setAdmin] = useState(null);
const [token, setToken] = useState([Link]("adminToken"));
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const login = async (email, password) => {
try {
setLoading(true);
const res = await [Link](
"[Link]
{ email, password },
{ withCredentials: true }
);
if ([Link]?.token) {
setToken([Link]);
setAdmin([Link]);
[Link]("adminToken", [Link]);
[Link]("role", "admin");
navigate("/admin/dashboard");
} catch (error) {
throw [Link]?.data?.message || "Login failed";
} finally {
setLoading(false);
};
const logout = () => {
setAdmin(null);
setToken(null);
[Link]("adminToken");
[Link]("role");
navigate("/", { replace: true });
};
useEffect(() => {
const fetchAdmin = async () => {
if (!token) return;
try {
const res = await [Link]("[Link] {
headers: { Authorization: `Bearer ${token}` },
withCredentials: true,
});
setAdmin([Link]);
} catch (error) {
[Link]("Admin fetch failed:", error);
logout();
};
fetchAdmin();
}, [token]);
return (
<[Link] value={{ admin, token, login, logout, loading }}>
{children}
</[Link]>
);
};
export const useAdmin = () => useContext(AdminContext);
[Link]
// src/context/[Link]
import { createContext, useContext, useState, useEffect } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
const MentorContext = createContext();
export const MentorProvider = ({ children }) => {
const [mentor, setMentor] = useState(null);
const [token, setToken] = useState([Link]("mentorToken"));
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
// Login mentor
const login = async (email, password) => {
try {
setLoading(true);
const res = await [Link](
"[Link]
{ email, password },
{ withCredentials: true }
);
if ([Link]?.token) {
// Save token in state + localStorage
setToken([Link]);
[Link]("mentorToken", [Link]);
[Link]("role", "mentor");
// Save mentor info
if ([Link]) {
setMentor([Link]);
navigate("/mentor/dashboard");
}
} catch (error) {
throw [Link]?.data?.message || "Login failed";
} finally {
setLoading(false);
};
const logout = () => {
setMentor(null);
setToken(null);
[Link]("mentorToken");
[Link]("role");
navigate("/", { replace: true });
};
//Auto fetch mentor
useEffect(() => {
const fetchMentor = async () => {
if (!token) return;
try {
const res = await [Link]("[Link] {
headers: { Authorization: `Bearer ${token}` },
withCredentials: true,
});
setMentor([Link] || [Link]);
} catch (error) {
[Link]("Mentor fetch failed:", error);
logout();
}
};
fetchMentor();
}, [token]);
return (
<[Link] value={{ mentor, token, login, logout, loading }}>
{children}
</[Link]>
);
};
export const useMentor = () => useContext(MentorContext);
[Link]
// src/context/[Link]
import { AdminProvider } from "./AdminContext";
import { MentorProvider } from "./MentorContext";
import { UserProvider } from "./UserContext";
export default function RoleProvider({ children }) {
return (
<AdminProvider>
<MentorProvider>
<UserProvider>
{children}
</UserProvider>
</MentorProvider>
</AdminProvider>
);
[Link]
// src/context/[Link]
import { createContext, useContext, useState, useEffect } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
const UserContext = createContext();
export const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [token, setToken] = useState([Link]("userToken"));
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
// Register user
const register = async (name, email, password) => {
try {
setLoading(true);
const res = await [Link](
"[Link]
{ name, email, password },
{ withCredentials: true }
);
if ([Link]?.token) {
// auto login after register
setToken([Link]);
[Link]("userToken", [Link]);
[Link]("role", "user");
if ([Link]) setUser([Link]);
navigate("/user/dashboard");
return [Link];
} catch (error) {
throw [Link]?.data?.message || "Registration failed";
} finally {
setLoading(false);
};
// Login user
const login = async (email, password) => {
try {
setLoading(true);
const res = await [Link](
"[Link]
{ email, password },
{ withCredentials: true }
);
if ([Link]?.token) {
setToken([Link]);
[Link]("userToken", [Link]);
[Link]("role", "user");
if ([Link]) setUser([Link]);
navigate("/user/dashboard");
return [Link];
} catch (error) {
throw [Link]?.data?.message || "Login failed";
} finally {
setLoading(false);
};
// Logout user
const logout = () => {
setUser(null);
setToken(null);
[Link]("userToken");
[Link]("role");
navigate("/", { replace: true });
};
//Auto fetch user
useEffect(() => {
const fetchUser = async () => {
if (!token) return;
try {
const res = await [Link]("[Link] {
headers: { Authorization: `Bearer ${token}` },
withCredentials: true,
});
setUser([Link]);
} catch (error) {
[Link]("User fetch failed:", error);
logout(); // clear invalid token
};
fetchUser();
}, [token]);
return (
<[Link]
value={{ user, token, register, login, logout, loading }}
>
{children}
</[Link]>
);
};
export const useUser = () => useContext(UserContext);
PAGES
[Link]
// src/pages/[Link]
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function About() {
const location = useLocation();
// Scroll to top
useEffect(() => {
[Link](0, 0);
}, [location]);
return (
<div className="max-w-6xl mx-auto px-6 py-12">
<h1 className="text-4xl font-bold text-indigo-600 mb-6 text-center">
About Studipal
</h1>
<p className="text-lg text-gray-700 text-center mb-12">
Studipal is your ultimate Learning Management System designed to help
learners, mentors, and administrators connect, grow, and achieve
success.
</p>
<div className="grid md:grid-cols-2 gap-10 mb-16">
<div className="bg-white p-8 rounded-xl shadow hover:shadow-lg transition">
<h2 className="text-2xl font-semibold text-indigo-700 mb-4">
🎯 Our Mission
</h2>
<p className="text-gray-700">
To empower learners worldwide by providing quality education,
mentorship, and tools that make learning engaging, accessible, and
effective.
</p>
</div>
<div className="bg-white p-8 rounded-xl shadow hover:shadow-lg transition">
<h2 className="text-2xl font-semibold text-indigo-700 mb-4">
🌍 Our Vision
</h2>
<p className="text-gray-700">
To build a global community where students learn, mentors guide, and
administrators ensure a smooth and secure education ecosystem.
</p>
</div>
</div>
<section className="mb-16">
<h2 className="text-3xl font-bold text-gray-800 text-center mb-10">
What Makes Studipal Unique?
</h2>
<div className="grid md:grid-cols-3 gap-8">
<div className="bg-indigo-50 p-6 rounded-xl shadow hover:shadow-lg
transition">
<h3 className="text-xl font-semibold mb-3">
📚 Interactive Learning
</h3>
<p className="text-gray-700">
Courses designed with practical projects and real-world use cases
so you can learn by doing.
</p>
</div>
<div className="bg-indigo-50 p-6 rounded-xl shadow hover:shadow-lg
transition">
<h3 className="text-xl font-semibold mb-3"> 👩 🏫 Expert Mentors</h3>
<p className="text-gray-700">
Get guidance from experienced mentors who provide personalized
support and feedback.
</p>
</div>
<div className="bg-indigo-50 p-6 rounded-xl shadow hover:shadow-lg
transition">
<h3 className="text-xl font-semibold mb-3"> 🛡 Secure Platform</h3>
<p className="text-gray-700">
Advanced admin tools and data protection ensure a safe and
reliable experience for all users.
</p>
</div>
</div>
</section>
<section className="text-center">
<h2 className="text-3xl font-bold text-gray-800 mb-6">Our Team</h2>
<p className="text-gray-700 max-w-3xl mx-auto mb-10">
Behind Studipal is a passionate team of developers, educators, and
innovators working together to transform education into a seamless and
enjoyable journey.
</p>
<div className="grid md:grid-cols-3 gap-8">
{[
{ name: "Satpal", role: "Founder & Developer" },
{ name: "Mentor Team", role: "Guides & Educators" },
{ name: "Admin Team", role: "Support & Management" },
].map((member, idx) => (
<div
key={idx}
className="bg-white p-6 rounded-xl shadow hover:shadow-lg transition"
>
<h3 className="text-xl font-semibold text-indigo-700">
{[Link]}
</h3>
<p className="text-gray-600">{[Link]}</p>
</div>
))}
</div>
</section>
</div>
);
[Link]
// SRC/PAGES/[Link]
IMPORT { USEEFFECT } FROM "REACT";
IMPORT { USELOCATION } FROM "REACT-ROUTER-DOM";
EXPORT DEFAULT FUNCTION ABOUT() {
CONST LOCATION = USELOCATION();
// SCROLL TO TOP
USEEFFECT(() => {
[Link](0, 0);
}, [LOCATION]);
RETURN (
<DIV CLASSNAME="MAX-W-6XL MX-AUTO PX-6 PY-12">
<H1 CLASSNAME="TEXT-4XL FONT-BOLD TEXT-INDIGO-600 MB-6 TEXT-
CENTER">
ABOUT STUDIPAL
</H1>
<P CLASSNAME="TEXT-LG TEXT-GRAY-700 TEXT-CENTER MB-12">
STUDIPAL IS YOUR ULTIMATE LEARNING MANAGEMENT SYSTEM DESIGNED
TO HELP
LEARNERS, MENTORS, AND ADMINISTRATORS CONNECT, GROW, AND
ACHIEVE
SUCCESS.
</P>
<DIV CLASSNAME="GRID MD:GRID-COLS-2 GAP-10 MB-16">
<DIV CLASSNAME="BG-WHITE P-8 ROUNDED-XL SHADOW HOVER:SHADOW-LG
TRANSITION">
<H2 CLASSNAME="TEXT-2XL FONT-SEMIBOLD TEXT-INDIGO-700 MB-4">
🎯 OUR MISSION
</H2>
<P CLASSNAME="TEXT-GRAY-700">
TO EMPOWER LEARNERS WORLDWIDE BY PROVIDING QUALITY EDUCATION,
MENTORSHIP, AND TOOLS THAT MAKE LEARNING ENGAGING, ACCESSIBLE,
AND
EFFECTIVE.
</P>
</DIV>
<DIV CLASSNAME="BG-WHITE P-8 ROUNDED-XL SHADOW HOVER:SHADOW-LG
TRANSITION">
<H2 CLASSNAME="TEXT-2XL FONT-SEMIBOLD TEXT-INDIGO-700 MB-4">
🌍 OUR VISION
</H2>
<P CLASSNAME="TEXT-GRAY-700">
TO BUILD A GLOBAL COMMUNITY WHERE STUDENTS LEARN, MENTORS
GUIDE, AND
ADMINISTRATORS ENSURE A SMOOTH AND SECURE EDUCATION
ECOSYSTEM.
</P>
</DIV>
</DIV>
<SECTION CLASSNAME="MB-16">
<H2 CLASSNAME="TEXT-3XL FONT-BOLD TEXT-GRAY-800 TEXT-CENTER MB-
10">
WHAT MAKES STUDIPAL UNIQUE?
</H2>
<DIV CLASSNAME="GRID MD:GRID-COLS-3 GAP-8">
<DIV CLASSNAME="BG-INDIGO-50 P-6 ROUNDED-XL SHADOW
HOVER:SHADOW-LG TRANSITION">
<H3 CLASSNAME="TEXT-XL FONT-SEMIBOLD MB-3">
📚 INTERACTIVE LEARNING
</H3>
<P CLASSNAME="TEXT-GRAY-700">
COURSES DESIGNED WITH PRACTICAL PROJECTS AND REAL-WORLD USE
CASES
SO YOU CAN LEARN BY DOING.
</P>
</DIV>
<DIV CLASSNAME="BG-INDIGO-50 P-6 ROUNDED-XL SHADOW
HOVER:SHADOW-LG TRANSITION">
<H3 CLASSNAME="TEXT-XL FONT-SEMIBOLD MB-3"> 👩 🏫 EXPERT
MENTORS</H3>
<P CLASSNAME="TEXT-GRAY-700">
GET GUIDANCE FROM EXPERIENCED MENTORS WHO PROVIDE
PERSONALIZED
SUPPORT AND FEEDBACK.
</P>
</DIV>
<DIV CLASSNAME="BG-INDIGO-50 P-6 ROUNDED-XL SHADOW
HOVER:SHADOW-LG TRANSITION">
<H3 CLASSNAME="TEXT-XL FONT-SEMIBOLD MB-3"> 🛡 SECURE
PLATFORM</H3>
<P CLASSNAME="TEXT-GRAY-700">
ADVANCED ADMIN TOOLS AND DATA PROTECTION ENSURE A SAFE AND
RELIABLE EXPERIENCE FOR ALL USERS.
</P>
</DIV>
</DIV>
</SECTION>
<SECTION CLASSNAME="TEXT-CENTER">
<H2 CLASSNAME="TEXT-3XL FONT-BOLD TEXT-GRAY-800 MB-6">OUR
TEAM</H2>
<P CLASSNAME="TEXT-GRAY-700 MAX-W-3XL MX-AUTO MB-10">
BEHIND STUDIPAL IS A PASSIONATE TEAM OF DEVELOPERS, EDUCATORS,
AND
INNOVATORS WORKING TOGETHER TO TRANSFORM EDUCATION INTO A
SEAMLESS AND
ENJOYABLE JOURNEY.
</P>
<DIV CLASSNAME="GRID MD:GRID-COLS-3 GAP-8">
{[
{ NAME: "SATPAL", ROLE: "FOUNDER & DEVELOPER" },
{ NAME: "MENTOR TEAM", ROLE: "GUIDES & EDUCATORS" },
{ NAME: "ADMIN TEAM", ROLE: "SUPPORT & MANAGEMENT" },
].MAP((MEMBER, IDX) => (
<DIV
KEY={IDX}
CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-LG
TRANSITION"
>
<H3 CLASSNAME="TEXT-XL FONT-SEMIBOLD TEXT-INDIGO-700">
{[Link]}
</H3>
<P CLASSNAME="TEXT-GRAY-600">{[Link]}</P>
</DIV>
))}
</DIV>
</SECTION>
</DIV>
);
[Link]
IMPORT { USESTATE } FROM "REACT";
IMPORT { USEADMIN } FROM "../CONTEXT/ADMINCONTEXT";
EXPORT DEFAULT FUNCTION ADMINLOGIN() {
CONST [EMAIL, SETEMAIL] = USESTATE("");
CONST [PASSWORD, SETPASSWORD] = USESTATE("");
CONST [ERROR, SETERROR] = USESTATE("");
CONST { LOGIN, LOADING } = USEADMIN();
CONST HANDLESUBMIT = ASYNC (E) => {
[Link]();
SETERROR("");
TRY {
AWAIT LOGIN(EMAIL, PASSWORD);
} CATCH (ERR) {
SETERROR(ERR || "LOGIN FAILED. TRY AGAIN.");
};
RETURN (
<DIV CLASSNAME="MIN-H-SCREEN FLEX ITEMS-CENTER JUSTIFY-CENTER BG-
GRAY-100 PX-4">
<DIV CLASSNAME="MAX-W-MD W-FULL BG-WHITE P-8 ROUNDED-2XL SHADOW-
LG">
<H2 CLASSNAME="TEXT-2XL FONT-BOLD TEXT-CENTER TEXT-GRAY-800 MB-
6">
ADMIN LOGIN
</H2>
{ERROR && (
<DIV CLASSNAME="MB-4 P-2 TEXT-SM TEXT-RED-700 BG-RED-100
ROUNDED">
{ERROR}
</DIV>
)}
<FORM ONSUBMIT={HANDLESUBMIT} CLASSNAME="SPACE-Y-4">
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-1">
EMAIL
</LABEL>
<INPUT
TYPE="EMAIL"
CLASSNAME="W-FULL PX-3 PY-2 BORDER ROUNDED-LG FOCUS:RING
FOCUS:RING-BLUE-300 OUTLINE-NONE"
VALUE={EMAIL}
ONCHANGE={(E) => SETEMAIL([Link])}
REQUIRED
/>
</DIV>
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-1">
PASSWORD
</LABEL>
<INPUT
TYPE="PASSWORD"
CLASSNAME="W-FULL PX-3 PY-2 BORDER ROUNDED-LG FOCUS:RING
FOCUS:RING-BLUE-300 OUTLINE-NONE"
VALUE={PASSWORD}
ONCHANGE={(E) => SETPASSWORD([Link])}
REQUIRED
/>
</DIV>
<BUTTON
TYPE="SUBMIT"
DISABLED={LOADING}
CLASSNAME="W-FULL BG-BLUE-600 TEXT-WHITE PY-2 ROUNDED-LG
HOVER:BG-BLUE-700 TRANSITION DISABLED:OPACITY-50"
>
{LOADING ? "LOGGING IN..." : "LOGIN"}
</BUTTON>
</FORM>
</DIV>
</DIV>
);
[Link]
IMPORT { USELOCATION } FROM "REACT-ROUTER-DOM";
IMPORT { USEEFFECT, USESTATE } FROM "REACT";
IMPORT AXIOS FROM "AXIOS";
IMPORT { TOAST } FROM "REACT-HOT-TOAST";
EXPORT DEFAULT FUNCTION CONTACT() {
CONST LOCATION = USELOCATION();
// SCROLL TO TOP
USEEFFECT(() => {
[Link](0, 0);
}, [LOCATION]);
CONST [FORMDATA, SETFORMDATA] = USESTATE({
NAME: "",
EMAIL: "",
MESSAGE: "",
});
CONST [LOADING, SETLOADING] = USESTATE(FALSE);
CONST HANDLECHANGE = (E) => {
SETFORMDATA({ ...FORMDATA, [[Link]]: [Link] });
};
CONST HANDLESUBMIT = ASYNC (E) => {
[Link]();
SETLOADING(TRUE);
TRY {
CONST RES = AWAIT [Link](
`${[Link].VITE_API_URL}/CONTACT`,
FORMDATA
);
[Link]([Link] || "MESSAGE SENT SUCCESSFULLY!");
SETFORMDATA({ NAME: "", EMAIL: "", MESSAGE: "" });
} CATCH (ERROR) {
[Link]([Link]?.DATA?.MESSAGE || "FAILED TO SEND
MESSAGE");
} FINALLY {
SETLOADING(FALSE);
};
RETURN (
<DIV CLASSNAME="MAX-W-5XL MX-AUTO PX-6 PY-12">
<H1 CLASSNAME="TEXT-4XL FONT-BOLD TEXT-INDIGO-600 MB-6 TEXT-
CENTER">
CONTACT US
</H1>
<P CLASSNAME="TEXT-GRAY-700 TEXT-CENTER MB-10">
HAVE QUESTIONS ABOUT <STRONG>STUDIPAL</STRONG>? WE’D LOVE TO
HEAR FROM
YOU. REACH US ANYTIME USING THE DETAILS BELOW.
</P>
{/* CONTACT INFORMATION */}
<DIV CLASSNAME="GRID MD:GRID-COLS-2 GAP-8 MB-16">
<DIV CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-LG
TRANSITION">
<H2 CLASSNAME="TEXT-2XL FONT-SEMIBOLD TEXT-INDIGO-700 MB-4">
📧 EMAIL
</H2>
<P CLASSNAME="TEXT-GRAY-700">DILPREETLOTEY25@[Link]</P>
</DIV>
<DIV CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-LG
TRANSITION">
<H2 CLASSNAME="TEXT-2XL FONT-SEMIBOLD TEXT-INDIGO-700 MB-4">
📞 PHONE
</H2>
<P CLASSNAME="TEXT-GRAY-700">+91 9988799198</P>
</DIV>
<DIV CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-LG
TRANSITION">
<H2 CLASSNAME="TEXT-2XL FONT-SEMIBOLD TEXT-INDIGO-700 MB-4">
🏢 ADDRESS
</H2>
<P CLASSNAME="TEXT-GRAY-700">
STUDIPAL HQ, 155 VPO GILL, LUDHIANA PUNJAB, INDIA
</P>
</DIV>
<DIV CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-LG
TRANSITION">
<H2 CLASSNAME="TEXT-2XL FONT-SEMIBOLD TEXT-INDIGO-700 MB-4">
🌐 WEBSITE
</H2>
<P CLASSNAME="TEXT-GRAY-700">[Link]</P>
</DIV>
</DIV>
{/* CONTACT FORM */}
<DIV CLASSNAME="BG-GRAY-50 P-8 ROUNDED-XL SHADOW-MD">
<H2 CLASSNAME="TEXT-2XL FONT-SEMIBOLD TEXT-INDIGO-700 MB-6 TEXT-
CENTER">
SEND US A MESSAGE
</H2>
<FORM ONSUBMIT={HANDLESUBMIT} CLASSNAME="SPACE-Y-6 MAX-W-2XL
MX-AUTO">
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-2">
YOUR NAME
</LABEL>
<INPUT
TYPE="TEXT"
NAME="NAME"
VALUE={[Link]}
ONCHANGE={HANDLECHANGE}
PLACEHOLDER="ENTER YOUR NAME"
CLASSNAME="W-FULL PX-4 PY-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-INDIGO-500 OUTLINE-NONE"
REQUIRED
/>
</DIV>
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-2">
EMAIL
</LABEL>
<INPUT
TYPE="EMAIL"
NAME="EMAIL"
VALUE={[Link]}
ONCHANGE={HANDLECHANGE}
PLACEHOLDER="ENTER YOUR EMAIL"
CLASSNAME="W-FULL PX-4 PY-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-INDIGO-500 OUTLINE-NONE"
REQUIRED
/>
</DIV>
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-2">
MESSAGE
</LABEL>
<TEXTAREA
ROWS="5"
NAME="MESSAGE"
VALUE={[Link]}
ONCHANGE={HANDLECHANGE}
PLACEHOLDER="WRITE YOUR MESSAGE..."
CLASSNAME="W-FULL PX-4 PY-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-INDIGO-500 OUTLINE-NONE"
REQUIRED
></TEXTAREA>
</DIV>
<BUTTON
TYPE="SUBMIT"
DISABLED={LOADING}
CLASSNAME="W-FULL BG-INDIGO-600 TEXT-WHITE FONT-MEDIUM PY-3
ROUNDED-LG HOVER:BG-INDIGO-700 TRANSITION DISABLED:OPACITY-60"
>
{LOADING ? "SENDING..." : "SEND MESSAGE"}
</BUTTON>
</FORM>
</DIV>
</DIV>
);
}
[Link]
// SRC/PAGES/[Link]
IMPORT { USEEFFECT, USESTATE } FROM "REACT";
IMPORT { USEPARAMS, USENAVIGATE } FROM "REACT-ROUTER-DOM";
IMPORT AXIOS FROM "AXIOS";
IMPORT { USEMENTOR } FROM "../CONTEXT/MENTORCONTEXT";
IMPORT { PLUSCIRCLE, X } FROM "LUCIDE-REACT";
EXPORT DEFAULT FUNCTION COURSELESSONS() {
CONST { COURSEID } = USEPARAMS();
CONST NAVIGATE = USENAVIGATE();
CONST { MENTOR, TOKEN } = USEMENTOR();
CONST [COURSE, SETCOURSE] = USESTATE(NULL);
CONST [LESSONS, SETLESSONS] = USESTATE([]);
CONST [CURRENTVIDEO, SETCURRENTVIDEO] = USESTATE(NULL);
CONST [LOADING, SETLOADING] = USESTATE(TRUE);
CONST [ERROR, SETERROR] = USESTATE("");
CONST [SHOWADDLESSON, SETSHOWADDLESSON] = USESTATE(FALSE);
CONST [LESSONFORM, SETLESSONFORM] = USESTATE({
TITLE: "",
CONTENT: "",
VIDEO: NULL,
THUMBNAIL: NULL,
});
// FETCH COURSE AND LESSONS
USEEFFECT(() => {
CONST FETCHDATA = ASYNC () => {
IF (!COURSEID) RETURN;
SETLOADING(TRUE);
SETERROR("");
TRY {
CONST HEADERS = MENTOR ? { AUTHORIZATION: `BEARER ${TOKEN}` } : {};
CONST [COURSERES, LESSONSRES] = AWAIT [Link]([
[Link](
`HTTP://LOCALHOST:3000/API/V1/COURSE/GET-ONE-
COURSE/${COURSEID}`,
{ HEADERS }
),
[Link](
`HTTP://LOCALHOST:3000/API/V1/LESSON/${
MENTOR ? "MENTOR/GET-ALL-LESSONS" : "GET-ALL-LESSONS"
}/${COURSEID}`,
{ HEADERS }
),
]);
SETCOURSE([Link] || NULL);
CONST SORTEDLESSONS = ([Link] || []).SORT(
(A, B) => [Link] - [Link]
);
SETLESSONS(SORTEDLESSONS);
IF ([Link] > 0) SETCURRENTVIDEO(SORTEDLESSONS[0]);
} CATCH (ERR) {
[Link]("ERROR FETCHING COURSE OR LESSONS:", ERR);
SETERROR("FAILED TO LOAD COURSE OR LESSONS.");
} FINALLY {
SETLOADING(FALSE);
};
FETCHDATA();
}, [COURSEID, MENTOR, TOKEN]);
CONST HANDLEADDLESSON = ASYNC (E) => {
[Link]();
IF (![Link]) RETURN ALERT("PLEASE SELECT A VIDEO FILE.");
TRY {
CONST FORMDATA = NEW FORMDATA();
[Link]("TITLE", [Link]);
[Link]("CONTENT", [Link]);
IF ([Link]) [Link]("VIDEOURL",
[Link]);
IF ([Link])
[Link]("THUMBNAIL", [Link]);
AWAIT [Link](
`HTTP://LOCALHOST:3000/API/V1/LESSON/ADD-LESSON/${COURSEID}`,
FORMDATA,
HEADERS: {
AUTHORIZATION: `BEARER ${TOKEN}`,
"CONTENT-TYPE": "MULTIPART/FORM-DATA",
},
);
CONST LESSONSRES = AWAIT [Link](
`HTTP://LOCALHOST:3000/API/V1/LESSON/MENTOR/GET-ALL-
LESSONS/${COURSEID}`,
{ HEADERS: { AUTHORIZATION: `BEARER ${TOKEN}` } }
);
CONST SORTEDLESSONS = ([Link] || []).SORT(
(A, B) => [Link] - [Link]
);
SETLESSONS(SORTEDLESSONS);
SETLESSONFORM({ TITLE: "", CONTENT: "", VIDEO: NULL, THUMBNAIL: NULL });
SETSHOWADDLESSON(FALSE);
IF (!CURRENTVIDEO && [Link] > 0)
SETCURRENTVIDEO(SORTEDLESSONS[0]);
} CATCH (ERR) {
[Link]("FAILED TO SAVE LESSON:", ERR);
ALERT([Link]?.DATA?.MESSAGE || "ERROR SAVING LESSON");
}
};
IF (LOADING) RETURN <P CLASSNAME="TEXT-CENTER MT-10">LOADING...</P>;
IF (ERROR) RETURN <P CLASSNAME="TEXT-CENTER MT-10 TEXT-RED-
600">{ERROR}</P>;
RETURN (
<DIV CLASSNAME="MIN-H-SCREEN BG-GRAY-100 P-6">
<BUTTON
ONCLICK={() => NAVIGATE(-1)}
CLASSNAME="MB-4 TEXT-BLUE-600 HOVER:UNDERLINE"
>
&LARR; BACK
</BUTTON>
{/* COURSE BANNER */}
{COURSE && (
<DIV CLASSNAME="BG-GRAY-800 TEXT-WHITE ROUNDED-XL OVERFLOW-
HIDDEN MB-6 RELATIVE">
<IMG
SRC={[Link] || "/[Link]"}
ALT={[Link]}
CLASSNAME="W-FULL H-60 OBJECT-COVER OPACITY-80"
/>
<DIV CLASSNAME="ABSOLUTE BOTTOM-4 LEFT-6">
<H1 CLASSNAME="TEXT-3XL FONT-BOLD">{[Link]}</H1>
<P CLASSNAME="TEXT-GRAY-200 MT-1">{[Link]}</P>
</DIV>
</DIV>
)}
<DIV CLASSNAME="FLEX FLEX-COL LG:FLEX-ROW GAP-6">
{/* LESSONS LIST */}
<DIV CLASSNAME="LG:W-1/3 SPACE-Y-4 OVERFLOW-Y-AUTO MAX-H-[70VH]">
{[Link]((LESSON, INDEX) => (
<DIV
KEY={LESSON._ID}
CLASSNAME={`BG-WHITE SHADOW ROUNDED-XL CURSOR-POINTER
OVERFLOW-HIDDEN ${
CURRENTVIDEO?._ID === LESSON._ID
? "BORDER-2 BORDER-BLUE-600"
: ""
}`}
ONCLICK={() => SETCURRENTVIDEO(LESSON)}
>
<IMG
SRC={[Link] || "/[Link]"}
ALT={[Link]}
CLASSNAME="W-FULL H-28 OBJECT-COVER"
/>
<DIV CLASSNAME="P-3">
<H3 CLASSNAME="FONT-BOLD TEXT-LG">
{INDEX + 1}. {[Link]}
</H3>
<P CLASSNAME="TEXT-GRAY-500 TEXT-SM LINE-CLAMP-2">
{[Link]}
</P>
</DIV>
</DIV>
))}
</DIV>
{/* VIDEO PLAYER */}
<DIV CLASSNAME="LG:W-2/3 BG-BLACK ROUNDED-XL SHADOW OVERFLOW-
HIDDEN">
{CURRENTVIDEO ? (
<VIDEO
KEY={CURRENTVIDEO._ID}
SRC={[Link]}
CONTROLS
AUTOPLAY
CLASSNAME="W-FULL H-80 LG:H-[500PX] OBJECT-COVER"
/>
):(
<P CLASSNAME="TEXT-WHITE P-6">SELECT A LESSON TO PLAY</P>
)}
</DIV>
</DIV>
{/* ADD LESSON MODAL */}
{MENTOR && SHOWADDLESSON && (
<DIV CLASSNAME="FIXED INSET-0 BG-BLACK BG-OPACITY-50 FLEX ITEMS-
CENTER JUSTIFY-CENTER Z-50">
<DIV CLASSNAME="BG-WHITE ROUNDED-2XL SHADOW-LG W-FULL MAX-W-
LG P-6 RELATIVE">
<BUTTON
ONCLICK={() => SETSHOWADDLESSON(FALSE)}
CLASSNAME="ABSOLUTE TOP-4 RIGHT-4 TEXT-GRAY-500 HOVER:TEXT-
GRAY-700"
>
<X CLASSNAME="W-6 H-6" />
</BUTTON>
<H2 CLASSNAME="TEXT-2XL FONT-BOLD MB-6 TEXT-CENTER TEXT-GREEN-
600">
ADD NEW LESSON
</H2>
<FORM ONSUBMIT={HANDLEADDLESSON} CLASSNAME="SPACE-Y-4">
<INPUT
TYPE="TEXT"
PLACEHOLDER="LESSON TITLE"
VALUE={[Link]}
ONCHANGE={(E) =>
SETLESSONFORM({ ...LESSONFORM, TITLE: [Link] })
CLASSNAME="W-FULL P-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-GREEN-500"
REQUIRED
/>
<TEXTAREA
PLACEHOLDER="CONTENT / DESCRIPTION"
VALUE={[Link]}
ONCHANGE={(E) =>
SETLESSONFORM({ ...LESSONFORM, CONTENT: [Link] })
CLASSNAME="W-FULL P-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-GREEN-500"
REQUIRED
/>
<INPUT
TYPE="FILE"
ACCEPT="VIDEO/*"
ONCHANGE={(E) =>
SETLESSONFORM({ ...LESSONFORM, VIDEO: [Link][0] })
CLASSNAME="W-FULL P-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-GREEN-500"
REQUIRED
/>
<INPUT
TYPE="FILE"
ACCEPT="IMAGE/*"
ONCHANGE={(E) =>
SETLESSONFORM({ ...LESSONFORM, THUMBNAIL: [Link][0] })
CLASSNAME="W-FULL P-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-GREEN-500"
/>
<DIV CLASSNAME="FLEX GAP-3 MT-4">
<BUTTON
TYPE="SUBMIT"
CLASSNAME="FLEX-1 BG-GREEN-600 TEXT-WHITE PY-2 ROUNDED-LG
HOVER:BG-GREEN-700"
>
SAVE
</BUTTON>
<BUTTON
TYPE="BUTTON"
ONCLICK={() => SETSHOWADDLESSON(FALSE)}
CLASSNAME="FLEX-1 BG-GRAY-300 TEXT-GRAY-800 PY-2 ROUNDED-LG
HOVER:BG-GRAY-400"
>
CANCEL
</BUTTON>
</DIV>
</FORM>
</DIV>
</DIV>
)}
</DIV>
);
[Link]
// SRC/PAGES/[Link]
IMPORT { USEEFFECT, USESTATE } FROM "REACT";
IMPORT { LINK, USENAVIGATE } FROM "REACT-ROUTER-DOM";
IMPORT AXIOS FROM "AXIOS";
IMPORT { USEUSER } FROM "../CONTEXT/USERCONTEXT";
EXPORT DEFAULT FUNCTION COURSES() {
CONST [COURSES, SETCOURSES] = USESTATE([]);
CONST [LOADING, SETLOADING] = USESTATE(TRUE);
CONST { USER, TOKEN } = USEUSER();
CONST NAVIGATE = USENAVIGATE();
// FETCH ALL APPROVED COURSES
USEEFFECT(() => {
CONST FETCHCOURSES = ASYNC () => {
TRY {
CONST RES = AWAIT [Link](
"HTTP://LOCALHOST:3000/API/V1/COURSE/GET-ALL-COURSES"
);
SETCOURSES([Link] || []);
} CATCH (ERR) {
[Link]("ERROR FETCHING COURSES:", ERR);
} FINALLY {
SETLOADING(FALSE);
};
FETCHCOURSES();
}, []);
CONST HANDLEENROLL = ASYNC (COURSEID) => {
IF (!USER) {
NAVIGATE("/USER/LOGIN");
RETURN;
TRY {
AWAIT [Link](
`HTTP://LOCALHOST:3000/API/V1/COURSES/ENROLL/${COURSEID}`,
{},
HEADERS: { AUTHORIZATION: `BEARER ${TOKEN}` },
);
ALERT(" SUCCESSFULLY ENROLLED!");
} CATCH (ERR) {
[Link]("ENROLL FAILED:", ERR);
ALERT(" ENROLLMENT FAILED. PLEASE TRY AGAIN.");
};
IF (LOADING) {
RETURN (
<DIV CLASSNAME="TEXT-CENTER PY-20 TEXT-GRAY-600 TEXT-LG">
LOADING COURSES...
</DIV>
);
RETURN (
<DIV CLASSNAME="MAX-W-7XL MX-AUTO PX-4 PY-12">
<H1 CLASSNAME="TEXT-4XL FONT-BOLD TEXT-INDIGO-600 MB-6 TEXT-
CENTER">
EXPLORE OUR COURSES
</H1>
<P CLASSNAME="TEXT-LG TEXT-GRAY-700 TEXT-CENTER MB-12">
UPSKILL YOURSELF WITH OUR PROFESSIONALLY CURATED COURSES.
</P>
{[Link] === 0 ? (
<P CLASSNAME="TEXT-CENTER TEXT-GRAY-500">
NO COURSES AVAILABLE RIGHT NOW.
</P>
):(
<DIV CLASSNAME="GRID SM:GRID-COLS-2 MD:GRID-COLS-3 LG:GRID-COLS-4
GAP-8">
{[Link]((COURSE) => (
<DIV
KEY={COURSE._ID}
CLASSNAME="BG-WHITE ROUNDED-XL SHADOW-MD HOVER:SHADOW-2XL
HOVER:SCALE-105 TRANSITION-TRANSFORM DURATION-300 OVERFLOW-HIDDEN
GROUP"
>
{/* THUMBNAIL WITH GRADIENT OVERLAY */}
<DIV CLASSNAME="RELATIVE">
<IMG
SRC={[Link]}
ALT={[Link]}
CLASSNAME="W-FULL H-48 OBJECT-COVER"
/>
<DIV CLASSNAME="ABSOLUTE INSET-0 BG-GRADIENT-TO-T FROM-
BLACK/50 TO-TRANSPARENT OPACITY-0 GROUP-HOVER:OPACITY-100
TRANSITION-OPACITY"></DIV>
<SPAN CLASSNAME="ABSOLUTE TOP-2 RIGHT-2 BG-INDIGO-600 TEXT-
WHITE TEXT-XS PX-3 PY-1 ROUNDED-FULL FONT-MEDIUM">
{[Link]}
</SPAN>
<H2 CLASSNAME="ABSOLUTE BOTTOM-2 LEFT-2 TEXT-WHITE FONT-
SEMIBOLD TEXT-SM SM:TEXT-BASE LINE-CLAMP-2">
{[Link]}
</H2>
</DIV>
{/* COURSE INFO */}
<DIV CLASSNAME="P-4">
<P CLASSNAME="TEXT-GRAY-600 TEXT-SM MB-4 LINE-CLAMP-3">
{[Link]}
</P>
{/* ENROLL/LOGIN BUTTON */}
<DIV CLASSNAME="FLEX JUSTIFY-CENTER">
{USER ? (
<BUTTON
ONCLICK={() => HANDLEENROLL(COURSE._ID)}
CLASSNAME="W-FULL BG-INDIGO-600 HOVER:BG-INDIGO-700 TEXT-
WHITE FONT-MEDIUM PY-2 ROUNDED-LG TRANSITION-ALL DURATION-300
SHADOW HOVER:SHADOW-LG"
>
ENROLL NOW
</BUTTON>
):(
<LINK
TO="/USER/LOGIN"
CLASSNAME="W-FULL TEXT-CENTER BG-INDIGO-600 HOVER:BG-
INDIGO-700 TEXT-WHITE FONT-MEDIUM PY-2 ROUNDED-LG TRANSITION-ALL
DURATION-300 SHADOW HOVER:SHADOW-LG"
>
LOGIN TO ENROLL
</LINK>
)}
</DIV>
</DIV>
</DIV>
))}
</DIV>
)}
</DIV>
);
}
[Link]
// SRC/PAGES/[Link]
IMPORT { LINK } FROM "REACT-ROUTER-DOM";
EXPORT DEFAULT FUNCTION LANDINGPAGE() {
RETURN (
<DIV CLASSNAME="BG-GRAY-50">
{/* HERO SECTION */}
<SECTION CLASSNAME="BG-GRADIENT-TO-R FROM-INDIGO-600 TO-BLUE-600
TEXT-WHITE PY-20">
<DIV CLASSNAME="MAX-W-6XL MX-AUTO PX-6 TEXT-CENTER">
<H1 CLASSNAME="TEXT-4XL MD:TEXT-6XL FONT-BOLD MB-4">
WELCOME TO STUDIPAL 🎓
</H1>
<P CLASSNAME="TEXT-LG MD:TEXT-XL MB-6">
LEARN, MENTOR, AND GROW WITH THE ULTIMATE LEARNING
MANAGEMENT
SYSTEM.
</P>
<DIV CLASSNAME="FLEX JUSTIFY-CENTER GAP-4">
<LINK
TO="/COURSES"
CLASSNAME="PX-6 PY-3 BG-WHITE TEXT-INDIGO-600 FONT-SEMIBOLD
ROUNDED-LG SHADOW HOVER:BG-GRAY-100 TRANSITION"
>
EXPLORE COURSES
</LINK>
<LINK
TO="/USER/REGISTER"
CLASSNAME="PX-6 PY-3 BG-YELLOW-400 TEXT-GRAY-900 FONT-SEMIBOLD
ROUNDED-LG SHADOW HOVER:BG-YELLOW-500 TRANSITION"
>
GET STARTED
</LINK>
</DIV>
</DIV>
</SECTION>
<SECTION CLASSNAME="PY-16 PX-6 MAX-W-6XL MX-AUTO TEXT-CENTER">
<H2 CLASSNAME="TEXT-3XL FONT-BOLD TEXT-GRAY-800 MB-10">WHY
CHOOSE STUDIPAL?</H2>
<DIV CLASSNAME="GRID MD:GRID-COLS-3 GAP-8">
<DIV CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-
LG TRANSITION">
<H3 CLASSNAME="TEXT-XL FONT-SEMIBOLD MB-2"> 📚 LEARN
ANYTIME</H3>
<P CLASSNAME="TEXT-GRAY-600">
ACCESS A VARIETY OF COURSES ACROSS TECHNOLOGY, DESIGN, AND
MORE —
AT YOUR OWN PACE.
</P>
</DIV>
<DIV CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-
LG TRANSITION">
<H3 CLASSNAME="TEXT-XL FONT-SEMIBOLD MB-2"> 👩 🏫 MENTOR
SUPPORT</H3>
<P CLASSNAME="TEXT-GRAY-600">
GET GUIDANCE FROM EXPERIENCED MENTORS TO STAY ON THE RIGHT
TRACK.
</P>
</DIV>
<DIV CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-
LG TRANSITION">
<H3 CLASSNAME="TEXT-XL FONT-SEMIBOLD MB-2"> 🛡 ADMIN
CONTROL</H3>
<P CLASSNAME="TEXT-GRAY-600">
A SECURE PLATFORM WITH POWERFUL ADMIN TOOLS TO MANAGE
LEARNERS AND
MENTORS.
</P>
</DIV>
</DIV>
</SECTION>
<SECTION CLASSNAME="PY-16 PX-6 BG-GRAY-100">
<DIV CLASSNAME="MAX-W-6XL MX-AUTO TEXT-CENTER">
<H2 CLASSNAME="TEXT-3XL FONT-BOLD TEXT-GRAY-800 MB-10">
POPULAR COURSES
</H2>
<DIV CLASSNAME="GRID MD:GRID-COLS-3 GAP-8">
{[
TITLE: "FULL STACK WEB DEVELOPMENT",
DESC: "MASTER THE MERN STACK WITH HANDS-ON PROJECTS.",
},
TITLE: "DATA SCIENCE & ML",
DESC: "LEARN PYTHON, PANDAS, AND MACHINE LEARNING.",
},
TITLE: "UI/UX DESIGN",
DESC: "DESIGN STUNNING USER EXPERIENCES WITH FIGMA.",
},
].MAP((COURSE, IDX) => (
<DIV
KEY={IDX}
CLASSNAME="BG-WHITE P-6 ROUNDED-XL SHADOW HOVER:SHADOW-LG
TRANSITION"
>
<H3 CLASSNAME="TEXT-XL FONT-SEMIBOLD MB-
2">{[Link]}</H3>
<P CLASSNAME="TEXT-GRAY-600 MB-4">{[Link]}</P>
<LINK
TO="/COURSES"
CLASSNAME="TEXT-INDIGO-600 FONT-MEDIUM HOVER:UNDERLINE"
>
LEARN MORE →
</LINK>
</DIV>
))}
</DIV>
</DIV>
</SECTION>
<SECTION CLASSNAME="PY-16 PX-6 BG-INDIGO-600 TEXT-WHITE TEXT-
CENTER">
<H2 CLASSNAME="TEXT-3XL FONT-BOLD MB-4">
READY TO START YOUR LEARNING JOURNEY?
</H2>
<P CLASSNAME="MB-6">
JOIN THOUSANDS OF LEARNERS ALREADY BUILDING THEIR FUTURE WITH
STUDIPAL.
</P>
<LINK
TO="/USER/REGISTER"
CLASSNAME="PX-6 PY-3 BG-YELLOW-400 TEXT-GRAY-900 FONT-SEMIBOLD
ROUNDED-LG SHADOW HOVER:BG-YELLOW-500 TRANSITION"
>
SIGN UP NOW
</LINK>
</SECTION>
<FOOTER CLASSNAME="BG-GRAY-900 TEXT-GRAY-400 PY-6 TEXT-CENTER">
<P>© {NEW DATE().GETFULLYEAR()} STUDIPAL. ALL RIGHTS RESERVED.</P>
<DIV CLASSNAME="FLEX JUSTIFY-CENTER GAP-4 MT-2">
<LINK TO="/ABOUT" CLASSNAME="HOVER:TEXT-WHITE">
ABOUT
</LINK>
<LINK TO="/CONTACT" CLASSNAME="HOVER:TEXT-WHITE">
CONTACT
</LINK>
</DIV>
</FOOTER>
</DIV>
);
}
[Link]
// SRC/PAGES/[Link]
IMPORT { USESTATE, USEEFFECT } FROM "REACT";
IMPORT { LINK, USENAVIGATE } FROM "REACT-ROUTER-DOM";
IMPORT { USEUSER } FROM "../CONTEXT/USERCONTEXT";
EXPORT DEFAULT FUNCTION USERLOGIN() {
CONST [FORMDATA, SETFORMDATA] = USESTATE({ EMAIL: "", PASSWORD: "" });
CONST [ERROR, SETERROR] = USESTATE("");
CONST NAVIGATE = USENAVIGATE();
CONST { LOGIN, LOADING, TOKEN } = USEUSER();
//REDIRECT IF ALREADY LOGGED IN
USEEFFECT(() => {
IF (TOKEN) {
NAVIGATE("/USER/DASHBOARD", { REPLACE: TRUE });
}, [TOKEN, NAVIGATE]);
CONST HANDLECHANGE = (E) => {
SETFORMDATA({
...FORMDATA,
[[Link]]: [Link],
});
};
CONST HANDLESUBMIT = ASYNC (E) => {
[Link]();
SETERROR("");
TRY {
AWAIT LOGIN([Link], [Link]);
//BACK BUTTON WON'T RETURN TO LOGIN
NAVIGATE("/USER/DASHBOARD", { REPLACE: TRUE });
} CATCH (ERR) {
SETERROR(ERR);
};
RETURN (
<DIV CLASSNAME="FLEX ITEMS-CENTER JUSTIFY-CENTER MIN-H-SCREEN BG-
GRAY-100 PX-4">
<DIV CLASSNAME="BG-WHITE P-8 ROUNDED-2XL SHADOW-MD W-FULL MAX-W-
MD">
<H2 CLASSNAME="TEXT-2XL FONT-BOLD TEXT-CENTER TEXT-INDIGO-600 MB-
6">
USER LOGIN
</H2>
{ERROR && (
<DIV CLASSNAME="MB-4 P-2 TEXT-SM TEXT-RED-700 BG-RED-100
ROUNDED">
{ERROR}
</DIV>
)}
<FORM ONSUBMIT={HANDLESUBMIT} CLASSNAME="SPACE-Y-4">
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700">EMAIL</LABEL>
<INPUT
TYPE="EMAIL"
NAME="EMAIL"
VALUE={[Link]}
ONCHANGE={HANDLECHANGE}
PLACEHOLDER="ENTER YOUR EMAIL"
CLASSNAME="W-FULL MT-1 P-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-INDIGO-500 OUTLINE-NONE"
REQUIRED
/>
</DIV>
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700">PASSWORD</LABEL>
<INPUT
TYPE="PASSWORD"
NAME="PASSWORD"
VALUE={[Link]}
ONCHANGE={HANDLECHANGE}
PLACEHOLDER="ENTER YOUR PASSWORD"
CLASSNAME="W-FULL MT-1 P-2 BORDER ROUNDED-LG FOCUS:RING-2
FOCUS:RING-INDIGO-500 OUTLINE-NONE"
REQUIRED
/>
</DIV>
<BUTTON
TYPE="SUBMIT"
DISABLED={LOADING}
CLASSNAME={`W-FULL TEXT-WHITE PY-2 ROUNDED-LG ${
LOADING
? "BG-GRAY-400 CURSOR-NOT-ALLOWED"
: "BG-INDIGO-600 HOVER:BG-INDIGO-700"
}`}
>
{LOADING ? "LOGGING IN..." : "LOGIN"}
</BUTTON>
</FORM>
<P CLASSNAME="TEXT-CENTER TEXT-GRAY-600 MT-4">
DON’T HAVE AN ACCOUNT?{" "}
<LINK
TO="/USER/REGISTER"
CLASSNAME="TEXT-INDIGO-600 FONT-SEMIBOLD HOVER:UNDERLINE"
>
REGISTER
</LINK>
</P>
</DIV>
</DIV>
);
}
[Link]
IMPORT { USEEFFECT, USESTATE } FROM "REACT";
IMPORT AXIOS FROM "AXIOS";
IMPORT { USEMENTOR } FROM "../CONTEXT/MENTORCONTEXT";
IMPORT TOAST FROM "REACT-HOT-TOAST"
IMPORT {
USERS,
BOOKOPEN,
PLUSCIRCLE,
LOGOUT,
LAYOUTDASHBOARD,
CLOCK,
CHECKCIRCLE,
MENU,
X,
} FROM "LUCIDE-REACT";
IMPORT {
PIE,
CELL,
PIECHART,
BARCHART,
BAR,
XAXIS,
YAXIS,
CARTESIANGRID,
TOOLTIP,
LEGEND,
RESPONSIVECONTAINER,
} FROM "RECHARTS";
IMPORT { USENAVIGATE } FROM "REACT-ROUTER-DOM";
EXPORT DEFAULT FUNCTION MENTORDASHBOARD() {
CONST { MENTOR, TOKEN, LOGOUT } = USEMENTOR();
CONST NAVIGATE = USENAVIGATE();
CONST [STATS, SETSTATS] = USESTATE({});
CONST [COURSES, SETCOURSES] = USESTATE([]);
CONST [LOADING, SETLOADING] = USESTATE(TRUE);
CONST [ACTIVETAB, SETACTIVETAB] = USESTATE("STATS");
CONST [SHOWADDCOURSE, SETSHOWADDCOURSE] = USESTATE(FALSE);
CONST [SHOWADDLESSON, SETSHOWADDLESSON] = USESTATE(FALSE);
CONST [SELECTEDCOURSE, SETSELECTEDCOURSE] = USESTATE(NULL);
CONST [SIDEBAROPEN, SETSIDEBAROPEN] = USESTATE(FALSE);
CONST [COURSEFORM, SETCOURSEFORM] = USESTATE({
NAME: "",
DESCRIPTION: "",
CATEGORY: "",
THUMBNAIL: NULL,
});
CONST [LESSONFORM, SETLESSONFORM] = USESTATE({
TITLE: "",
CONTENT: "",
VIDEO: NULL,
ORDER: 1,
});
// FETCH COURSES AND STATS
CONST FETCHCOURSES = ASYNC () => {
IF (!TOKEN) RETURN;
TRY {
CONST HEADERS = { AUTHORIZATION: `BEARER ${TOKEN}` };
CONST RES = AWAIT [Link](
"HTTP://LOCALHOST:3000/API/V1/COURSE/MENTOR/GET-ALL-COURSES",
{ HEADERS, WITHCREDENTIALS: TRUE }
);
CONST ALLCOURSES = [Link] || [];
SETCOURSES(ALLCOURSES);
CONST TOTALCOURSES = [Link];
CONST PENDINGCOURSES = [Link](
(C) => [Link] === "PENDING"
).LENGTH;
CONST APPROVEDCOURSES = [Link](
(C) => [Link] === "APPROVED"
).LENGTH;
CONST REJECTEDCOURSES = [Link](
(C) => [Link] === "REJECTED"
).LENGTH;
SETSTATS({
TOTALCOURSES,
PENDINGCOURSES,
APPROVEDCOURSES,
REJECTEDCOURSES,
});
} CATCH (ERROR) {
[Link]("ERROR FETCHING COURSES:", ERROR);
} FINALLY {
SETLOADING(FALSE);
};
USEEFFECT(() => {
FETCHCOURSES();
}, [TOKEN]);
//ADD COURSE
CONST HANDLEADDCOURSE = ASYNC (E) => {
[Link]();
TRY {
CONST FORMDATA = NEW FORMDATA();
[Link]("NAME", [Link]);
[Link]("DESCRIPTION", [Link]);
[Link]("CATEGORY", [Link]);
IF ([Link])
[Link]("THUMBNAIL", [Link]);
AWAIT [Link](
"HTTP://LOCALHOST:3000/API/V1/COURSE/ADD-COURSE",
FORMDATA,
HEADERS: {
AUTHORIZATION: `BEARER ${TOKEN}`,
"CONTENT-TYPE": "MULTIPART/FORM-DATA",
},
WITHCREDENTIALS: TRUE,
);
[Link]("COURSE ADDED SUCCESSFULLY!");
SETCOURSEFORM({
NAME: "",
DESCRIPTION: "",
CATEGORY: "",
THUMBNAIL: NULL,
});
SETSHOWADDCOURSE(FALSE);
FETCHCOURSES();
} CATCH (ERROR) {
[Link]("FAILED TO ADD COURSE:", ERROR);
ALERT([Link]?.DATA?.MESSAGE || "ERROR ADDING COURSE");
};
//ADD LESSON
CONST HANDLEADDLESSON = ASYNC (E) => {
[Link]();
IF (![Link]) RETURN ALERT("PLEASE SELECT A VIDEO FILE.");
TRY {
CONST FORMDATA = NEW FORMDATA();
[Link]("TITLE", [Link]);
[Link]("CONTENT", [Link]);
[Link]("ORDER", [Link] || 1);
[Link]("VIDEOURL", [Link]);
AWAIT [Link](
`HTTP://LOCALHOST:3000/API/V1/LESSON/ADD-
LESSON/${SELECTEDCOURSE._ID}`,
FORMDATA,
HEADERS: {
AUTHORIZATION: `BEARER ${TOKEN}`,
"CONTENT-TYPE": "MULTIPART/FORM-DATA",
},
);
[Link]("LESSON ADDED SUCCESSFULLY!");
SETLESSONFORM({ TITLE: "", CONTENT: "", VIDEO: NULL, ORDER: 1 });
SETSHOWADDLESSON(FALSE);
FETCHCOURSES();
} CATCH (ERROR) {
[Link]("LESSON ERROR:", [Link]?.DATA ||
[Link]);
ALERT([Link]?.DATA?.MESSAGE || "ERROR ADDING LESSON");
};
IF (LOADING) RETURN <P CLASSNAME="TEXT-CENTER MT-10">LOADING...</P>;
//COURSE GRID
CONST COURSEGRID = ({ COURSES }) => (
<DIV CLASSNAME="GRID GRID-COLS-1 SM:GRID-COLS-2 LG:GRID-COLS-3 GAP-6">
{[Link]((COURSE) => (
<DIV
KEY={COURSE._ID}
CLASSNAME="BG-WHITE SHADOW ROUNDED-XL OVERFLOW-HIDDEN P-4"
>
<IMG
SRC={[Link] || "/[Link]"}
ALT={[Link]}
CLASSNAME="W-FULL H-40 OBJECT-COVER ROUNDED-LG"
/>
<H4 CLASSNAME="FONT-BOLD TEXT-LG MT-2">{[Link]}</H4>
<P CLASSNAME="TEXT-SM TEXT-GRAY-600 LINE-CLAMP-2">
{[Link]}
</P>
<P CLASSNAME="TEXT-SM TEXT-GRAY-500 MT-1">
STATUS: {[Link] || "PENDING"}
</P>
<BUTTON
ONCLICK={() => {
SETSELECTEDCOURSE(COURSE);
SETSHOWADDLESSON(TRUE);
}}
CLASSNAME="MT-2 W-FULL FLEX ITEMS-CENTER JUSTIFY-CENTER GAP-2
PX-3 PY-2 BG-GRAY-600 TEXT-WHITE ROUNDED-LG HOVER:BG-GRAY-700"
>
<PLUSCIRCLE CLASSNAME="W-5 H-5" /> ADD LESSON
</BUTTON>
<BUTTON
ONCLICK={() => NAVIGATE(`/COURSE/${COURSE._ID}/LESSONS`)}
CLASSNAME="MT-2 W-FULL FLEX ITEMS-CENTER JUSTIFY-CENTER GAP-2
PX-3 PY-2 BG-BLUE-600 TEXT-WHITE ROUNDED-LG HOVER:BG-BLUE-700"
>
VIEW LESSONS
</BUTTON>
</DIV>
))}
</DIV>
);
RETURN (
<DIV CLASSNAME="MIN-H-SCREEN BG-GRAY-100 FLEX">
{/* SIDEBAR */}
<DIV
CLASSNAME={`FIXED INSET-Y-0 LEFT-0 W-64 BG-WHITE SHADOW-LG P-4 FLEX
FLEX-COL TRANSFORM TRANSITION-TRANSFORM DURATION-300 Z-40
${
SIDEBAROPEN ? "TRANSLATE-X-0" : "-TRANSLATE-X-FULL"
} MD:TRANSLATE-X-0`}
>
<DIV CLASSNAME="FLEX JUSTIFY-BETWEEN ITEMS-CENTER MB-8">
<H2 CLASSNAME="TEXT-2XL FONT-BOLD FLEX ITEMS-CENTER GAP-2">
<LAYOUTDASHBOARD CLASSNAME="W-6 H-6 TEXT-BLUE-600" /> MENTOR
PANEL
</H2>
<BUTTON CLASSNAME="MD:HIDDEN" ONCLICK={() =>
SETSIDEBAROPEN(FALSE)}>
<X CLASSNAME="W-6 H-6" />
</BUTTON>
</DIV>
<UL CLASSNAME="SPACE-Y-3 FLEX-1">
{[
{ KEY: "STATS", LABEL: "DASHBOARD", ICON: USERS },
{ KEY: "COURSES", LABEL: "MY COURSES", ICON: BOOKOPEN },
{ KEY: "PENDING", LABEL: "PENDING COURSES", ICON: CLOCK },
{ KEY: "APPROVED", LABEL: "APPROVED COURSES", ICON: CHECKCIRCLE },
].MAP(({ KEY, LABEL, ICON: ICON }) => (
<LI KEY={KEY}>
<BUTTON
ONCLICK={() => {
SETACTIVETAB(KEY);
SETSIDEBAROPEN(FALSE);
}}
CLASSNAME={`W-FULL FLEX ITEMS-CENTER GAP-3 PX-3 PY-2 ROUNDED-
LG TRANSITION ${
ACTIVETAB === KEY
? "BG-BLUE-600 TEXT-WHITE"
: "HOVER:BG-GRAY-100 TEXT-GRAY-700"
}`}
>
<ICON CLASSNAME="W-5 H-5" /> {LABEL}
</BUTTON>
</LI>
))}
<LI>
<BUTTON
ONCLICK={() => {
SETSHOWADDCOURSE(TRUE);
SETSIDEBAROPEN(FALSE);
}}
CLASSNAME="W-FULL FLEX ITEMS-CENTER GAP-3 PX-3 PY-2 ROUNDED-LG
TRANSITION BG-GREEN-600 TEXT-WHITE HOVER:BG-GREEN-700"
>
<PLUSCIRCLE CLASSNAME="W-5 H-5" /> ADD COURSE
</BUTTON>
</LI>
</UL>
<BUTTON
ONCLICK={LOGOUT}
CLASSNAME="MT-6 W-FULL PX-4 PY-2 BG-RED-600 TEXT-WHITE ROUNDED-
LG HOVER:BG-RED-700 FLEX ITEMS-CENTER JUSTIFY-CENTER GAP-2"
>
<LOGOUT CLASSNAME="W-5 H-5" /> LOGOUT
</BUTTON>
</DIV>
<DIV CLASSNAME="FLEX-1 P-6 MD:ML-64">
<DIV CLASSNAME="MD:HIDDEN FLEX JUSTIFY-BETWEEN ITEMS-CENTER MB-
6">
<H2 CLASSNAME="TEXT-XL FONT-BOLD TEXT-GREEN-700">
WELCOME, {MENTOR?.NAME}
</H2>
<BUTTON
CLASSNAME="TEXT-GRAY-600"
ONCLICK={() => SETSIDEBAROPEN(TRUE)}
>
<MENU CLASSNAME="W-6 H-6" />
</BUTTON>
</DIV>
<H2 CLASSNAME="HIDDEN MD:BLOCK TEXT-2XL FONT-BOLD MB-6 TEXT-
GREEN-700">
WELCOME, {MENTOR?.NAME || "MENTOR"}
</H2>
{ACTIVETAB === "STATS" && (
<DIV>
<DIV CLASSNAME="GRID GRID-COLS-1 SM:GRID-COLS-2 LG:GRID-COLS-4
GAP-6 MB-8">
<DIV CLASSNAME="BG-WHITE SHADOW ROUNDED-LG P-5 TEXT-CENTER">
<P CLASSNAME="TEXT-GRAY-600">TOTAL COURSES</P>
<P CLASSNAME="FONT-BOLD TEXT-3XL TEXT-INDIGO-600">
{[Link]}
</P>
</DIV>
<DIV CLASSNAME="BG-WHITE SHADOW ROUNDED-LG P-5 TEXT-CENTER">
<P CLASSNAME="TEXT-GRAY-600">APPROVED</P>
<P CLASSNAME="FONT-BOLD TEXT-3XL TEXT-GREEN-600">
{[Link]}
</P>
</DIV>
<DIV CLASSNAME="BG-WHITE SHADOW ROUNDED-LG P-5 TEXT-CENTER">
<P CLASSNAME="TEXT-GRAY-600">PENDING</P>
<P CLASSNAME="FONT-BOLD TEXT-3XL TEXT-YELLOW-500">
{[Link]}
</P>
</DIV>
<DIV CLASSNAME="BG-WHITE SHADOW ROUNDED-LG P-5 TEXT-CENTER">
<P CLASSNAME="TEXT-GRAY-600">REJECTED</P>
<P CLASSNAME="FONT-BOLD TEXT-3XL TEXT-RED-500">
{[Link]}
</P>
</DIV>
</DIV>
{/* CHART */}
<DIV CLASSNAME="BG-WHITE SHADOW ROUNDED-LG P-6">
<H3 CLASSNAME="TEXT-LG FONT-SEMIBOLD TEXT-GRAY-800 TEXT-
CENTER MB-4">
COURSE STATUS OVERVIEW
</H3>
<DIV CLASSNAME="H-96">
<RESPONSIVECONTAINER WIDTH="100%" HEIGHT="100%">
<PIECHART>
<PIE
DATA={[
{ NAME: "APPROVED", VALUE: [Link] || 0 },
{ NAME: "PENDING", VALUE: [Link] || 0 },
{ NAME: "REJECTED", VALUE: [Link] || 0 },
]}
DATAKEY="VALUE"
NAMEKEY="NAME"
CX="50%"
CY="50%"
INNERRADIUS={80}
OUTERRADIUS={130}
PADDINGANGLE={4}
LABEL={({ NAME, VALUE }) => `${NAME}: ${VALUE}`}
>
<CELL FILL="#22C55E" /> {/* APPROVED - GREEN */}
<CELL FILL="#EAB308" /> {/* PENDING - YELLOW */}
<CELL FILL="#EF4444" /> {/* REJECTED - RED */}
</PIE>
<TOOLTIP />
<LEGEND VERTICALALIGN="BOTTOM" HEIGHT={36} />
</PIECHART>
</RESPONSIVECONTAINER>
</DIV>
</DIV>
</DIV>
)}
{ACTIVETAB === "COURSES" && (
<DIV>
<H3 CLASSNAME="TEXT-XL FONT-BOLD MB-4">MY COURSES</H3>
<COURSEGRID COURSES={COURSES} />
</DIV>
)}
{ACTIVETAB === "PENDING" && (
<DIV>
<H3 CLASSNAME="TEXT-XL FONT-BOLD MB-4">PENDING COURSES</H3>
<COURSEGRID
COURSES={[Link]((C) => [Link] === "PENDING")}
/>
</DIV>
)}
{ACTIVETAB === "APPROVED" && (
<DIV>
<H3 CLASSNAME="TEXT-XL FONT-BOLD MB-4">APPROVED COURSES</H3>
<COURSEGRID
COURSES={[Link]((C) => [Link] === "APPROVED")}
/>
</DIV>
)}
{/* ADD COURSE MODAL */}
{SHOWADDCOURSE && (
<DIV CLASSNAME="FIXED INSET-0 BG-BLACK BG-OPACITY-50 FLEX ITEMS-
CENTER JUSTIFY-CENTER Z-50 P-4">
<FORM
ONSUBMIT={HANDLEADDCOURSE}
CLASSNAME="BG-WHITE P-6 ROUNDED-XL W-FULL MAX-W-MD FLEX FLEX-
COL GAP-3"
>
<H3 CLASSNAME="TEXT-LG FONT-BOLD">ADD COURSE</H3>
<INPUT
TYPE="TEXT"
PLACEHOLDER="NAME"
VALUE={[Link]}
ONCHANGE={(E) =>
SETCOURSEFORM({ ...COURSEFORM, NAME: [Link] })
REQUIRED
CLASSNAME="BORDER P-2 ROUNDED"
/>
<INPUT
TYPE="TEXT"
PLACEHOLDER="CATEGORY"
VALUE={[Link]}
ONCHANGE={(E) =>
SETCOURSEFORM({ ...COURSEFORM, CATEGORY: [Link] })
}
REQUIRED
CLASSNAME="BORDER P-2 ROUNDED"
/>
<TEXTAREA
PLACEHOLDER="DESCRIPTION"
VALUE={[Link]}
ONCHANGE={(E) =>
SETCOURSEFORM({ ...COURSEFORM, DESCRIPTION: [Link] })
REQUIRED
CLASSNAME="BORDER P-2 ROUNDED"
/>
<INPUT
TYPE="FILE"
ACCEPT="IMAGE/*"
ONCHANGE={(E) =>
SETCOURSEFORM({ ...COURSEFORM, THUMBNAIL: [Link][0] })
/>
<DIV CLASSNAME="FLEX JUSTIFY-END GAP-2 MT-2">
<BUTTON
TYPE="BUTTON"
ONCLICK={() => SETSHOWADDCOURSE(FALSE)}
CLASSNAME="PX-4 PY-2 BG-GRAY-400 ROUNDED-LG"
>
CANCEL
</BUTTON>
<BUTTON
TYPE="SUBMIT"
CLASSNAME="PX-4 PY-2 BG-BLUE-600 TEXT-WHITE ROUNDED-LG"
>
ADD
</BUTTON>
</DIV>
</FORM>
</DIV>
)}
{/* ADD LESSON MODAL */}
{SHOWADDLESSON && SELECTEDCOURSE && (
<DIV CLASSNAME="FIXED INSET-0 BG-BLACK BG-OPACITY-50 FLEX ITEMS-
CENTER JUSTIFY-CENTER Z-50 P-4">
<FORM
ONSUBMIT={HANDLEADDLESSON}
CLASSNAME="BG-WHITE P-6 ROUNDED-XL W-FULL MAX-W-MD FLEX FLEX-
COL GAP-3"
>
<H3 CLASSNAME="TEXT-LG FONT-BOLD">
ADD LESSON TO {[Link]}
</H3>
<INPUT
TYPE="TEXT"
PLACEHOLDER="LESSON TITLE"
VALUE={[Link]}
ONCHANGE={(E) =>
SETLESSONFORM({ ...LESSONFORM, TITLE: [Link] })
REQUIRED
CLASSNAME="BORDER P-2 ROUNDED"
/>
<TEXTAREA
PLACEHOLDER="CONTENT"
VALUE={[Link]}
ONCHANGE={(E) =>
SETLESSONFORM({ ...LESSONFORM, CONTENT: [Link] })
REQUIRED
CLASSNAME="BORDER P-2 ROUNDED"
/>
<INPUT
TYPE="FILE"
ACCEPT="VIDEO/*"
ONCHANGE={(E) =>
SETLESSONFORM({ ...LESSONFORM, VIDEO: [Link][0] })
REQUIRED
CLASSNAME="BORDER P-2 ROUNDED"
/>
<INPUT
TYPE="NUMBER"
PLACEHOLDER="ORDER"
VALUE={[Link]}
ONCHANGE={(E) =>
SETLESSONFORM({ ...LESSONFORM, ORDER: [Link] })
CLASSNAME="BORDER P-2 ROUNDED"
/>
<DIV CLASSNAME="FLEX JUSTIFY-END GAP-2 MT-2">
<BUTTON
TYPE="BUTTON"
ONCLICK={() => SETSHOWADDLESSON(FALSE)}
CLASSNAME="PX-4 PY-2 BG-GRAY-400 ROUNDED-LG"
>
CANCEL
</BUTTON>
<BUTTON
TYPE="SUBMIT"
CLASSNAME="PX-4 PY-2 BG-GREEN-600 TEXT-WHITE ROUNDED-LG"
>
ADD LESSON
</BUTTON>
</DIV>
</FORM>
</DIV>
)}
</DIV>
</DIV>
);
[Link]
// SRC/PAGES/[Link]
IMPORT { USESTATE } FROM "REACT";
IMPORT { USEMENTOR } FROM "../CONTEXT/MENTORCONTEXT";
EXPORT DEFAULT FUNCTION MENTORLOGIN() {
CONST [EMAIL, SETEMAIL] = USESTATE("");
CONST [PASSWORD, SETPASSWORD] = USESTATE("");
CONST [ERROR, SETERROR] = USESTATE("");
CONST { LOGIN, LOADING } = USEMENTOR();
CONST HANDLESUBMIT = ASYNC (E) => {
[Link]();
SETERROR("");
TRY {
AWAIT LOGIN(EMAIL, PASSWORD);
} CATCH (ERR) {
SETERROR(ERR);
};
RETURN (
<DIV CLASSNAME="MIN-H-SCREEN FLEX ITEMS-CENTER JUSTIFY-CENTER BG-
GRAY-100 PX-4">
<DIV CLASSNAME="MAX-W-MD W-FULL BG-WHITE P-8 ROUNDED-2XL SHADOW-
LG">
<H2 CLASSNAME="TEXT-2XL FONT-BOLD TEXT-CENTER TEXT-GRAY-800 MB-
6">
MENTOR LOGIN
</H2>
{ERROR && (
<DIV CLASSNAME="MB-4 P-2 TEXT-SM TEXT-RED-700 BG-RED-100
ROUNDED">
{ERROR}
</DIV>
)}
<FORM ONSUBMIT={HANDLESUBMIT} CLASSNAME="SPACE-Y-4">
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-1">
EMAIL
</LABEL>
<INPUT
TYPE="EMAIL"
CLASSNAME="W-FULL PX-3 PY-2 BORDER ROUNDED-LG FOCUS:RING
FOCUS:RING-BLUE-300 OUTLINE-NONE"
VALUE={EMAIL}
ONCHANGE={(E) => SETEMAIL([Link])}
REQUIRED
/>
</DIV>
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-1">
PASSWORD
</LABEL>
<INPUT
TYPE="PASSWORD"
CLASSNAME="W-FULL PX-3 PY-2 BORDER ROUNDED-LG FOCUS:RING
FOCUS:RING-BLUE-300 OUTLINE-NONE"
VALUE={PASSWORD}
ONCHANGE={(E) => SETPASSWORD([Link])}
REQUIRED
/>
</DIV>
<BUTTON
TYPE="SUBMIT"
DISABLED={LOADING}
CLASSNAME="W-FULL BG-BLUE-600 TEXT-WHITE PY-2 ROUNDED-LG
HOVER:BG-BLUE-700 TRANSITION DISABLED:OPACITY-50"
>
{LOADING ? "LOGGING IN..." : "LOGIN"}
</BUTTON>
</FORM>
</DIV>
</DIV>
);
[Link]
// SRC/PAGES/[Link]
IMPORT { USESTATE } FROM "REACT";
IMPORT { USEMENTOR } FROM "../CONTEXT/MENTORCONTEXT";
EXPORT DEFAULT FUNCTION MENTORLOGIN() {
CONST [EMAIL, SETEMAIL] = USESTATE("");
CONST [PASSWORD, SETPASSWORD] = USESTATE("");
CONST [ERROR, SETERROR] = USESTATE("");
CONST { LOGIN, LOADING } = USEMENTOR();
CONST HANDLESUBMIT = ASYNC (E) => {
[Link]();
SETERROR("");
TRY {
AWAIT LOGIN(EMAIL, PASSWORD);
} CATCH (ERR) {
SETERROR(ERR);
};
RETURN (
<DIV CLASSNAME="MIN-H-SCREEN FLEX ITEMS-CENTER JUSTIFY-CENTER BG-
GRAY-100 PX-4">
<DIV CLASSNAME="MAX-W-MD W-FULL BG-WHITE P-8 ROUNDED-2XL SHADOW-
LG">
<H2 CLASSNAME="TEXT-2XL FONT-BOLD TEXT-CENTER TEXT-GRAY-800 MB-
6">
MENTOR LOGIN
</H2>
{ERROR && (
<DIV CLASSNAME="MB-4 P-2 TEXT-SM TEXT-RED-700 BG-RED-100
ROUNDED">
{ERROR}
</DIV>
)}
<FORM ONSUBMIT={HANDLESUBMIT} CLASSNAME="SPACE-Y-4">
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-1">
EMAIL
</LABEL>
<INPUT
TYPE="EMAIL"
CLASSNAME="W-FULL PX-3 PY-2 BORDER ROUNDED-LG FOCUS:RING
FOCUS:RING-BLUE-300 OUTLINE-NONE"
VALUE={EMAIL}
ONCHANGE={(E) => SETEMAIL([Link])}
REQUIRED
/>
</DIV>
<DIV>
<LABEL CLASSNAME="BLOCK TEXT-GRAY-700 FONT-MEDIUM MB-1">
PASSWORD
</LABEL>
<INPUT
TYPE="PASSWORD"
CLASSNAME="W-FULL PX-3 PY-2 BORDER ROUNDED-LG FOCUS:RING
FOCUS:RING-BLUE-300 OUTLINE-NONE"
VALUE={PASSWORD}
ONCHANGE={(E) => SETPASSWORD([Link])}
REQUIRED
/>
</DIV>
<BUTTON
TYPE="SUBMIT"
DISABLED={LOADING}
CLASSNAME="W-FULL BG-BLUE-600 TEXT-WHITE PY-2 ROUNDED-LG
HOVER:BG-BLUE-700 TRANSITION DISABLED:OPACITY-50"
>
{LOADING ? "LOGGING IN..." : "LOGIN"}
</BUTTON>
</FORM>
</DIV>
</DIV>
);
[Link]
IMPORT { USEEFFECT, USESTATE } FROM "REACT";
IMPORT AXIOS FROM "AXIOS";
IMPORT { USEUSER } FROM "../CONTEXT/USERCONTEXT";
IMPORT TOAST FROM "REACT-HOT-TOAST";
IMPORT { MENU, X } FROM "LUCIDE-REACT";
EXPORT DEFAULT FUNCTION USERDASHBOARD() {
CONST { USER, TOKEN, LOGOUT } = USEUSER();
CONST [ALLCOURSES, SETALLCOURSES] = USESTATE([]);
CONST [ENROLLEDCOURSES, SETENROLLEDCOURSES] = USESTATE([]);
CONST [SELECTEDCOURSE, SETSELECTEDCOURSE] = USESTATE(NULL);
CONST [LESSONS, SETLESSONS] = USESTATE([]);
CONST [LOADING, SETLOADING] = USESTATE(TRUE);
CONST [VIEWENROLLED, SETVIEWENROLLED] = USESTATE(FALSE);
CONST [SIDEBAROPEN, SETSIDEBAROPEN] = USESTATE(FALSE);
USEEFFECT(() => {
CONST FETCHCOURSES = ASYNC () => {
IF (!TOKEN) RETURN;
SETLOADING(TRUE);
TRY {
CONST [COURSESRES, ENROLLEDRES] = AWAIT [Link]([
[Link]("HTTP://LOCALHOST:3000/API/V1/COURSE/GET-ALL-COURSES"),
[Link]("HTTP://LOCALHOST:3000/API/V1/USER-ENROLL/GET-
ENROLLED", {
HEADERS: { AUTHORIZATION: `BEARER ${TOKEN}` },
}),
]);
CONST APPROVEDCOURSES = ([Link] || []).FILTER(
(C) => C && [Link] === "APPROVED" && C._ID
);
SETALLCOURSES(APPROVEDCOURSES);
CONST ENROLLEDFULL = ([Link] || []).FILTER((C) => C &&
C._ID);
SETENROLLEDCOURSES(ENROLLEDFULL);
} CATCH (ERR) {
[Link](ERR);
[Link]("FAILED TO FETCH COURSES");
} FINALLY {
SETLOADING(FALSE);
};
FETCHCOURSES();
}, [TOKEN]);
CONST FETCHLESSONS = ASYNC (COURSEID) => {
TRY {
CONST RES = AWAIT [Link](
`HTTP://LOCALHOST:3000/API/V1/LESSON/GET-ALL-LESSONS/${COURSEID}`,
{ HEADERS: { AUTHORIZATION: `BEARER ${TOKEN}` } }
);
SETLESSONS([Link] || []);
CONST COURSE =
[Link]((C) => C && C._ID === COURSEID) ||
[Link]((C) => C && C._ID === COURSEID);
SETSELECTEDCOURSE(COURSE || NULL);
} CATCH (ERR) {
[Link](ERR);
[Link]("FAILED TO FETCH LESSONS");
}
};
CONST HANDLEENROLL = ASYNC (COURSEID) => {
IF ([Link]((C) => C && C._ID === COURSEID)) {
[Link]("YOU ARE ALREADY ENROLLED IN THIS COURSE");
RETURN;
TRY {
CONST RES = AWAIT [Link](
`HTTP://LOCALHOST:3000/API/V1/USER-ENROLL/${COURSEID}`,
{},
{ HEADERS: { AUTHORIZATION: `BEARER ${TOKEN}` } }
);
IF ([Link]) {
[Link]("ENROLLED SUCCESSFULLY!");
CONST ENROLLEDCOURSE = [Link]((C) => C && C._ID ===
COURSEID);
IF (ENROLLEDCOURSE) SETENROLLEDCOURSES((PREV) => [...PREV,
ENROLLEDCOURSE]);
ELSE {
CONST COURSERES = AWAIT [Link](
`HTTP://LOCALHOST:3000/API/V1/COURSE/GET-COURSE/${COURSEID}`,
{ HEADERS: { AUTHORIZATION: `BEARER ${TOKEN}` } }
);
IF ([Link]?.DATA && [Link]._ID)
SETENROLLEDCOURSES((PREV) => [...PREV, [Link]]);
} ELSE [Link]([Link] || "ENROLLMENT FAILED");
} CATCH (ERR) {
[Link]("ENROLL ERROR:", [Link]?.DATA || ERR);
[Link]([Link]?.DATA?.MESSAGE || "ENROLLMENT FAILED");
};
IF (LOADING) RETURN <P CLASSNAME="TEXT-CENTER MT-10 TEXT-
LG">LOADING...</P>;
CONST COURSESTODISPLAY = VIEWENROLLED ? ENROLLEDCOURSES :
ALLCOURSES;
RETURN (
<DIV CLASSNAME="MIN-H-SCREEN BG-GRAY-100 FLEX">
<ASIDE
CLASSNAME={`FIXED INSET-Y-0 LEFT-0 W-64 BG-WHITE SHADOW-LG P-6 FLEX
FLEX-COL TRANSFORM TRANSITION-TRANSFORM DURATION-300 Z-40
${SIDEBAROPEN ? "TRANSLATE-X-0" : "-TRANSLATE-X-FULL"} MD:TRANSLATE-
X-0`}
>
<DIV CLASSNAME="FLEX JUSTIFY-BETWEEN ITEMS-CENTER MB-8
MD:HIDDEN">
<H2 CLASSNAME="TEXT-2XL FONT-BOLD">DASHBOARD</H2>
<BUTTON ONCLICK={() => SETSIDEBAROPEN(FALSE)}>
<X CLASSNAME="W-6 H-6" />
</BUTTON>
</DIV>
<H2 CLASSNAME="TEXT-BLUE-600 HIDDEN MD:BLOCK TEXT-3XL FONT-BOLD
">STUDIPAL</H2>
<HR CLASSNAME="MY-2" />
{/* <H2 CLASSNAME="HIDDEN MD:BLOCK TEXT-2XL FONT-BOLD MB-8">USER
DASHBOARD</H2> */}
<BUTTON
ONCLICK={() => {
SETVIEWENROLLED(FALSE);
SETSIDEBAROPEN(FALSE);
}}
CLASSNAME={`MB-2 PX-4 PY-2 ROUNDED-LG W-FULL TEXT-WHITE ${
!VIEWENROLLED
? "BG-BLUE-600 HOVER:BG-BLUE-700"
: "BG-GRAY-300 HOVER:BG-GRAY-400"
} TRANSITION`}
>
ALL COURSES
</BUTTON>
<BUTTON
ONCLICK={() => {
SETVIEWENROLLED(TRUE);
SETSIDEBAROPEN(FALSE);
}}
CLASSNAME={`MB-2 PX-4 PY-2 ROUNDED-LG W-FULL TEXT-WHITE ${
VIEWENROLLED
? "BG-BLUE-600 HOVER:BG-BLUE-700"
: "BG-GRAY-300 HOVER:BG-GRAY-400"
} TRANSITION`}
>
ENROLLED COURSES
</BUTTON>
<BUTTON
ONCLICK={LOGOUT}
CLASSNAME="MT-AUTO PX-4 PY-2 BG-RED-600 TEXT-WHITE ROUNDED-LG
HOVER:BG-RED-700 TRANSITION"
>
LOGOUT
</BUTTON>
</ASIDE>
<MAIN CLASSNAME="FLEX-1 P-6 MD:ML-64 OVERFLOW-Y-AUTO">
<DIV CLASSNAME="MD:HIDDEN FLEX JUSTIFY-BETWEEN ITEMS-CENTER MB-
6">
<H2 CLASSNAME="TEXT-2XL FONT-BOLD TEXT-BLUE-600">STUDIPAL</H2>
<BUTTON ONCLICK={() => SETSIDEBAROPEN(TRUE)}>
<MENU CLASSNAME="W-6 H-6" />
</BUTTON>
</DIV>
{!SELECTEDCOURSE && (
<>
<H1 CLASSNAME="TEXT-XL SM:TEXT-2XL FONT-BOLD MB-6 TEXT-GRAY-
800">
<SPAN CLASSNAME="BLOCK TEXT-XL SM:TEXT-2XL TEXT-GREEN-600 MB-
2">
WELCOME, {USER?.NAME}
</SPAN>
<HR />
{VIEWENROLLED ? "ENROLLED COURSES" : "ALL COURSES"}
</H1>
{[Link] === 0 && (
<P CLASSNAME="TEXT-GRAY-500 TEXT-CENTER">NO COURSES TO
DISPLAY.</P>
)}
<DIV CLASSNAME="GRID GRID-COLS-1 SM:GRID-COLS-2 MD:GRID-COLS-3
LG:GRID-COLS-4 GAP-6">
{COURSESTODISPLAY
.FILTER((COURSE) => COURSE && COURSE._ID)
.MAP((COURSE) => {
CONST ISENROLLED = [Link](
(C) => C && C._ID === COURSE._ID
);
RETURN (
<DIV
KEY={COURSE._ID}
CLASSNAME="BG-WHITE ROUNDED-XL SHADOW-MD HOVER:SHADOW-
2XL HOVER:SCALE-105 TRANSITION-TRANSFORM DURATION-300 OVERFLOW-
HIDDEN GROUP"
>
<DIV CLASSNAME="RELATIVE">
<IMG
SRC={[Link] || "/[Link]"}
ALT={[Link] || "COURSE"}
CLASSNAME="W-FULL H-48 OBJECT-COVER"
/>
<DIV CLASSNAME="ABSOLUTE INSET-0 BG-GRADIENT-TO-T FROM-
BLACK/50 TO-TRANSPARENT OPACITY-0 GROUP-HOVER:OPACITY-100
TRANSITION-OPACITY"></DIV>
<SPAN CLASSNAME="ABSOLUTE TOP-2 RIGHT-2 BG-INDIGO-600
TEXT-WHITE TEXT-XS PX-3 PY-1 ROUNDED-FULL FONT-MEDIUM">
{[Link] || "GENERAL"}
</SPAN>
<H2 CLASSNAME="ABSOLUTE BOTTOM-2 LEFT-2 TEXT-WHITE FONT-
SEMIBOLD TEXT-SM SM:TEXT-BASE LINE-CLAMP-2">
{[Link] || "UNTITLED COURSE"}
</H2>
</DIV>
<DIV CLASSNAME="P-4">
<P CLASSNAME="TEXT-GRAY-600 TEXT-SM MB-3 LINE-CLAMP-3">
{[Link] || "NO DESCRIPTION AVAILABLE."}
</P>
<P CLASSNAME="TEXT-SM TEXT-GRAY-500 MB-3">
MENTOR: {[Link]?.NAME || "N/A"}
</P>
<DIV CLASSNAME="FLEX JUSTIFY-CENTER">
{ISENROLLED ? (
<BUTTON
ONCLICK={() => FETCHLESSONS(COURSE._ID)}
CLASSNAME="W-FULL BG-BLUE-600 HOVER:BG-BLUE-700 TEXT-
WHITE FONT-MEDIUM PY-2 ROUNDED-LG TRANSITION-ALL DURATION-300
SHADOW HOVER:SHADOW-LG"
>
VIEW LESSONS
</BUTTON>
):(
<BUTTON
ONCLICK={() => HANDLEENROLL(COURSE._ID)}
CLASSNAME="W-FULL BG-GREEN-600 HOVER:BG-GREEN-700
TEXT-WHITE FONT-MEDIUM PY-2 ROUNDED-LG TRANSITION-ALL DURATION-300
SHADOW HOVER:SHADOW-LG"
>
ENROLL NOW
</BUTTON>
)}
</DIV>
</DIV>
</DIV>
);
})}
</DIV>
</>
)}
{SELECTEDCOURSE && (
<>
<BUTTON
ONCLICK={() => {
SETSELECTEDCOURSE(NULL);
SETLESSONS([]);
}}
CLASSNAME="MB-6 PX-4 PY-2 BG-GRAY-300 ROUNDED-LG HOVER:BG-
GRAY-400 TRANSITION"
>
← BACK TO COURSES
</BUTTON>
<H2 CLASSNAME="TEXT-2XL FONT-BOLD MB-6 TEXT-GRAY-800">
{[Link]} - LESSONS
</H2>
<DIV CLASSNAME="GRID GRID-COLS-1 SM:GRID-COLS-2 LG:GRID-COLS-3
GAP-6">
{[Link] === 0 ? (
<P CLASSNAME="TEXT-GRAY-500">NO LESSONS AVAILABLE.</P>
):(
[Link](
(LESSON) =>
LESSON &&
LESSON._ID && (
<DIV
KEY={LESSON._ID}
CLASSNAME="BG-WHITE ROUNDED-XL SHADOW-MD
HOVER:SHADOW-LG TRANSITION OVERFLOW-HIDDEN"
>
<DIV CLASSNAME="P-4">
<H4 CLASSNAME="FONT-BOLD TEXT-LG MB-
2">{[Link]}</H4>
<P CLASSNAME="TEXT-SM TEXT-GRAY-600 MB-2 LINE-CLAMP-3">
{[Link]}
</P>
{[Link] && (
<VIDEO
CONTROLS
CLASSNAME="W-FULL H-48 ROUNDED-LG MT-2"
>
<SOURCE SRC={[Link]} TYPE="VIDEO/MP4" />
</VIDEO>
)}
</DIV>
</DIV>
)}
</DIV>
</>
)}
</MAIN>
</DIV>
);
}
ROUTES
[Link]
IMPORT { NAVIGATE } FROM "REACT-ROUTER-DOM";
IMPORT { USEUSER } FROM "../CONTEXT/USERCONTEXT";
IMPORT { USEMENTOR } FROM "../CONTEXT/MENTORCONTEXT";
IMPORT { USEADMIN } FROM "../CONTEXT/ADMINCONTEXT";
EXPORT DEFAULT FUNCTION PROTECTEDROUTE({ CHILDREN, ROLE }) {
IF (ROLE === "USER") {
CONST { TOKEN } = USEUSER();
RETURN TOKEN ? CHILDREN : <NAVIGATE TO="/USER/LOGIN" REPLACE />;
IF (ROLE === "MENTOR") {
CONST { TOKEN } = USEMENTOR();
RETURN TOKEN ? CHILDREN : <NAVIGATE TO="/MENTOR/LOGIN" REPLACE />;
IF (ROLE === "ADMIN") {
CONST { TOKEN } = USEADMIN();
RETURN TOKEN ? CHILDREN : <NAVIGATE TO="/ADMIN/LOGIN" REPLACE />;
RETURN <NAVIGATE TO="/" REPLACE />;
[Link]
IMPORT { NAVIGATE } FROM "REACT-ROUTER-DOM";
IMPORT { USEUSER } FROM "../CONTEXT/USERCONTEXT";
IMPORT { USEMENTOR } FROM "../CONTEXT/MENTORCONTEXT";
IMPORT { USEADMIN } FROM "../CONTEXT/ADMINCONTEXT";
EXPORT DEFAULT FUNCTION PUBLICROUTE({ CHILDREN, ROLE }) {
CONST { TOKEN: USERTOKEN } = USEUSER();
CONST { TOKEN: MENTORTOKEN } = USEMENTOR();
CONST { TOKEN: ADMINTOKEN } = USEADMIN();
IF (ROLE === "USER" && USERTOKEN) RETURN <NAVIGATE
TO="/USER/DASHBOARD" REPLACE />;
IF (ROLE === "MENTOR" && MENTORTOKEN) RETURN <NAVIGATE
TO="/MENTOR/DASHBOARD" REPLACE />;
IF (ROLE === "ADMIN" && ADMINTOKEN) RETURN <NAVIGATE
TO="/ADMIN/DASHBOARD" REPLACE />;
RETURN CHILDREN;
BACKEND
CONTROLLER-ADMIN
CONST CONST BCRYPT = REQUIRE("BCRYPT");
CONST JWT = REQUIRE("JSONWEBTOKEN");
CONST ADMIN = REQUIRE("../MODELS/[Link]");
CONST COURSE = REQUIRE("../MODELS/[Link]")
CONST MENTOR = REQUIRE("../MODELS/[Link]")
CONST USER = REQUIRE("../MODELS/[Link]");
// REGISTER ADMIN
CONST REGISTERADMIN = ASYNC (REQ, RES) => {
TRY {
CONST { NAME, EMAIL, PASSWORD } = [Link];
IF (!NAME || !EMAIL || !PASSWORD) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "ALL FIELDS ARE
REQUIRED" });
}
CONST EMAILLOWER = [Link]().TOLOWERCASE();
CONST EXISTINGADMIN = AWAIT [Link]({ EMAIL: EMAILLOWER });
IF (EXISTINGADMIN) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "ADMIN ALREADY
REGISTERED" });
CONST HASHEDPASSWORD = AWAIT [Link](PASSWORD, 10);
CONST NEWADMIN = NEW ADMIN({ NAME, EMAIL: EMAILLOWER, PASSWORD:
HASHEDPASSWORD });
CONST SAVEDADMIN = AWAIT [Link]();
[Link](201).JSON({
STATUS: 1,
MESSAGE: "ADMIN REGISTERED SUCCESSFULLY",
DATA: { ID: SAVEDADMIN._ID, NAME: [Link], EMAIL:
[Link] },
});
} CATCH (ERROR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: `SERVER ERROR:
${[Link]}` });
};
// LOGIN ADMIN
CONST LOGINADMIN = ASYNC (REQ, RES) => {
TRY {
CONST { EMAIL, PASSWORD } = [Link];
IF (!EMAIL || !PASSWORD) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "ALL FIELDS ARE
REQUIRED" });
CONST EMAILLOWER = [Link]().TOLOWERCASE();
CONST ADMIN = AWAIT [Link]({ EMAIL: EMAILLOWER });
IF (!ADMIN) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "ADMIN NOT
REGISTERED" });
CONST ISMATCH = AWAIT [Link](PASSWORD, [Link]);
IF (!ISMATCH) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "EMAIL OR
PASSWORD IS WRONG" });
CONST TOKEN = [Link]({ ID: ADMIN._ID, ROLE: "ADMIN" },
[Link].JWT_SECRET, { EXPIRESIN: "7D" });
[Link]("TOKEN", TOKEN, {
HTTPONLY: TRUE,
SECURE: [Link].NODE_ENV === "PRODUCTION",
SAMESITE: "STRICT",
MAXAGE: 7 * 24 * 60 * 60 * 1000,
});
[Link](200).JSON({ STATUS: 1, MESSAGE: "ADMIN LOGIN SUCCESSFUL",
TOKEN });
} CATCH (ERROR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: `SERVER ERROR:
${[Link]}` });
}
};
// ADDING MENTOR
CONST ADDMENTOR = ASYNC (REQ, RES) => {
TRY {
CONST { NAME, EMAIL, PASSWORD } = [Link];
IF (!NAME || !EMAIL || !PASSWORD) {
RETURN [Link](403).JSON({
MESSAGE: "ALL FIELDS ARE REQUIRED",
});
CONST EXISTINGMENTOR = AWAIT [Link]({ EMAIL });
IF (EXISTINGMENTOR) {
RETURN [Link](403).JSON({
MESSAGE: "MENTOR ALREADY REGISTERED",
});
CONST HASHEDPASSWORD = AWAIT [Link](PASSWORD, 10);
CONST MENTOR = NEW MENTOR({
NAME,
EMAIL,
PASSWORD: HASHEDPASSWORD,
});
CONST SAVEDMENTOR = AWAIT [Link]();
[Link](201).JSON({
STATUS: 1,
MESSAGE: "MENTOR CREATED SUCCESSFULLY",
SAVEDMENTOR,
});
} CATCH (ERROR) {
[Link](500).JSON({
STATUS: 0,
MESSAGE: `SERVER ERROR: ${[Link]}`,
});
};
//APPROVE A PENDING COURSE
CONST APPROVECOURSE = ASYNC (REQ, RES) => {
TRY {
CONST { ID } = [Link]; // PENDING COURSE ID
CONST PENDINGCOURSE = AWAIT [Link](ID);
IF (!PENDINGCOURSE || [Link] !== "PENDING") {
RETURN [Link](404).JSON({ STATUS: 0, MESSAGE: "PENDING COURSE
NOT FOUND" });
// IF THIS IS AN UPDATE VERSION
IF ([Link]) {
// ARCHIVE OLD APPROVED COURSE
AWAIT [Link]([Link],
{ STATUS: "ARCHIVED" });
}
// APPROVE THE PENDING COURSE
[Link] = "APPROVED";
AWAIT [Link]();
[Link](200).JSON({
STATUS: 1,
MESSAGE: "COURSE APPROVED SUCCESSFULLY",
DATA: PENDINGCOURSE
});
} CATCH (ERROR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: `SERVER ERROR:
${[Link]}` });
};
//REJECT A PENDING COURSE
CONST REJECTCOURSE = ASYNC (REQ, RES) => {
TRY {
CONST { ID } = [Link];
CONST PENDINGCOURSE = AWAIT [Link](ID);
IF (!PENDINGCOURSE || [Link] !== "PENDING") {
RETURN [Link](404).JSON({ STATUS: 0, MESSAGE: "PENDING COURSE
NOT FOUND" });
[Link] = "REJECTED";
AWAIT [Link]();
[Link](200).JSON({
STATUS: 1,
MESSAGE: "COURSE REJECTED SUCCESSFULLY",
DATA: PENDINGCOURSE
});
} CATCH (ERROR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: `SERVER ERROR:
${[Link]}` });
};
//GET ADMIN STATS
CONST GETADMINSTATS = ASYNC (REQ, RES) => {
TRY {
CONST TOTALUSERS = AWAIT [Link]();
CONST TOTALMENTORS = AWAIT [Link]();
CONST TOTALPENDINGCOURSES = AWAIT
[Link]({ STATUS: "PENDING" });
CONST TOTALAPPROVEDCOURSES = AWAIT
[Link]({ STATUS: "APPROVED" });
CONST TOTALREJECTEDCOURSES = AWAIT
[Link]({ STATUS: "REJECTED" });
CONST TOTALCOURSES = AWAIT [Link]();
[Link](200).JSON({
STATUS: 1,
MESSAGE: "ADMIN STATS FETCHED SUCCESSFULLY",
DATA: {
TOTALUSERS,
TOTALMENTORS,
TOTALPENDINGCOURSES,
TOTALAPPROVEDCOURSES,
TOTALREJECTEDCOURSES,
TOTALCOURSES,
},
});
} CATCH (ERROR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: `SERVER ERROR:
${[Link]}` });
};
// GET ALL MENTORS
CONST GETALLMENTORS = ASYNC (REQ, RES) => {
TRY {
CONST MENTORS = AWAIT [Link]().SELECT("-PASSWORD"); // EXCLUDE
PASSWORD IF ANY
[Link]({
STATUS: 1,
MESSAGE: "ALL MENTORS FETCHED SUCCESSFULLY",
DATA: MENTORS,
});
} CATCH (ERROR) {
[Link](500).JSON({
STATUS: 0,
MESSAGE: "ERROR FETCHING MENTORS",
ERROR: [Link],
});
};
// GET ALL COURSES
CONST GETALLCOURSES = ASYNC (REQ, RES) => {
TRY {
CONST COURSES = AWAIT [Link]().POPULATE("MENTOR", "NAME
EMAIL"); // POPULATE MENTOR INFO IF NEEDED
[Link]({
STATUS: 1,
MESSAGE: "ALL COURSES FETCHED SUCCESSFULLY",
DATA: COURSES,
});
} CATCH (ERROR) {
[Link](500).JSON({
STATUS: 0,
MESSAGE: "ERROR FETCHING COURSES",
ERROR: [Link],
});
};
[Link] = { REGISTERADMIN, LOGINADMIN,APPROVECOURSE,
REJECTCOURSE, ADDMENTOR, GETADMINSTATS, GETALLMENTORS,
GETALLCOURSES};
CONTROLLER-CONTACT
REQUIRE("DOTENV").CONFIG();
CONST CONTACT = REQUIRE("../MODELS/[Link]");
CONST SENDEMAIL = REQUIRE("../UTILS/SENDEMAIL");
CONST SUBMITCONTACT = ASYNC (REQ, RES) => {
TRY {
CONST { NAME, EMAIL, MESSAGE, SUBJECT = "" } = [Link];
IF (!NAME || !EMAIL || !MESSAGE) {
RETURN [Link](400).JSON({
SUCCESS: FALSE,
MESSAGE: "ALL FIELDS ARE REQUIRED.",
});
CONST NEWCONTACT = NEW CONTACT({ NAME, EMAIL, MESSAGE });
AWAIT [Link]();
CONST ADMINSUBJECT = `NEW CONTACT FORM SUBMISSION: ${SUBJECT ||
"CONTACT INQUIRY"}`;
CONST ADMINHTML = `
<H2>NEW CONTACT FORM SUBMISSION</H2>
<P><STRONG>NAME:</STRONG> ${NAME}</P>
<P><STRONG>EMAIL:</STRONG> ${EMAIL}</P>
<P><STRONG>MESSAGE:</STRONG></P>
<P>${MESSAGE}</P>
`;
CONST USERSUBJECT = `WE RECEIVED YOUR MESSAGE: ${SUBJECT ||
"CONTACT INQUIRY"}`;
CONST USERHTML = `
<H2>THANK YOU FOR REACHING OUT!</H2>
<P>HI ${NAME},</P>
<P>THANKS FOR CONTACTING US. WE’VE RECEIVED YOUR MESSAGE AND
WILL RESPOND SHORTLY.</P>
<HR/>
<P><STRONG>YOUR MESSAGE:</STRONG></P>
<P>${MESSAGE}</P>
<HR/>
<P>BEST REGARDS,<BR/>THE STUDIPAL TEAM</P>
`;
AWAIT SENDEMAIL([Link].EMAIL_USER, ADMINSUBJECT, ADMINHTML);
AWAIT SENDEMAIL(EMAIL, USERSUBJECT, USERHTML);
[Link](200).JSON({
SUCCESS: TRUE,
MESSAGE: "FORM SUBMITTED SUCCESSFULLY!",
});
} CATCH (ERR) {
[Link]("ERROR IN SUBMITCONTACT:", ERR);
[Link](500).JSON({
SUCCESS: FALSE,
MESSAGE: "SOMETHING WENT WRONG.",
ERROR: [Link],
});
};
// GET ALL CONTACTS
CONST GETALLCONTACTS = ASYNC (REQ, RES) => {
TRY {
CONST CONTACTS = AWAIT [Link]();
[Link](200).JSON({
SUCCESS: TRUE,
MESSAGE: "CONTACTS FETCHED SUCCESSFULLY.",
DATA: CONTACTS,
});
} CATCH (ERROR) {
[Link](500).JSON({
SUCCESS: FALSE,
MESSAGE: "FAILED TO FETCH CONTACTS.",
ERROR: [Link],
});
};
// GET SINGLE CONTACT BY ID
CONST GETCONTACTBYID = ASYNC (REQ, RES) => {
TRY {
CONST CONTACT = AWAIT [Link]([Link]);
IF (!CONTACT) {
RETURN RES
.STATUS(404)
.JSON({ SUCCESS: FALSE, MESSAGE: "CONTACT NOT FOUND." });
[Link](200).JSON({
SUCCESS: TRUE,
MESSAGE: "CONTACT FETCHED SUCCESSFULLY.",
DATA: CONTACT,
});
} CATCH (ERROR) {
[Link](500).JSON({
SUCCESS: FALSE,
MESSAGE: "ERROR FETCHING CONTACT.",
ERROR: [Link],
});
}
};
// DELETE ALL CONTACTS
CONST DELETEALL = ASYNC (REQ, RES) => {
TRY {
AWAIT [Link]();
[Link](200).JSON({
SUCCESS: TRUE,
MESSAGE: "ALL CONTACTS DELETED SUCCESSFULLY.",
});
} CATCH (ERROR) {
[Link](500).JSON({
SUCCESS: FALSE,
MESSAGE: "FAILED TO DELETE ALL CONTACTS.",
ERROR: [Link],
});
};
// DELETE CONTACT BY ID
CONST DELETECONTACT = ASYNC (REQ, RES) => {
TRY {
CONST CONTACT = AWAIT [Link]([Link]);
IF (!CONTACT) {
RETURN RES
.STATUS(404)
.JSON({ SUCCESS: FALSE, MESSAGE: "CONTACT NOT FOUND." });
RES
.STATUS(200)
.JSON({ SUCCESS: TRUE, MESSAGE: "CONTACT DELETED SUCCESSFULLY." });
} CATCH (ERROR) {
[Link](500).JSON({
SUCCESS: FALSE,
MESSAGE: "ERROR DELETING CONTACT.",
ERROR: [Link],
});
};
[Link] = {
SUBMITCONTACT,
GETALLCONTACTS,
GETCONTACTBYID,
DELETEALL,
DELETECONTACT,
};
[Link]
CONST BCRYPT = REQUIRE("BCRYPT");
CONST JWT = REQUIRE("JSONWEBTOKEN");
CONST USER = REQUIRE("../MODELS/[Link]");
// REGISTER USER
CONST REGISTERUSER = ASYNC (REQ, RES) => {
TRY {
CONST { NAME, EMAIL, PASSWORD } = [Link];
IF (!NAME || !EMAIL || !PASSWORD) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "ALL FIELDS ARE
REQUIRED" });
}
CONST EMAILLOWER = [Link]().TOLOWERCASE();
CONST EXISTINGUSER = AWAIT [Link]({ EMAIL: EMAILLOWER });
IF (EXISTINGUSER) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "USER ALREADY
REGISTERED" });
CONST HASHEDPASSWORD = AWAIT [Link](PASSWORD, 10);
CONST NEWUSER = NEW USER({ NAME, EMAIL: EMAILLOWER, PASSWORD:
HASHEDPASSWORD });
CONST SAVEDUSER = AWAIT [Link]();
[Link](201).JSON({
STATUS: 1,
MESSAGE: "USER REGISTERED SUCCESSFULLY",
DATA: { ID: SAVEDUSER._ID, NAME: [Link], EMAIL:
[Link] },
});
} CATCH (ERROR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: `SERVER ERROR:
${[Link]}` });
};
// LOGIN USER
CONST LOGINUSER = ASYNC (REQ, RES) => {
TRY {
CONST { EMAIL, PASSWORD } = [Link];
IF (!EMAIL || !PASSWORD) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "ALL FIELDS ARE
REQUIRED" });
CONST EMAILLOWER = [Link]().TOLOWERCASE();
CONST USER = AWAIT [Link]({ EMAIL: EMAILLOWER });
IF (!USER) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "USER NOT
REGISTERED" });
CONST ISMATCH = AWAIT [Link](PASSWORD, [Link]);
IF (!ISMATCH) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "EMAIL OR
PASSWORD IS WRONG" });
CONST TOKEN = [Link]({ ID: USER._ID, ROLE: "USER" },
[Link].JWT_SECRET, { EXPIRESIN: "7D" });
[Link]("TOKEN", TOKEN, {
HTTPONLY: TRUE,
SECURE: [Link].NODE_ENV === "PRODUCTION",
SAMESITE: "STRICT",
MAXAGE: 7 * 24 * 60 * 60 * 1000,
});
[Link](200).JSON({ STATUS: 1, MESSAGE: "USER LOGIN SUCCESSFUL",
TOKEN });
} CATCH (ERROR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: `SERVER ERROR:
${[Link]}` });
}
};
//GET PROFILE
CONST GETPROFILE = ASYNC (REQ, RES) => {
TRY {
CONST USER = AWAIT [Link]([Link]).SELECT("-PASSWORD");
IF (!USER) {
RETURN [Link](404).JSON({ STATUS: 0, MESSAGE: "USER NOT FOUND" });
[Link]({ STATUS: 1, DATA: USER });
} CATCH (ERR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: "SERVER ERROR" });
};
//UPDATE NAME
CONST UPDATENAME = ASYNC (REQ, RES) => {
TRY {
CONST { NAME } = [Link];
IF (!NAME) {
RETURN [Link](400).JSON({ STATUS: 0, MESSAGE: "NAME IS REQUIRED"
});
CONST USER = AWAIT [Link](
[Link],
{ NAME },
{ NEW: TRUE }
).SELECT("-PASSWORD");
[Link]({ STATUS: 1, MESSAGE: "NAME UPDATED SUCCESSFULLY", DATA:
USER });
} CATCH (ERR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: "SERVER ERROR" });
};
//UPDATE PASSWORD
CONST UPDATEPASSWORD = ASYNC (REQ, RES) => {
TRY {
CONST { OLDPASSWORD, NEWPASSWORD } = [Link];
IF (!OLDPASSWORD || !NEWPASSWORD) {
RETURN RES
.STATUS(400)
.JSON({ STATUS: 0, MESSAGE: "BOTH OLD AND NEW PASSWORD ARE
REQUIRED" });
CONST USER = AWAIT [Link]([Link]);
IF (!USER)
RETURN [Link](404).JSON({ STATUS: 0, MESSAGE: "USER NOT FOUND" });
CONST ISMATCH = AWAIT [Link](OLDPASSWORD,
[Link]);
IF (!ISMATCH) {
RETURN RES
.STATUS(400)
.JSON({ STATUS: 0, MESSAGE: "OLD PASSWORD IS INCORRECT" });
[Link] = AWAIT [Link](NEWPASSWORD, 10);
AWAIT [Link]();
[Link]({ STATUS: 1, MESSAGE: "PASSWORD UPDATED SUCCESSFULLY" });
} CATCH (ERR) {
[Link](500).JSON({ STATUS: 0, MESSAGE: "SERVER ERROR" });
};
[Link] = { REGISTERUSER, LOGINUSER, GETPROFILE, UPDATENAME,
UPDATEPASSWORD};
DATABASE
CONST MONGOOSE = REQUIRE("MONGOOSE");
CONST CONNECTDB = ASYNC () => {
TRY {
AWAIT [Link]([Link].MONGO_URI);
[Link]("DATABASE CONNECTED SUCCESSFULLY");
} CATCH (ERROR) {
[Link]("DATABASE CONNECTING ERROR");
};
[Link] = CONNECTDB;
MODELS-ADMIN
CONST MONGOOSE = REQUIRE("MONGOOSE")
CONST ADMINSCHEMA = NEW [Link]({
NAME:{
TYPE: STRING,
REQUIRED: TRUE
},
EMAIL:{
TYPE: STRING,
REQUIRED: TRUE
},
PASSWORD:{
TYPE: STRING,
REQUIRED: TRUE
})
[Link] = NEW [Link]("ADMIN", ADMINSCHEMA)
CONTACT-MODELS
CONST MONGOOSE = REQUIRE("MONGOOSE");
CONST CONTACTSCHEMA = NEW [Link](
{
NAME: { TYPE: STRING, REQUIRED: TRUE },
EMAIL: { TYPE: STRING, REQUIRED: TRUE },
MESSAGE: { TYPE: STRING, REQUIRED: TRUE },
},
TIMESTAMPS: TRUE,
);
CONST CONTACT = [Link]("CONTACT", CONTACTSCHEMA);
[Link] = CONTACT;
COURSE-MODELS
CONST MONGOOSE = REQUIRE("MONGOOSE");
CONST COURSESCHEMA = NEW [Link]({
NAME: {
TYPE: STRING,
REQUIRED: TRUE
},
DESCRIPTION: {
TYPE: STRING,
REQUIRED: TRUE
},
CATEGORY: {
TYPE: STRING,
REQUIRED: TRUE
},
THUMBNAIL: {
TYPE: STRING,
REQUIRED: FUNCTION () {
// ONLY REQUIRE THUMBNAIL WHEN ADDING A NEW COURSE
RETURN [Link] === "PENDING";
},
MENTOR: {
TYPE: [Link],
REF: "MENTOR"
},
STUDENTSENROLLED: [{
TYPE: [Link],
REF: "USER"
}],
STATUS: { //ADMIN WILL APPROVE OR REJECT
TYPE: STRING,
ENUM: ["PENDING", "APPROVED", "REJECTED"],
DEFAULT: "PENDING"
},
ORIGINALCOURSEID: {
TYPE: [Link],
REF: "COURSE"
}, {
TIMESTAMPS: TRUE
});
[Link] = [Link]("COURSE", COURSESCHEMA);
==
ENROLL-MODELS
CONST { DEFAULT: MONGOOSE } = REQUIRE("MONGOOSE");
CONST ENROLLMENTSCHEMA = NEW [Link]({
COURSEID: {
TYPE: [Link],
REF: "COURSE",
},
USERID: {
TYPE: [Link],
REF: "USER",
REQUIRED: TRUE,
},
STATUS: {
TYPE: STRING,
ENUM: ["PENDING", "COMPLETED"],
DEFAULT: "PENDING",
},
PROGRESS: {
TYPE: NUMBER,
DEFAULT: 0, // PERCENTAGE (0–100)
},
ENROLLEDAT: {
TYPE: DATE,
DEFAULT: [Link],
},
COMPLETEDAT: DATE,
});
CONST ENROLLMODEL = NEW [Link]("ENROLL",
ENROLLMENTSCHEMA);
[Link] = ENROLLMODEL;
ROUTES-ADMIN
CONST EXPRESS = REQUIRE("EXPRESS")
CONST { REGISTERADMIN, LOGINADMIN, APPROVECOURSE, REJECTCOURSE,
ADDMENTOR, GETADMINSTATS, GETALLMENTORS, GETALLCOURSES } =
REQUIRE("../CONTROLLERS/[Link]")
CONST ADMINAUTHENTICATION = REQUIRE("../MIDDLEWARE/[Link]")
CONST { GETPENDINGCOURSES } =
REQUIRE("../CONTROLLERS/[Link]")
CONST { GETCOURSELESSONS } =
REQUIRE("../CONTROLLERS/[Link]")
CONST ADMINROUTER = [Link]()
[Link]("/REGISTER", REGISTERADMIN)
[Link]("/LOGIN", LOGINADMIN)
[Link]("/ME", ADMINAUTHENTICATION, (REQ, RES) => {
[Link]([Link]) // 👈 SEND ADMIN DETAILS (WITHOUT PASSWORD)
})
[Link]("/ADD-MENTOR", ADDMENTOR)
[Link]("/APPROVE-COURSE/:ID", ADMINAUTHENTICATION,
APPROVECOURSE);
[Link]("/REJECT-COURSE/:ID", ADMINAUTHENTICATION,
REJECTCOURSE);
[Link]("/STATS", ADMINAUTHENTICATION, GETADMINSTATS);
[Link]("/PENDING-COURSES", ADMINAUTHENTICATION,
GETPENDINGCOURSES)
[Link]("/ALL-MENTORS", ADMINAUTHENTICATION,
GETALLMENTORS);
[Link]("/ALL-COURSES", ADMINAUTHENTICATION, GETALLCOURSES);
[Link]("/COURSE/:COURSEID/LESSONS", ADMINAUTHENTICATION,
GETCOURSELESSONS);
[Link] = ADMINROUTER
ROUTES-CONTACT
CONST EXPRESS = REQUIRE("EXPRESS");
CONST ROUTER = [Link]();
CONST { SUBMITCONTACT } =
REQUIRE("../CONTROLLERS/[Link]");
[Link]("/", SUBMITCONTACT);
[Link] = ROUTER;
ROUTES-COURSE
CONST EXPRESS = REQUIRE("EXPRESS")
CONST UPLOAD = REQUIRE("../MIDDLEWARE/UPLOADS")
CONST { ADDCOURSE, GETALLCOURSES, GETONECOURSE, UPDATECOURSE,
DELETECOURSE, GETALLCOURSESMENTOR } =
REQUIRE("../CONTROLLERS/[Link]")
CONST MENTORAUTHENTICATION = REQUIRE("../MIDDLEWARE/[Link]")
// CONST USERAUTHENTICATION = REQUIRE("../MIDDLEWARE/[Link]")
CONST COURSEROUTER = [Link]()
[Link]("/ADD-
COURSE",MENTORAUTHENTICATION,[Link]("THUMBNAIL"),ADDCOURSE)
[Link]("/GET-ALL-COURSES",GETALLCOURSES)
[Link]("/MENTOR/GET-ALL-COURSES",
MENTORAUTHENTICATION,GETALLCOURSESMENTOR)
[Link]("/GET-ONE-COURSE/:ID",
MENTORAUTHENTICATION,GETONECOURSE)
[Link]("/UPDATE-COURSE/:ID",
MENTORAUTHENTICATION,UPDATECOURSE)
[Link]("/DELETE-COURSE/:ID",
MENTORAUTHENTICATION,DELETECOURSE)
[Link] = COURSEROUTER
[Link]
C CONST CLOUDINARY = REQUIRE("CLOUDINARY").V2
REQUIRE("DOTENV").CONFIG();
[Link]({
CLOUD_NAME: [Link].CLOUDINARY_CLOUD_NAME,
API_KEY: [Link].CLOUDINARY_API_KEY,
API_SECRET: [Link].CLOUDINARY_API_SECRET,
});
[Link] = CLOUDINARY;
SEND [Link]
REQUIRE("DOTENV").CONFIG();
CONST NODEMAILER = REQUIRE("NODEMAILER");
CONST SENDEMAIL = ASYNC (TO, SUBJECT, HTML) => {
IF (![Link].EMAIL_USER || ![Link].EMAIL_PASS) {
THROW NEW ERROR("EMAIL CREDENTIALS ARE NOT CONFIGURED IN
ENVIRONMENT VARIABLES.");
TRY {
CONST TRANSPORTER = [Link]({
HOST: "[Link]",
PORT: 587,
SECURE: FALSE,
AUTH: {
USER: [Link].EMAIL_USER,
PASS: [Link].EMAIL_PASS,
},
TLS: {
REJECTUNAUTHORIZED: FALSE,
},
});
AWAIT [Link]();
CONST RESULT = AWAIT [Link]({
FROM: `"STUDIPAL CONTACT" <${[Link].EMAIL_USER}>`,
TO,
SUBJECT,
HTML,
});
[Link]("EMAIL SENT SUCCESSFULLY:", [Link]);
RETURN RESULT;
} CATCH (ERROR) {
[Link]("ERROR SENDING EMAIL:", ERROR);
THROW NEW ERROR(`EMAIL COULD NOT BE SENT: ${[Link]}`);
};
[Link] = SENDEMAIL;
[Link]
REQUIRE("DOTENV").CONFIG();
CONST EXPRESS = REQUIRE("EXPRESS")
CONST COOKIEPARSER = REQUIRE("COOKIE-PARSER")
CONST COURSEROUTER = REQUIRE("./ROUTES/[Link]")
CONST USERROUTER = REQUIRE("./ROUTES/[Link]")
CONST MENTORROUTER = REQUIRE("./ROUTES/[Link]")
CONST ADMINROUTER = REQUIRE("./ROUTES/[Link]")
CONST LESSONROUTER = REQUIRE("./ROUTES/[Link]")
CONST CONNECTDB = REQUIRE("./DATABASE/DB")
CONST ENROLLROUTER = REQUIRE("./ROUTES/[Link]")
CONST CONTACTROUTER = REQUIRE("./ROUTES/[Link]")
CONST CORS = REQUIRE("CORS")
CONST APP = EXPRESS()
CONST PORT = [Link] || 3000
[Link]([Link]());
[Link](COOKIEPARSER());
[Link](CORS({
ORIGIN: "HTTP://LOCALHOST:5173",
CREDENTIALS: TRUE
}));
CONNECTDB().THEN(()=>{
[Link]("/API/V1/COURSE", COURSEROUTER)
[Link]("/API/V1/USER", USERROUTER)
[Link]("/API/V1/MENTOR", MENTORROUTER)
[Link]("/API/V1/ADMIN", ADMINROUTER)
[Link]("/API/V1/LESSON", LESSONROUTER)
[Link]("/API/V1/USER-ENROLL", ENROLLROUTER)
[Link]("/API/V1/CONTACT",CONTACTROUTER)
[Link](PORT,()=>{
[Link](`DATABASE RUNNING ON PORT HTTP://LOCALHOST:${PORT}`)
})
})
.CATCH((ERROR)=>{
[Link]("DATABASE CONNECTION FAILED:", ERROR);
})
[Link]
"NAME": "SERVER",
"VERSION": "1.0.0",
"MAIN": "[Link]",
"SCRIPTS": {
"TEST": "ECHO \"ERROR: NO TEST SPECIFIED\" && EXIT 1",
"START": "NODEMON [Link]",
"DEV": "NODEMON [Link]"
},
"KEYWORDS": [],
"AUTHOR": "",
"LICENSE": "ISC",
"DESCRIPTION": "",
"DEPENDENCIES": {
"BCRYPT": "^6.0.0",
"BCRYPTJS": "^3.0.2",
"CLOUDINARY": "^2.7.0",
"COOKIE-PARSER": "^1.4.7",
"CORS": "^2.8.5",
"DOTENV": "^17.2.1",
"EXPRESS": "^5.1.0",
"JSONWEBTOKEN": "^9.0.2",
"MONGOOSE": "^8.17.0",
"MULTER": "^2.0.2",
"NODEMAILER": "^7.0.10",
"NODEMON": "^3.1.10",
"RESEND": "^6.12.3",
"STREAMIFIER": "^0.1.1"
}
OUTPUT
ADMIN LOGIN
ADMIN PORTAL
HOW TO ADD MENTOR
HOME PAGE
MENTOR CREDENTIONAL
NAME :
EMAIL :
PASSWORD :
MENTOR PORTOL-DASHBOARD
MENTOR CAN ADD COURSE –
HOW TO CREATE ACCOUNT IN STUDIPAL
NAME :
EMAIL :
PASSWORD :
YOU CAN ENROLL COURSCES IN STUDIPAL
BIBLIOGRAPHY
* SOFTWARE ENGINEERING BY ROGER S. PRESSMAN
* [Link] DOCUMENTATION
* [Link] OFFICIAL DOCUMENTATION
* MONGODB MANUAL
* [Link] GUIDE
* MDN WEB DOCS
* TAILWIND CSS DOCUMENTATION
WEB REFERENCES
HTTPS://[Link]
HTTPS://[Link]
HTTPS://[Link]
HTTPS://[Link]
HTTPS://[Link]