Instructor Profiles are a key feature in educational listing platforms, offering a window into the qualifications and expertise of the educators behind each course. These profiles provide potential students with valuable information about the instructors, helping them make informed decisions about their learning journey.
Key Features Of Instructor Profiles
Typically, an Instructor Profile includes the instructor’s name, photo, and a detailed biography that highlights their educational background, professional experience, and areas of expertise. Additional elements such as certifications, awards, and links to published work or social media profiles may also be featured, adding credibility and personal connection.
Instructor Profiles often showcase student reviews and ratings, providing insights into the instructor's teaching style and effectiveness. This feedback loop not only helps students choose the right courses but also motivates instructors to maintain high teaching standards.
In summary, Instructor Profiles are essential in building trust and transparency within educational platforms, offering a personal touch that enhances the overall learning experience.
Approach to implement Instructor Profiles
Backend
- Router Setup: Use express.Router() to define routes for managing instructors.
- Route Definitions: Set up routes to handle getting all instructors, creating a new instructor, retrieving an instructor by ID, and deleting an instructor.
- Controller Functions: Implement functions to interact with the Instructor model, including fetching, creating, retrieving, and deleting instructor data.
- Error Handling: Handle errors with appropriate status codes and messages to ensure robust error management.
- Data Management: Use Mongoose to define the schema and interact with the MongoDB database for CRUD operations.
- Modular Code: Organize routes and controller logic in separate files for maintainability and clarity.
Frontend
- Course Component: Displays course details, including image, title, description, creator, and stats, with options to watch or add to a playlist.
- Courses Component: Fetches course data, filters by keyword and category, and renders a list of Course components with search and category filtering.
- InstructorProfiles Component: Fetches and displays a list of instructors with their profile details, such as name, bio, expertise, and courses.
- InstructorProfile Component: Fetches and displays details of a single instructor based on the ID from URL parameters, including their bio, expertise, and list of courses.
- Data Fetching and State Management: Utilizes axios for API calls and React's useState and useEffect for managing and updating component state.
Backend Code
// app.js
const app = require("./app")
const { config } = require("dotenv")
const database = require('./config/database');
const cloudinary = require('cloudinary').v2;
database.connectedToDatabase();
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.CLOUD_API_KEY,
api_secret: process.env.CLOUD_API_SECRET,
});
config({
path: './config/config.env'
})
app.listen(process.env.PORT, () => {
console.log(`Server is Running at ${process.env.PORT}`);
})
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const user = require('./routes/userRoute')
const course = require('./routes/courseRoutes');
const ErrorMiddleware = require('./middleware/Error');
const cookieParser = require('cookie-parser');
const enrollment = require('./routes/enrollmentRoutes')
const instructorRoutes = require('./routes/instructorRoutes');
const cors = require('cors')
app.use(cors({
origin: ['http://localhost:3000'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser())
app.use('/api/v1', user)
app.use('/api/v1', enrollment)
app.use('/api/v1', course)
app.use('/api/v1', instructorRoutes);
app.use(ErrorMiddleware);
module.exports = app;
// config/database.js
const mongoose = require('mongoose');
const connectedToDatabase = async () => {
try {
await mongoose.connect('mongodb://localhost:27017/course', {
useNewUrlParser: true,
useUnifiedTopology: true
});
console.log('Connected to MongoDB');
} catch (error) {
console.error('MongoDB connection error:', error);
}
}
module.exports = { connectedToDatabase }
// Models/instructorModel.js
const mongoose = require('mongoose');
const InstructorSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
bio: {
type: String,
required: true,
},
expertise: {
type: String,
required: true,
},
imageSrc: {
type: String,
required: true,
},
courses: {
type: [String],
required: true,
},
});
const Instructor = mongoose.model('Instructor', InstructorSchema);
module.exports = Instructor;
// controllers/instructorController.js
const Instructor = require('../models/InstructorModel');
// Get all instructors
exports.getAllInstructors = async (req, res) => {
try {
const instructors = await Instructor.find();
res.status(200).json(instructors);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
// Create a new instructor
exports.createInstructor = async (req, res) => {
const { name, bio, expertise, imageSrc, courses } = req.body;
try {
const newInstructor = new Instructor({
name,
bio,
expertise,
imageSrc,
courses,
});
const instructor = await newInstructor.save();
res.status(201).json(instructor);
} catch (err) {
res.status(400).json({ message: err.message });
}
};
// Get instructor by ID
exports.getInstructorById = async (req, res) => {
try {
const instructor = await Instructor.findById(req.params.id);
if (!instructor) {
return res.status(404).json({ message: 'Instructor not found' });
}
res.status(200).json(instructor);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
// Delete an instructor
exports.deleteInstructor = async (req, res) => {
try {
const instructor = await Instructor.findById(req.params.id);
if (!instructor) {
return res.status(404).json({ message: 'Instructor not found' });
}
await instructor.remove();
res.status(200).json({ message: 'Instructor removed' });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
// routes/instructorRoutes.js
const express = require('express');
const router = express.Router();
const enrollmentController = require('../controllers/enrollmentController');
// Create a new enrollment
router.post('/enrollments', enrollmentController.createEnrollment);
// Get all enrollments
router.get('/enrollments', enrollmentController.getAllEnrollments);
// Get a specific enrollment by ID
router.get('/enrollments/:id', enrollmentController.getEnrollmentById);
// Update an enrollment by ID
router.put('/enrollments/:id', enrollmentController.updateEnrollment);
// Delete an enrollment by ID
router.delete('/enrollments/:id', enrollmentController.deleteEnrollment);
module.exports = router;
Start your server using the below command:
node index.jsFrontend Code
// App.jsx
import React from "react";
import { Route, BrowserRouter as Router, Routes } from "react-router-dom";
import Home from "./components/Home/Home";
import Header from "../src/components/Layout/Header/Header";
import Courses from "./components/Courses/Courses";
import Footer from "./components/Layout/Footer/Footer";
import Login from "./components/Auth/Login";
import Register from "./components/Auth/Register";
import ForgotPassword from "./components/Auth/ForgotPassword";
import ResetPassword from "./components/Auth/ResetPassword";
import Contact from "./components/Contact/Contact";
import Request from "./components/Request/Request";
import About from "./components/About/About";
import Subscribe from "./components/Payments/Subscribe";
import CoursePage from "./components/CoursePage/CoursePage";
import Profile from "./components/Profile/Profile";
import ChangePassword from "./components/Profile/ChangePassword";
import UpdateProfile from "./components/Profile/UpdateProfile";
import Dashboard from "./components/Admin/Dashboard/Dashboard";
import CreateCourses from "./components/Admin/CreateCourse/CreateCourses";
import AdminCourses from "./components/Admin/AdminCourses/AdminCourses";
import Users from "./components/Admin/Users/Users";
import EnrollmentForm from "./components/Auth/EnrollmentForm";
import InstructorProfiles from "./components/Instructor/Instructor";
import InstructorProfile from "./components/Instructor/InstructorProfile";
function App() {
return (
<Router>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/courses" element={<Courses />} />
<Route path="/course/:id" element={<CoursePage />} />
<Route path="/contact" element={<Contact />} />
<Route path="/request" element={<Request />} />
<Route path="/about" element={<About />} />
<Route path="/profile" element={<Profile />} />
<Route path="/changepassword" element={<ChangePassword />} />
<Route path="/updateprofile" element={<UpdateProfile />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Register />} />
<Route path="/forgetpassword" element={<ForgotPassword />} />
<Route path="/resetpassword/:token" element={<ResetPassword />} />
<Route path="/subscribe" element={<Subscribe />} />
<Route path="/admin/dashboard" element={<Dashboard />} />
<Route path="/admin/createcourse" element={<CreateCourses />} />
<Route path="/admin/courses" element={<AdminCourses />} />
<Route path="/admin/users" element={<Users />} />
<Route path="/enroll" element={<EnrollmentForm />} />
<Route path="/inst" element={<InstructorProfiles />} />
<Route path="/instructor/:id" element={<InstructorProfile />} />
</Routes>
<Footer />
</Router>
);
}
export default App;
// src/components/Instructor/InstructorProfiles.jsx
import React, { useEffect, useState } from "react";
import axios from "axios";
import { Box, Avatar, Text, Stack, Container, Heading } from "@chakra-ui/react";
const InstructorProfiles = () => {
const [instructors, setInstructors] = useState([]);
useEffect(() => {
const fetchInstructors = async () => {
try {
const { data } = await axios.get(
"http://localhost:4000/api/v1/instructors"
);
setInstructors(data);
} catch (error) {
console.error("Error fetching instructors:", error);
}
};
fetchInstructors();
}, []);
return (
<Container maxW="container.xl" py={8}>
<Heading mb={6}>Instructor Profiles</Heading>
<Stack spacing={6}>
{instructors.map((instructor) => (
<Box
key={instructor._id}
p={5}
shadow="md"
borderWidth="1px"
borderRadius="md"
bg="white"
>
<Stack direction="row" spacing={4} align="center">
<Avatar name={instructor.name} src={instructor.imageSrc} />
<Stack spacing={1}>
<Text fontWeight="bold" color={"black"} fontSize="xl">
{instructor.name}
</Text>
<Text color="gray.600">{instructor.bio}</Text>
<Text color="teal.500">{instructor.expertise}</Text>
<Text color="blue.500">
Courses: {instructor.courses.join(", ")}
</Text>
</Stack>
</Stack>
</Box>
))}
</Stack>
</Container>
);
};
export default InstructorProfiles;
// src/components/Instructor/instructorProfile.jsx
import React, { useState, useEffect } from "react";
import {
Avatar,
Box,
Container,
Heading,
Stack,
Text,
useToast,
Divider,
VStack,
Button,
} from "@chakra-ui/react";
import axios from "axios";
import { useParams } from "react-router-dom";
const InstructorProfile = () => {
const { id } = useParams();
const [instructor, setInstructor] = useState(null);
const toast = useToast();
useEffect(() => {
const fetchInstructor = async () => {
try {
const { data } = await axios.get(
`http://localhost:4000/api/v1/instructor/${id}`
);
setInstructor(data);
} catch (error) {
toast({
title: "Error fetching instructor.",
description:
error.response?.data?.message ||
"An error occurred while fetching instructor details.",
status: "error",
duration: 5000,
isClosable: true,
});
}
};
fetchInstructor();
}, [id, toast]);
if (!instructor) {
return <Text>Loading...</Text>;
}
return (
<Container maxW="container.md" py={16}>
<VStack spacing={6} align="center">
<Box
borderWidth={1}
borderRadius="lg"
overflow="hidden"
boxShadow="md"
p={4}
bg="white"
w="full"
>
<Avatar
name={instructor.name}
src={instructor.imageSrc}
size="xl"
mb={4}
/>
<Heading as="h1" size="lg" mb={2} textAlign="center">
{instructor.name}
</Heading>
<Text fontSize="md" color="gray.600" textAlign="center">
{instructor.bio}
</Text>
<Divider my={4} />
<Stack spacing={3} align="center">
<Text fontSize="md" fontWeight="bold" color="teal.500">
Expertise
</Text>
<Text fontSize="md" textAlign="center">
{instructor.expertise}
</Text>
<Divider />
<Text fontSize="md" fontWeight="bold" color="teal.500">
Courses
</Text>
<Text fontSize="md" textAlign="center">
{instructor.courses}
</Text>
</Stack>
</Box>
<Button
colorScheme="teal"
variant="solid"
size="lg"
mt={6}
onClick={() => alert("Follow Instructor")}
>
Follow Instructor
</Button>
</VStack>
</Container>
);
};
export default InstructorProfile;
// src/components/Courses/Course.jsx
import {
Button,
Container,
HStack,
Heading,
Image,
Input,
Stack,
Text,
VStack,
} from "@chakra-ui/react";
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
const Course = ({
views,
title,
imageSrc,
id,
addToPlaylistHandler,
creator,
description,
lectureCount,
}) => {
return (
<VStack className="course" alignItems={["center", "flex-start"]}>
<Image src={imageSrc} boxSize={"60"} objectFit={"contain"} />
<Heading
textAlign={["center", "left"]}
maxW={"200px"}
size={"sm"}
fontFamily={"sans-serif"}
noOfLines={3}
children={title}
/>
<Text children={description} noOfLines={2} />
<HStack>
<Text
fontWeight={"bold"}
textTransform={"uppercase"}
children={"Creator :"}
/>
<Link to={`/instructor/${creator?._id}`}>
<Text
fontFamily={"body"}
textTransform={"uppercase"}
color="blue.500"
_hover={{ textDecoration: "underline" }}
>
{creator?.name || "Unknown Creator"}
</Text>
</Link>
</HStack>
<Heading
textAlign={"center"}
size={"xs"}
children={`Lectures - ${lectureCount}`}
textTransform={"uppercase"}
/>
<Heading
size={"xs"}
children={`Views - ${views}`}
textTransform={"uppercase"}
/>
<Stack direction={["column", "row"]} alignItems={"center"}>
<Link to={`/course/${id}`}>
<Button colorScheme="yellow">Watch Now</Button>
</Link>
<Button
variant={"ghost"}
colorScheme="yellow"
onClick={() => addToPlaylistHandler(id)}
>
Add To Playlist
</Button>
</Stack>
</VStack>
);
};
const Courses = () => {
const [courses, setCourses] = useState([]);
const [keyword, setKeyword] = useState("");
const [category, setCategory] = useState("");
const categories = [
"App Development",
"Web Development",
"Data Structures & Algorithm",
"Machine Learning",
"Artificial Intelligence",
"Data Science",
];
useEffect(() => {
const fetchCourses = async () => {
try {
const { data } = await axios.get(
"http://localhost:4000/api/v1/course",
{
withCredentials: true,
}
);
setCourses(data.courses);
} catch (error) {
console.error(error.response.data.message);
}
};
fetchCourses();
}, []);
// Filter courses based on keyword and category
const filteredCourses = courses.filter(
(course) =>
course.title.toLowerCase().includes(keyword.toLowerCase()) &&
(category ? course.category === category : true)
);
const addToPlaylistHandler = (courseId) => {
console.log("Add To playlist", courseId);
};
return (
<Container minH={"95vh"} maxW={"container.lg"} paddingY={"8"}>
<Heading children="All Courses" m={"8"} />
<Input
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="Search a course"
type="text"
focusBorderColor="yellow"
/>
<HStack
overflowX={"auto"}
padding={"8"}
css={{ "&::-webkit-scrollbar": { display: "none" } }}
>
{categories.map((item, index) => (
<Button
colorScheme="yellow"
variant={"ghost"}
key={index}
onClick={() => setCategory(item)}
minW={"60"}
>
<Text children={item} />
</Button>
))}
<Button
colorScheme="yellow"
variant={"ghost"}
onClick={() => setCategory("")}
minW={"60"}
>
<Text children="All Categories" />
</Button>
</HStack>
<Stack
direction={["column", "row"]}
flexWrap={"wrap"}
justifyContent={["flex-start", "space-evenly"]}
alignItems={["center", "flex-start"]}
>
{filteredCourses.length > 0 ? (
filteredCourses.map((course) => (
<Course
key={course._id}
title={course.title}
description={course.description}
views={course.views}
imageSrc={course.poster.url}
id={course._id}
creator={course.createdBy}
lectureCount={course.numOfVideos}
addToPlaylistHandler={addToPlaylistHandler}
/>
))
) : (
<Text>No courses found</Text>
)}
</Stack>
</Container>
);
};
export default Courses;
// src/components/Admin/CreateCourse/CreateCourse.jsx
import {
Button,
Container,
Grid,
Heading,
Image,
Input,
Select,
VStack,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import axios from "axios";
import Sidebar from "../Sidebar";
const CreateCourses = () => {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [createdBy, setCreatedBy] = useState("");
const [category, setCategory] = useState("");
const [image, setImage] = useState(null);
const [imagePrev, setImagePrev] = useState("");
const [instructors, setInstructors] = useState([]);
const categories = [
"App Development",
"Web Development",
"Data Structures & Algorithm",
"Machine Learning",
"Artificial Intelligence",
"Data Science",
];
useEffect(() => {
const fetchInstructors = async () => {
try {
const { data } = await axios.get(
"http://localhost:4000/api/v1/instructors"
);
setInstructors(data);
} catch (error) {
console.error("Error fetching instructors:", error);
}
};
fetchInstructors();
}, []);
const changeImageHandler = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
setImagePrev(reader.result);
setImage(file);
};
};
const submitHandler = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("title", title);
formData.append("description", description);
formData.append("category", category);
formData.append("createdBy", createdBy); // Assuming `createdBy` is the instructor's ID
formData.append("file", image);
try {
const { data } = await axios.post(
"http://localhost:4000/api/v1/createcourse",
formData,
{
withCredentials: true,
headers: {
"Content-Type": "multipart/form-data",
},
}
);
console.log(data.message);
setTitle("");
setDescription("");
setCreatedBy("");
setCategory("");
setImage(null);
setImagePrev("");
} catch (error) {
console.error(error.response.data.message);
}
};
return (
<Grid minH={"100vh"} templateColumns={["1fr", "5fr 1fr"]}>
<Container py={"16"}>
<form onSubmit={submitHandler}>
<Heading
textTransform={"uppercase"}
my={16}
textAlign={["center", "left"]}
>
Create Course
</Heading>
<VStack m={"auto"} spacing={"8"}>
<Input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Title"
type="text"
focusBorderColor="purple.300"
/>
<Input
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Description"
type="text"
focusBorderColor="purple.300"
/>
<Select
focusBorderColor="purple.300"
value={createdBy}
onChange={(e) => setCreatedBy(e.target.value)}
placeholder="Select Instructor"
>
{instructors.map((instructor) => (
<option key={instructor._id} value={instructor._id}>
{instructor.name}
</option>
))}
</Select>
<Select
focusBorderColor="purple.300"
value={category}
onChange={(e) => setCategory(e.target.value)}
>
<option value="">Category</option>
{categories.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</Select>
<Input
accept="image/*"
css={{
"&::file-selector-button": {
color: "purple",
},
}}
required
id="chooseAvatar"
type="file"
focusBorderColor="purple.300"
onChange={changeImageHandler}
/>
{imagePrev && (
<Image src={imagePrev} boxSize="64" objectFit={"contain"} />
)}
<Button w={"full"} colorScheme="purple" type="submit">
Create
</Button>
</VStack>
</form>
</Container>
<Sidebar />
</Grid>
);
};
export default CreateCourses;
Start your frontend using the below command:
npm start