Commit 60d92c17 authored by Gamage B.G.J's avatar Gamage B.G.J

Merge branch 'feature/UI-API-Connect' into 'master'

Feature/ui api connect

See merge request !21
parents e3c48555 10f207b5
...@@ -32,7 +32,8 @@ export const createCurriculum = async (req, res) => { ...@@ -32,7 +32,8 @@ export const createCurriculum = async (req, res) => {
let totalTutorialMark = 0; let totalTutorialMark = 0;
for (const tutorialId of curriculumData.tutorials) { for (const tutorialId of curriculumData.tutorials) {
const tutorial = await Tutorial.findById(tutorialId); const tutorial = await Tutorial.findById(tutorialId);
totalTutorialMark += tutorial.tutorialMarks; const tutorialMarks = typeof tutorial.tutorialMarks === 'string' ? parseFloat(tutorial.tutorialMarks) : tutorial.tutorialMarks;
totalTutorialMark += isNaN(tutorialMarks) ? 0 : tutorialMarks;
} }
newCurriculum.curriculumMark = totalTutorialMark; newCurriculum.curriculumMark = totalTutorialMark;
...@@ -55,7 +56,8 @@ export const updateCurriculum = async (req, res) => { ...@@ -55,7 +56,8 @@ export const updateCurriculum = async (req, res) => {
let totalTutorialMark = 0; let totalTutorialMark = 0;
for (const tutorialId of updatedCurriculum.tutorials) { for (const tutorialId of updatedCurriculum.tutorials) {
const tutorial = await Tutorial.findById(tutorialId); const tutorial = await Tutorial.findById(tutorialId);
totalTutorialMark += tutorial.tutorialMarks; const tutorialMarks = typeof tutorial.tutorialMarks === 'string' ? parseFloat(tutorial.tutorialMarks) : tutorial.tutorialMarks;
totalTutorialMark += isNaN(tutorialMarks) ? 0 : tutorialMarks;
} }
updatedCurriculum.curriculumMark = totalTutorialMark; updatedCurriculum.curriculumMark = totalTutorialMark;
...@@ -71,7 +73,7 @@ export const deleteCurriculum = async (req, res) => { ...@@ -71,7 +73,7 @@ export const deleteCurriculum = async (req, res) => {
const { id } = req.params; const { id } = req.params;
try { try {
await Curriculum.findByIdAndDelete(id); await Curriculum.findByIdAndDelete(id);
res.status(200).json({ message: 'Curriculum deleted successfully' }); res.status(200).json({ _id: id, message: 'Curriculum deleted successfully' });
} catch (error) { } catch (error) {
res.status(404).json({ message: 'Curriculum not found' }); res.status(404).json({ message: 'Curriculum not found' });
} }
......
import { exec } from "child_process"; import { exec } from "child_process";
import multer from "multer"
export const marksCalculator = async (req, res) => { export const marksCalculator = async (req, res) => {
const imageData = req.file.buffer.toString('base64'); try {
const targetClass = req.body.class; // console.log(req.file);
const { curriculumIndex, tutorialIndex } = req.params;
const status = ""; // if (!req.file || !req.body.class || !req.params.curriculumIndex || !req.params.tutorialIndex) {
// return res.status(400).json({ code: "02", message: "Missing required data" });
// }
// console.log(curriculumIndex, tutorialIndex);
try { const imageData = req.file.buffer.toString('base64');
const targetClass = req.body.class;
const { curriculumIndex, tutorialIndex } = req.params;
if (curriculumIndex == 1 && tutorialIndex == 1) { if (curriculumIndex == 1 && tutorialIndex == 1) {
// Run Python script to perform prediction // Run Python script to perform prediction
const pythonProcess = exec('python prediction_config/C1T1/predict.py', (error, stdout, stderr) => { const pythonProcess = exec('python prediction_config/C1T1/predict.py', (error, stdout, stderr) => {
if (error) { if (error) {
console.error(error); console.error(error);
return res.status(500).json({ error: 'An error occurred' }); return res.status(500).json({ code: '03', message: 'An error occurred while running the prediction script' });
} }
const [predicted_class_name, confidence] = stdout.trim().split(','); const [predicted_class_name, confidence] = stdout.trim().split(',');
...@@ -43,9 +45,10 @@ export const marksCalculator = async (req, res) => { ...@@ -43,9 +45,10 @@ export const marksCalculator = async (req, res) => {
pythonProcess.stdin.write(`${imageData}\n${targetClass}`); pythonProcess.stdin.write(`${imageData}\n${targetClass}`);
pythonProcess.stdin.end(); pythonProcess.stdin.end();
} else { } else {
return res.status(400).json({ code: "02", message: "Curriculum Index or Tutorial Index Invalid" }) return res.status(400).json({ code: "02", message: "Curriculum Index or Tutorial Index Invalid" });
} }
} catch (error) { } catch (error) {
res.status(500).json({ code: "00", message: "Something went wrong" }) console.error(error);
res.status(500).json({ code: "00", message: "Something went wrong" });
} }
} }
\ No newline at end of file
...@@ -21,42 +21,64 @@ export const getTutorialById = async (req, res) => { ...@@ -21,42 +21,64 @@ export const getTutorialById = async (req, res) => {
export const createTutorial = async (req, res) => { export const createTutorial = async (req, res) => {
const tutorialData = req.body; const tutorialData = req.body;
// Calculate total tutorial marks based on task item marks // Calculate total tutorial marks based on task item marks
const totalTaskMarks = tutorialData.taskItems.reduce((total, task) => total + (task.taskItemMark || 0), 0); const totalTaskMarks = tutorialData.taskItems.reduce((total, task) => {
const taskItemMark = typeof task.taskItemMark === 'string' ? parseFloat(task.taskItemMark) : task.taskItemMark;
return total + (isNaN(taskItemMark) ? 0 : taskItemMark);
}, 0);
tutorialData.tutorialMarks = totalTaskMarks; tutorialData.tutorialMarks = totalTaskMarks;
try { try {
const newTutorial = new Tutorial(tutorialData); const newTutorial = new Tutorial(tutorialData);
await newTutorial.save(); await newTutorial.save();
res.status(201).json(newTutorial); res.status(201).json(newTutorial);
} catch (error) { } catch (error) {
res.status(400).json({ message: error.message }); res.status(500).json({ message: 'Failed to create tutorial', error: error.message });
} }
} };
export const updateTutorial = async (req, res) => { export const updateTutorial = async (req, res) => {
const { id } = req.params; const { id } = req.params;
const updatedTutorialData = req.body; const updatedTutorialData = req.body;
// Calculate total tutorial marks based on updated task item marks // Calculate total tutorial marks based on updated task item marks
const totalTaskMarks = updatedTutorialData.taskItems.reduce((total, task) => total + (task.taskItemMark || 0), 0); const totalTaskMarks = updatedTutorialData.taskItems.reduce((total, task) => {
const taskItemMark = typeof task.taskItemMark === 'string' ? parseFloat(task.taskItemMark) : task.taskItemMark;
return total + (isNaN(taskItemMark) ? 0 : taskItemMark);
}, 0);
updatedTutorialData.tutorialMarks = totalTaskMarks; updatedTutorialData.tutorialMarks = totalTaskMarks;
try { try {
const result = await Tutorial.findByIdAndUpdate(id, updatedTutorialData, { new: true }); const tutorial = await Tutorial.findById(id);
res.status(200).json(result);
if (!tutorial) {
return res.status(404).json({ message: 'Tutorial not found' });
}
// Update the tutorial with the new data
const updatedTutorial = await Tutorial.findByIdAndUpdate(id, updatedTutorialData, { new: true });
res.status(200).json(updatedTutorial);
} catch (error) { } catch (error) {
res.status(404).json({ message: 'Tutorial not found' }); res.status(500).json({ message: error.message });
} }
} };
export const deleteTutorial = async (req, res) => { export const deleteTutorial = async (req, res) => {
const { id } = req.params; const { id } = req.params;
try { try {
const tutorial = await Tutorial.findById(id);
if (!tutorial) {
return res.status(404).json({ message: 'Tutorial not found' });
}
await Tutorial.findByIdAndDelete(id); await Tutorial.findByIdAndDelete(id);
res.status(200).json({ message: 'Tutorial deleted successfully' }); res.status(200).json({ _id: id, message: 'Tutorial deleted successfully' });
} catch (error) { } catch (error) {
res.status(404).json({ message: 'Tutorial not found' }); res.status(500).json({ message: error.message });
} }
} };
...@@ -157,3 +157,23 @@ export const deleteUser = async (req, res) => { ...@@ -157,3 +157,23 @@ export const deleteUser = async (req, res) => {
} }
} }
export const currentUser = async (req, res) => {
try {
// req.userId contains the user ID extracted from the token by the auth middleware
const userId = req.userId;
// Fetch the user account information from the database
const user = await User.findById(userId);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
// Send the user account information as a response
res.status(200).json({ user });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
}
import Curriculum from "../models/curriculum.model.js";
import UserProgress from "../models/userProgress.model.js"; import UserProgress from "../models/userProgress.model.js";
// Logic to subscribe to a curriculum for a user
export const subscribeCurriculum = async (req, res) => { export const subscribeCurriculum = async (req, res) => {
const { userId, curriculum } = req.body; const { userId, curriculum } = req.body;
try { try {
// Check if the user is already subscribed to the curriculum
let userProgress = await UserProgress.findOne({ userId }); let userProgress = await UserProgress.findOne({ userId });
if (!userProgress) { if (!userProgress) {
// If there is no user progress record for this user, create a new one // If there is no user progress record for this user, create a new one
userProgress = new UserProgress({ userProgress = new UserProgress({
userId, userId,
curriculums: [curriculum], // Add the curriculum to the curriculums array curriculums: [curriculum],
}); });
} else { } else {
// If there is an existing user progress record, check if the curriculum already exists // If there is an existing user progress record, check if the curriculum already exists
...@@ -19,7 +20,7 @@ export const subscribeCurriculum = async (req, res) => { ...@@ -19,7 +20,7 @@ export const subscribeCurriculum = async (req, res) => {
if (existingCurriculumIndex !== -1) { if (existingCurriculumIndex !== -1) {
// If the curriculum exists, update it // If the curriculum exists, update it
userProgress.curriculums[existingCurriculumIndex] = curriculum; return res.status(400).json({ error: 'User already subscribed to this curriculum' });
} else { } else {
// If the curriculum doesn't exist, add it to the array // If the curriculum doesn't exist, add it to the array
userProgress.curriculums.push(curriculum); userProgress.curriculums.push(curriculum);
...@@ -27,9 +28,21 @@ export const subscribeCurriculum = async (req, res) => { ...@@ -27,9 +28,21 @@ export const subscribeCurriculum = async (req, res) => {
} }
// Update the totalCurriculumsMarks based on curriculum marks // Update the totalCurriculumsMarks based on curriculum marks
const totalCurriculumsMarks = userProgress.curriculums.reduce((total, curriculum) => total + curriculum.curriculumMark, 0); const totalCurriculumsMarks = userProgress.curriculums.reduce((total, curriculum) => {
const curriculumMark = typeof curriculum.curriculumMark === 'string' ? parseFloat(curriculum.curriculumMark) : curriculum.curriculumMark;
return total + (isNaN(curriculumMark) ? 0 : curriculumMark);
}, 0);
userProgress.totalCurriculumsMarks = totalCurriculumsMarks; userProgress.totalCurriculumsMarks = totalCurriculumsMarks;
const curriculumCode = curriculum.curriculumCode
// Now, update the curriculum collection with the subscribed user
await Curriculum.updateOne(
{ curriculumCode },
{ $addToSet: { subscribedUser: userId } }
);
// Save the user progress record // Save the user progress record
await userProgress.save(); await userProgress.save();
...@@ -86,8 +99,9 @@ export const updateTaskItemProgress = async (req, res) => { ...@@ -86,8 +99,9 @@ export const updateTaskItemProgress = async (req, res) => {
} }
// Update task item progress // Update task item progress
userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex].taskItemMarkUser = taskItemMarkUser; const taskItem = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex];
userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex].taskItemSpentTime = taskItemSpentTime; taskItem.taskItemMarkUser = typeof taskItemMarkUser === 'string' ? parseFloat(taskItemMarkUser) : taskItemMarkUser;
taskItem.taskItemSpentTime = taskItemSpentTime;
// Calculate total task marks and spent time for the tutorial // Calculate total task marks and spent time for the tutorial
const tutorial = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex]; const tutorial = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex];
...@@ -112,3 +126,4 @@ export const updateTaskItemProgress = async (req, res) => { ...@@ -112,3 +126,4 @@ export const updateTaskItemProgress = async (req, res) => {
res.status(500).json({ error: 'Internal server error' }); res.status(500).json({ error: 'Internal server error' });
} }
}; };
...@@ -35,7 +35,11 @@ const curriculumSchema = new mongoose.Schema({ ...@@ -35,7 +35,11 @@ const curriculumSchema = new mongoose.Schema({
type: Number, type: Number,
default: 1, // Default status as active (1) default: 1, // Default status as active (1)
}, },
...commonFields ...commonFields,
subscribedUser: [{
type: mongoose.Schema.Types.ObjectId,
ref: "User", // Reference to the User model if you have one
}],
}); });
const Curriculum = mongoose.model("Curriculum", curriculumSchema); const Curriculum = mongoose.model("Curriculum", curriculumSchema);
......
...@@ -16,7 +16,7 @@ const commonFields = { ...@@ -16,7 +16,7 @@ const commonFields = {
const taskItemSchema = new mongoose.Schema({ const taskItemSchema = new mongoose.Schema({
title: String, title: String,
description: String, description: String,
howToDo: String, howToDo: [String],
referenceImage: String, referenceImage: String,
referenceVideo: String, referenceVideo: String,
taskItemMark : { taskItemMark : {
......
...@@ -31,7 +31,7 @@ const tutorialTypeUserProgressSchema = new mongoose.Schema({ ...@@ -31,7 +31,7 @@ const tutorialTypeUserProgressSchema = new mongoose.Schema({
tutorialTitle: String, tutorialTitle: String,
tutorialDescription: String, tutorialDescription: String,
tutorialImage: String, tutorialImage: String,
tutorialMark: { tutorialMarks: {
type: Number, type: Number,
default: 0 default: 0
}, },
......
import express from "express"; import express from "express";
import { deleteUser, getUser, getUserAccordingToType, getUsers, signIn, signUp, updateUser } from "../controllers/user.controller.js"; import { currentUser, deleteUser, getUser, getUserAccordingToType, getUsers, signIn, signUp, updateUser } from "../controllers/user.controller.js";
import auth from "../middleware/auth.middleware.js";
const router = express.Router(); const router = express.Router();
router.post('/sign-in', signIn) router.post('/sign-in', signIn)
router.post('/sign-up', signUp) router.post('/sign-up', signUp)
router.get('/all', getUsers); router.get('/all', getUsers);
router.get('/current-user', auth, currentUser);
router.get('/:id', getUser); router.get('/:id', getUser);
router.get('/all/type/:userType', getUserAccordingToType); router.get('/all/type/:userType', getUserAccordingToType);
router.put('/:id', updateUser); router.put('/:id', updateUser);
......
...@@ -23,9 +23,13 @@ import userProgressRoutes from "./routes/userProgress.routes.js"; ...@@ -23,9 +23,13 @@ import userProgressRoutes from "./routes/userProgress.routes.js";
dotenv.config(); dotenv.config();
const app = express(); const app = express();
const corsOptions = {
origin: 'http://localhost:3000',
};
app.use(bodyParser.json({ limit: "30mb", extended: true })); app.use(bodyParser.json({ limit: "30mb", extended: true }));
app.use(bodyParser.urlencoded({ limit: "30mb", extended: true })); app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
app.use(cors()); app.use(cors(corsOptions));
//end //end
app.get("/", (req, res) => { app.get("/", (req, res) => {
......
...@@ -56,17 +56,27 @@ export const JWTProvider = ({ children }: { children: React.ReactElement }) => { ...@@ -56,17 +56,27 @@ export const JWTProvider = ({ children }: { children: React.ReactElement }) => {
const init = async () => { const init = async () => {
try { try {
const serviceToken = window.localStorage.getItem('serviceToken'); const serviceToken = window.localStorage.getItem('serviceToken');
console.log(verifyToken(serviceToken!));
if (serviceToken && verifyToken(serviceToken)) { if (serviceToken && verifyToken(serviceToken)) {
setSession(serviceToken); setSession(serviceToken);
// const response = await axios.get('/api/account/me');
// const { user } = response.data; // Set the token in your Axios instance for future requests
axiosServices.defaults.headers.common['Authorization'] = `Bearer ${serviceToken}`;
// Make the API request
const response = await axiosServices.get('/rest_node/user/current-user');
const { user } = response.data;
dispatch({ dispatch({
type: LOGIN, type: LOGIN,
payload: { payload: {
isLoggedIn: true, isLoggedIn: true,
// user user: {
id: user._id,
email: user.email,
name: `${user.firstName} ${user.lastName}`,
role: user.type
}
} }
}); });
} else { } else {
......
import { tutorialType } from "types/tutorial"; import { Tutorial, tutorialType } from "types/tutorial";
export const tutorials: tutorialType[] = [ export const tutorials: tutorialType[] = [
{ {
...@@ -180,4 +180,26 @@ export const tutorials: tutorialType[] = [ ...@@ -180,4 +180,26 @@ export const tutorials: tutorialType[] = [
"createdAt": new Date("2023-08-30T12:00:00Z"), "createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0, "tutorialMark": 0,
} }
] ]
\ No newline at end of file
export const tutorialReqOb: Tutorial = {
"tutorialCode": "01-SAMPLE",
"tutorialTitle": "Numbers and Counting in Sign Language",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"taskItems": [
{
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "",
"taskItemMark": 10
}
],
"createdBy": "Nuwan Gamage"
}
\ No newline at end of file
...@@ -19,7 +19,7 @@ export const userProgress: userProgressType = { ...@@ -19,7 +19,7 @@ export const userProgress: userProgressType = {
"tutorialTitle": "Numbers and Counting", "tutorialTitle": "Numbers and Counting",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.", "tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf", "tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"tutorialMark": 100, "tutorialMarks": 100,
"tutorialMarkUser": 24, "tutorialMarkUser": 24,
"tutorialSpentTime": 15, "tutorialSpentTime": 15,
"taskItems": [ "taskItems": [
...@@ -170,7 +170,7 @@ export const userProgress: userProgressType = { ...@@ -170,7 +170,7 @@ export const userProgress: userProgressType = {
"tutorialTitle": "Everyday Vocabulary", "tutorialTitle": "Everyday Vocabulary",
"tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.", "tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy", "tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy",
"tutorialMark": 0, "tutorialMarks": 0,
"tutorialMarkUser": 0, "tutorialMarkUser": 0,
"tutorialSpentTime": 0, "tutorialSpentTime": 0,
"taskItems": [] "taskItems": []
...@@ -180,7 +180,7 @@ export const userProgress: userProgressType = { ...@@ -180,7 +180,7 @@ export const userProgress: userProgressType = {
"tutorialTitle": "Family Signs", "tutorialTitle": "Family Signs",
"tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.", "tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg", "tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg",
"tutorialMark": 0, "tutorialMarks": 0,
"tutorialMarkUser": 0, "tutorialMarkUser": 0,
"tutorialSpentTime": 0, "tutorialSpentTime": 0,
"taskItems": [] "taskItems": []
...@@ -190,7 +190,7 @@ export const userProgress: userProgressType = { ...@@ -190,7 +190,7 @@ export const userProgress: userProgressType = {
"tutorialTitle": "Basic Conversational Phrases", "tutorialTitle": "Basic Conversational Phrases",
"tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.", "tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE", "tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE",
"tutorialMark": 0, "tutorialMarks": 0,
"tutorialMarkUser": 0, "tutorialMarkUser": 0,
"tutorialSpentTime": 0, "tutorialSpentTime": 0,
"taskItems": [] "taskItems": []
......
...@@ -157,7 +157,7 @@ const application: NavItemType = { ...@@ -157,7 +157,7 @@ const application: NavItemType = {
id: 'learning-curriculums-subscribed-tutorial', id: 'learning-curriculums-subscribed-tutorial',
title: <FormattedMessage id="learning-curriculums-subscribed-tutorial" />, title: <FormattedMessage id="learning-curriculums-subscribed-tutorial" />,
type: 'item', type: 'item',
url: '/learning-management/curriculums-subscribed-tutorial', url: '/learning-management/curriculums-subscribed-tutorial/0/0',
}, },
{ {
id: 'learning-lead-board', id: 'learning-lead-board',
......
// material-ui // material-ui
// project import // project import
// ==============================|| Dashboard ||============================== // // ==============================|| Dashboard ||============================== //
const Dashboard = () => ( const Dashboard = () => {
<>
</> return (
); <>
</>
);
}
export default Dashboard; export default Dashboard;
import { useState } from 'react';
// material-ui // material-ui
import { Grid } from '@mui/material'; import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
import { Card, CardContent, CardHeader, Collapse, Grid, IconButton, Typography } from '@mui/material';
// third-party // third-party
...@@ -15,11 +17,67 @@ import WelcomeBanner from 'sections/learning-management/WelcomeBanner'; ...@@ -15,11 +17,67 @@ import WelcomeBanner from 'sections/learning-management/WelcomeBanner';
// ==============================|| Dashboard ||============================== // // ==============================|| Dashboard ||============================== //
const Dashboard = () => { const Dashboard = () => {
const [expanded, setExpanded] = useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};
return ( return (
<Grid container rowSpacing={4.5} columnSpacing={3}> <Grid container rowSpacing={2} columnSpacing={3}>
<Grid item xs={12}> <Grid item xs={12}>
<WelcomeBanner /> <WelcomeBanner />
</Grid> </Grid>
<Grid md={12} item>
<Card>
<CardHeader
title="Learn Sign Language Curriculums"
subheader="Building a Solid Foundation, Enhancing Skills, and Mastering Sign Language"
action={
<IconButton
aria-expanded={expanded}
aria-label="show more"
onClick={handleExpandClick}
>
{expanded ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
</IconButton>
}
/>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent>
<Typography variant="body1">
Explore the world of sign language with our comprehensive Curriculum offerings. We provide three levels of sign language Curriculums designed to cater to learners of varying proficiency levels.
</Typography>
<Typography variant="subtitle1" sx={{ mt: 3 }}>
1. Base Level - Learn Sign Language:
</Typography>
<Typography variant="body2" sx={{ mt: 3 }}>
Start your sign language journey with our Base Level Curriculum. Perfect for beginners, this Curriculum offers essential sign language skills, including basic vocabulary, grammar, and sentence structures. Through interactive lessons and hands-on practice, you'll learn to express yourself through gestures and facial expressions, enabling effective communication with the deaf and hard of hearing community. Join us today and begin breaking down communication barriers.
</Typography>
<Typography variant="subtitle1" sx={{ mt: 3 }}>
2. Medium Level - Enhancing Sign Language Skills:
</Typography>
<Typography variant="body2" sx={{ mt: 3 }}>
Take your sign language skills to the next level with our Medium Level Curriculum. Designed for learners with some basic knowledge of sign language, this Curriculum focuses on expanding vocabulary, improving grammar usage, and honing expressive abilities. Engage in challenging yet rewarding lessons, interactive exercises, role-playing scenarios, and conversations to strengthen fluency. By delving deeper into sign language communication, including idiomatic expressions and storytelling techniques, you'll be able to connect with the deaf community on a deeper level.
</Typography>
<Typography variant="subtitle1" sx={{ mt: 3 }}>
3. Advanced Level - Mastering Sign Language:
</Typography>
<Typography variant="body2" sx={{ mt: 3 }}>
Become a proficient sign language user with our Advanced Level Curriculum. This comprehensive Curriculum is designed for experienced sign language learners seeking mastery. Dive into advanced topics such as complex grammar structures, specialized vocabulary, and cultural nuances. Through immersive activities, real-life simulations, and in-depth discussions, you'll refine your receptive and expressive skills. Explore various sign language modalities and gain a deep understanding of different signing systems. With personalized feedback and guidance from expert instructors, our Advanced Level Curriculum equips you to engage confidently in diverse sign language contexts.
</Typography>
<Typography variant="subtitle2" sx={{ mt: 3 }}>
Whether you're starting from scratch or aiming to enhance your existing sign language skills, our Curriculums offer a supportive learning environment and the opportunity to build connections with the deaf community. Join us today and embark on your sign language journey!
</Typography>
</CardContent>
</Collapse>
</Card>
</Grid>
</Grid> </Grid>
) )
} }
......
...@@ -26,9 +26,12 @@ import { GlobalFilter } from 'utils/react-table'; ...@@ -26,9 +26,12 @@ import { GlobalFilter } from 'utils/react-table';
// assets // assets
import { BookOutlined } from '@ant-design/icons'; import { BookOutlined } from '@ant-design/icons';
import { userProgress } from 'data/userProgress'; import useAuth from 'hooks/useAuth';
import CurriculumSection from 'sections/learning-management/learning-curriculums-subscribed/CurriculumSection'; import CurriculumSection from 'sections/learning-management/learning-curriculums-subscribed/CurriculumSection';
import EmptyCurriculumCard from 'sections/learning-management/learning-curriculums-subscribed/skeleton/EmptyCurriculumCard'; import EmptyCurriculumCard from 'sections/learning-management/learning-curriculums-subscribed/skeleton/EmptyCurriculumCard';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { fetchUserProgress, toInitialState } from 'store/reducers/userProgress';
import { curriculumTypeUserProgress, userProgressType } from 'types/userProgress'; import { curriculumTypeUserProgress, userProgressType } from 'types/userProgress';
// ==============================|| List ||============================== // // ==============================|| List ||============================== //
...@@ -46,6 +49,9 @@ const allColumns = [ ...@@ -46,6 +49,9 @@ const allColumns = [
const List = () => { const List = () => {
const theme = useTheme(); const theme = useTheme();
const dispatch = useDispatch();
const { userProgress, error, success, isLoading } = useSelector(state => state.userProgress);
const { user } = useAuth();
const [data, setData] = useState<userProgressType["curriculums"]>([]) const [data, setData] = useState<userProgressType["curriculums"]>([])
...@@ -61,7 +67,9 @@ const List = () => { ...@@ -61,7 +67,9 @@ const List = () => {
}; };
// search // search
useEffect(() => { useEffect(() => {
if (!data || data.length == 0) return
const newData = data?.filter((value: any) => { const newData = data?.filter((value: any) => {
if (globalFilter) { if (globalFilter) {
return value.curriculumTitle.toLowerCase().includes(globalFilter.toLowerCase()); return value.curriculumTitle.toLowerCase().includes(globalFilter.toLowerCase());
...@@ -83,16 +91,96 @@ const List = () => { ...@@ -83,16 +91,96 @@ const List = () => {
_DATA.jump(p); _DATA.jump(p);
}; };
useEffect(() => {
setData(userProgress.curriculums)
}, [])
const [expanded, setExpanded] = useState<string | false>('panel0'); const [expanded, setExpanded] = useState<string | false>('panel0');
const handleAccordionChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { const handleAccordionChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false); setExpanded(newExpanded ? panel : false);
}; };
/**
* API Config
* User Progress API
*/
useEffect(() => {
if (!user) {
// User is missing
dispatch(
openSnackbar({
open: true,
message: 'User data is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else if (!user.id) {
// User ID is missing
dispatch(
openSnackbar({
open: true,
message: 'User ID is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else {
dispatch(fetchUserProgress(user.id));
}
}, [dispatch]);
useEffect(() => {
if (userProgress) {
setData(userProgress?.curriculums);
}
}, [userProgress])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return ( return (
<> <>
<Box sx={{ position: 'relative', marginBottom: 3 }}> <Box sx={{ position: 'relative', marginBottom: 3 }}>
...@@ -173,7 +261,7 @@ const List = () => { ...@@ -173,7 +261,7 @@ const List = () => {
</Stack> </Stack>
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails>
<CurriculumSection curriculum={curriculum} /> <CurriculumSection curriculum={curriculum} curriculumIndex={index} />
</AccordionDetails> </AccordionDetails>
</Accordion> </Accordion>
</Box> </Box>
......
...@@ -11,4 +11,9 @@ export interface selectedCommonDataProps { ...@@ -11,4 +11,9 @@ export interface selectedCommonDataProps {
curriculumCode: string curriculumCode: string
tutorialCode: string tutorialCode: string
title: string title: string
}
export interface itemResultProps {
itemMarkUser: number
status: string
} }
\ No newline at end of file
...@@ -21,13 +21,15 @@ import { ...@@ -21,13 +21,15 @@ import {
import usePagination from 'hooks/usePagination'; import usePagination from 'hooks/usePagination';
import CurriculumCard from 'sections/learning-management/learning-curriculums/CurriculumCard'; import CurriculumCard from 'sections/learning-management/learning-curriculums/CurriculumCard';
import EmptyCurriculumCard from 'sections/learning-management/learning-curriculums/skeleton/EmptyCurriculumCard'; import EmptyCurriculumCard from 'sections/learning-management/learning-curriculums/skeleton/EmptyCurriculumCard';
import { useDispatch, useSelector } from 'store';
import { fetchCurriculums, toInitialState } from 'store/reducers/curriculum';
import { openSnackbar } from 'store/reducers/snackbar';
import { curriculumType } from 'types/curriculum'; import { curriculumType } from 'types/curriculum';
import { GlobalFilter } from 'utils/react-table'; import { GlobalFilter } from 'utils/react-table';
// types // types
// assets // assets
import { curriculums } from 'data/curriculums';
// ==============================|| List ||============================== // // ==============================|| List ||============================== //
...@@ -43,6 +45,10 @@ const allColumns = [ ...@@ -43,6 +45,10 @@ const allColumns = [
]; ];
const List = () => { const List = () => {
const dispatch = useDispatch();
const { curriculums, error, success, isLoading } = useSelector(state => state.curriculum);
const { success: userProgressSuccess } = useSelector(state => state.userProgress);
const [data, setData] = useState<curriculumType[]>([]) const [data, setData] = useState<curriculumType[]>([])
const matchDownSM = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm')); const matchDownSM = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
...@@ -79,9 +85,59 @@ const List = () => { ...@@ -79,9 +85,59 @@ const List = () => {
_DATA.jump(p); _DATA.jump(p);
}; };
/**
* API Config
* Curriculum API
*/
useEffect(() => {
dispatch(fetchCurriculums());
}, [dispatch, userProgressSuccess]);
useEffect(() => { useEffect(() => {
setData(curriculums) setData(curriculums);
}, []) }, [curriculums])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return ( return (
<> <>
......
import { MouseEvent, useMemo } from 'react'; import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui // material-ui
import { import {
...@@ -31,7 +31,12 @@ import { ...@@ -31,7 +31,12 @@ import {
import { DeleteTwoTone, EditTwoTone } from '@ant-design/icons'; import { DeleteTwoTone, EditTwoTone } from '@ant-design/icons';
//types //types
import { ReactTableProps, dataProps } from './types/types'; import status from 'data/status';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { fetchUsers, toInitialState } from 'store/reducers/user';
import { Users } from 'types/user';
import { ReactTableProps } from './types/types';
// ==============================|| REACT TABLE ||============================== // // ==============================|| REACT TABLE ||============================== //
...@@ -101,10 +106,10 @@ function ReactTable({ columns, data }: ReactTableProps) { ...@@ -101,10 +106,10 @@ function ReactTable({ columns, data }: ReactTableProps) {
); );
}) })
) : ( ) : (
<EmptyTable msg="No Data" colSpan={5} /> <EmptyTable msg="No Data" colSpan={12} />
)} )}
<TableRow> <TableRow>
<TableCell sx={{ p: 2 }} colSpan={5}> <TableCell sx={{ p: 2 }} colSpan={12}>
<TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} /> <TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} />
</TableCell> </TableCell>
</TableRow> </TableRow>
...@@ -119,18 +124,22 @@ function ReactTable({ columns, data }: ReactTableProps) { ...@@ -119,18 +124,22 @@ function ReactTable({ columns, data }: ReactTableProps) {
const List = () => { const List = () => {
const theme = useTheme(); const theme = useTheme();
const dispatch = useDispatch();
const { users, error, success, isLoading } = useSelector(state => state.user);
// table // table
const data: dataProps[] = [ // const data: dataProps[] = [
{ // {
id: 1, // id: 1,
userName: "Nuwan Gamage", // userName: "Nuwan Gamage",
userContactNumber: "0768523456", // userContactNumber: "0768523456",
userEmailAddress: "nuwangamage@gmail.com", // userEmailAddress: "nuwangamage@gmail.com",
userType: "Member", // userType: "Member",
userStatus: "Active" // userStatus: "Active"
} // }
] // ]
const [data, setData] = useState<Users[]>([])
const columns = useMemo( const columns = useMemo(
() => () =>
...@@ -155,33 +164,69 @@ const List = () => { ...@@ -155,33 +164,69 @@ const List = () => {
}, },
{ {
Header: 'User Name', Header: 'User Name',
accessor: 'userName', accessor: 'firstName',
className: 'cell-center', Cell: ({ row }: { row: Row }) => {
if (row.values.firstName === undefined || row.values.firstName === null || row.values.firstName === '') {
return <>-</>
}
if (typeof row.values.firstName === 'string') {
//@ts-ignore
return <>{row.values.firstName + " " + row.original.lastName}</>;
}
if (typeof row.values.firstName === 'number') {
return <>{row.values.firstName}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
}, },
{ {
Header: 'User Email', Header: 'User Email',
accessor: 'userEmailAddress', accessor: 'email',
className: 'cell-center',
}, },
{ {
Header: 'User Contact Number', Header: 'User Contact Number',
accessor: 'userContactNumber', accessor: 'contactNumber',
className: 'cell-center',
}, },
{ {
Header: 'User Type', Header: 'User Type',
accessor: 'userType', accessor: 'type',
className: 'cell-center', Cell: ({ row }: { row: Row }) => {
if (row.values.type === undefined || row.values.type === null || row.values.type === '') {
return <>-</>
}
if (typeof row.values.type === 'string') {
return <>{row.values.type.toUpperCase()}</>;
}
if (typeof row.values.type === 'number') {
return <>{row.values.type}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
}, },
{ {
Header: 'User Status', Header: 'User Status',
accessor: 'userStatus', accessor: 'states',
className: 'cell-center', className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.values.states === undefined || row.values.states === null || row.values.states === '') {
return <>-</>
}
if (typeof row.values.states === 'string') {
return <>{status.find(status => status.id === parseInt(row.values.states))?.description || "-"}</>;
}
if (typeof row.values.states === 'number') {
return <>{status.find(status => status.id === row.values.states)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
}, },
{ {
id: "actions", id: "actions",
Header: 'Actions', Header: 'Actions',
accessor: 'actions', accessor: 'actions',
Cell: ({ row }: { row: Row }) => { Cell: ({ row }: { row: Row }) => {
return ( return (
<> <>
...@@ -225,6 +270,59 @@ const List = () => { ...@@ -225,6 +270,59 @@ const List = () => {
[] []
); );
/**
* API Config
* User API
*/
useEffect(() => {
dispatch(fetchUsers('member'));
}, [dispatch]);
useEffect(() => {
setData(users);
}, [users])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return ( return (
<> <>
<MainCard content={false}> <MainCard content={false}>
......
import { Column } from 'react-table'; import { Column } from 'react-table';
import { Users } from 'types/user';
export interface dataProps { export interface dataProps {
id: number | string | undefined id: number | string | undefined
...@@ -11,7 +12,7 @@ export interface dataProps { ...@@ -11,7 +12,7 @@ export interface dataProps {
export interface ReactTableProps { export interface ReactTableProps {
columns: Column[] columns: Column[]
data: dataProps[] data: Users[]
} }
export interface userProps { export interface userProps {
......
import { MouseEvent, useMemo, useState } from 'react'; import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui // material-ui
import { import {
...@@ -34,9 +34,15 @@ import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-desig ...@@ -34,9 +34,15 @@ import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-desig
//types //types
import { PopupTransition } from 'components/@extended/Transitions'; import { PopupTransition } from 'components/@extended/Transitions';
import curriculumLevels from 'data/curriculumLevels';
import status from 'data/status';
import AddEditCurriculum from 'sections/parameters/curriculum-management/AddEditCurriculum'; import AddEditCurriculum from 'sections/parameters/curriculum-management/AddEditCurriculum';
import AlertCurriculumDelete from 'sections/parameters/curriculum-management/AlertCurriculumDelete'; import AlertCurriculumDelete from 'sections/parameters/curriculum-management/AlertCurriculumDelete';
import { ReactTableProps, curriculumProps, dataProps } from './types/types'; import { useDispatch, useSelector } from 'store';
import { fetchCurriculums, toInitialState } from 'store/reducers/curriculum';
import { openSnackbar } from 'store/reducers/snackbar';
import { Curriculum, Curriculums } from 'types/curriculum';
import { ReactTableProps } from './types/types';
// ==============================|| REACT TABLE ||============================== // // ==============================|| REACT TABLE ||============================== //
...@@ -122,16 +128,21 @@ function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) { ...@@ -122,16 +128,21 @@ function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
// ==============================|| List ||============================== // // ==============================|| List ||============================== //
const List = () => { const List = () => {
const theme = useTheme(); const theme = useTheme();
const dispatch = useDispatch();
const { curriculums, error, success, isLoading } = useSelector(state => state.curriculum);
// table // table
const data: dataProps[] = [] // const data: dataProps[] = []
// table
const [data, setData] = useState<Curriculums[]>([])
const columns = useMemo( const columns = useMemo(
() => () =>
[ [
{ {
Header: '#', Header: '#',
accessor: 'id', accessor: '_id',
className: 'cell-center', className: 'cell-center',
Cell: ({ row }: { row: Row }) => { Cell: ({ row }: { row: Row }) => {
if (row.id === undefined || row.id === null || row.id === '') { if (row.id === undefined || row.id === null || row.id === '') {
...@@ -150,23 +161,76 @@ const List = () => { ...@@ -150,23 +161,76 @@ const List = () => {
{ {
Header: 'Curriculum Code', Header: 'Curriculum Code',
accessor: 'curriculumCode' accessor: 'curriculumCode'
}, },
{ {
Header: 'Curriculum LVL', Header: 'Curriculum LVL',
accessor: 'curriculumLevel' accessor: 'curriculumLevel',
}, Cell: ({ row }: { row: Row }) => {
if (row.values.curriculumLevel === undefined || row.values.curriculumLevel === null || row.values.curriculumLevel === '') {
return <>-</>
}
if (typeof row.values.curriculumLevel === 'string') {
return <>{row.values.curriculumLevel}</>;
}
if (typeof row.values.curriculumLevel === 'number') {
return <>{curriculumLevels.find(curriculumLevel => curriculumLevel.id === row.values.curriculumLevel)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{ {
Header: 'Curriculum Name', Header: 'Curriculum Name',
accessor: 'curriculumTitle' accessor: 'curriculumTitle',
}, Cell: ({ row }: { row: Row }) => {
if (row.values.curriculumTitle === undefined || row.values.curriculumTitle === null || row.values.curriculumTitle === '') {
return <>-</>
}
if (typeof row.values.curriculumTitle === 'string') {
return <>{row.values.curriculumTitle}</>;
}
if (typeof row.values.curriculumTitle === 'number') {
return <>{row.values.curriculumTitle || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{ {
Header: 'Status', Header: 'Status',
accessor: 'status' accessor: 'status',
}, className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.values.status === undefined || row.values.status === null || row.values.status === '') {
return <>-</>
}
if (typeof row.values.status === 'string') {
return <>{row.values.status}</>;
}
if (typeof row.values.status === 'number') {
return <>{status.find(status => status.id === row.values.status)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{ {
Header: 'Created By', Header: 'Created By',
accessor: 'createdBy' accessor: 'createdBy',
}, Cell: ({ row }: { row: Row }) => {
if (row.values.createdBy === undefined || row.values.createdBy === null || row.values.createdBy === '') {
return <>-</>
}
if (typeof row.values.createdBy === 'string') {
return <>{row.values.createdBy}</>;
}
if (typeof row.values.createdBy === 'number') {
return <>{row.values.createdBy}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{ {
id: "actions", id: "actions",
Header: 'Actions', Header: 'Actions',
...@@ -209,6 +273,7 @@ const List = () => { ...@@ -209,6 +273,7 @@ const List = () => {
onClick={(e: MouseEvent<HTMLButtonElement>) => { onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation(); e.stopPropagation();
setCurriculumId(row.values._id) setCurriculumId(row.values._id)
setCurriculumTitle(row.values.curriculumTitle)
setOpenAlert(true) setOpenAlert(true)
}} }}
> >
...@@ -226,7 +291,7 @@ const List = () => { ...@@ -226,7 +291,7 @@ const List = () => {
//dialog model //dialog model
const [addEdit, setAddEdit] = useState<boolean>(false); const [addEdit, setAddEdit] = useState<boolean>(false);
const [curriculum, setCurriculum] = useState<curriculumProps>(); const [curriculum, setCurriculum] = useState<Curriculum>();
const handleAddEdit = () => { const handleAddEdit = () => {
setAddEdit(!addEdit); setAddEdit(!addEdit);
...@@ -235,12 +300,66 @@ const List = () => { ...@@ -235,12 +300,66 @@ const List = () => {
//alert model //alert model
const [openAlert, setOpenAlert] = useState(false); const [openAlert, setOpenAlert] = useState(false);
const [curriculumId, setCurriculumId] = useState<number | string | undefined>(undefined) const [curriculumId, setCurriculumId] = useState<string | undefined>(undefined)
const [curriculumTitle, setCurriculumTitle] = useState<string | undefined>(undefined)
const handleAlertClose = () => { const handleAlertClose = () => {
setOpenAlert(!openAlert); setOpenAlert(!openAlert);
}; };
/**
* API Config
* Curriculum API
*/
useEffect(() => {
dispatch(fetchCurriculums());
}, [dispatch]);
useEffect(() => {
setData(curriculums);
}, [curriculums])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return ( return (
<> <>
<MainCard content={false}> <MainCard content={false}>
...@@ -258,10 +377,10 @@ const List = () => { ...@@ -258,10 +377,10 @@ const List = () => {
sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }} sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }}
aria-describedby="alert-dialog-slide-description" aria-describedby="alert-dialog-slide-description"
> >
<AddEditCurriculum curriculum={curriculum} onCancel={handleAddEdit} /> <AddEditCurriculum curriculum={curriculum!} onCancel={handleAddEdit} />
</Dialog> </Dialog>
{/* alert model */} {/* alert model */}
{!curriculum && <AlertCurriculumDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={curriculumId} />} {!curriculum && <AlertCurriculumDelete title={curriculumTitle!} open={openAlert} handleClose={handleAlertClose} deleteId={curriculumId} />}
</MainCard> </MainCard>
</> </>
) )
......
import { Column } from 'react-table'; import { Column } from 'react-table';
import { Curriculums } from 'types/curriculum';
export interface dataProps { export interface dataProps {
_id: number | string | undefined; _id: number | string | undefined;
...@@ -17,7 +18,7 @@ export interface dataProps { ...@@ -17,7 +18,7 @@ export interface dataProps {
export interface ReactTableProps { export interface ReactTableProps {
columns: Column[] columns: Column[]
data: dataProps[] data: Curriculums[]
handleAddEdit: () => void handleAddEdit: () => void
} }
......
import { Column } from 'react-table'; import { Column } from 'react-table';
import { Tutorials } from 'types/tutorial';
export interface dataProps { export interface dataProps {
_id: number | string | undefined; _id: number | string | undefined;
...@@ -16,7 +17,7 @@ export interface dataProps { ...@@ -16,7 +17,7 @@ export interface dataProps {
export interface ReactTableProps { export interface ReactTableProps {
columns: Column[] columns: Column[]
data: dataProps[] data: Tutorials[]
handleAddEdit: () => void handleAddEdit: () => void
} }
......
import { MouseEvent, useMemo, useState } from 'react'; import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui // material-ui
import { import {
...@@ -32,11 +32,16 @@ import { ...@@ -32,11 +32,16 @@ import {
import { DeleteTwoTone, EditTwoTone, PlusOutlined } from '@ant-design/icons'; import { DeleteTwoTone, EditTwoTone, PlusOutlined } from '@ant-design/icons';
//types //types
import { ReactTableProps, dataProps, userProps } from './types/types'; import status from 'data/status';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { fetchUsersByType, toInitialState } from 'store/reducers/user';
import { Users } from 'types/user';
import { ReactTableProps, userProps } from './types/types';
// ==============================|| REACT TABLE ||============================== // // ==============================|| REACT TABLE ||============================== //
function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) { function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
const filterTypes = useMemo(() => renderFilterTypes, []); const filterTypes = useMemo(() => renderFilterTypes, []);
const defaultColumn = useMemo(() => ({ Filter: DefaultColumnFilter }), []); const defaultColumn = useMemo(() => ({ Filter: DefaultColumnFilter }), []);
...@@ -102,10 +107,10 @@ function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) { ...@@ -102,10 +107,10 @@ function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) {
); );
}) })
) : ( ) : (
<EmptyTable msg="No Data" colSpan={5} /> <EmptyTable msg="No Data" colSpan={12} />
)} )}
<TableRow> <TableRow>
<TableCell sx={{ p: 2 }} colSpan={5}> <TableCell sx={{ p: 2 }} colSpan={12}>
<TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} /> <TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} />
</TableCell> </TableCell>
</TableRow> </TableRow>
...@@ -120,26 +125,33 @@ function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) { ...@@ -120,26 +125,33 @@ function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) {
const List = () => { const List = () => {
const theme = useTheme(); const theme = useTheme();
const dispatch = useDispatch();
const { users, error, success, isLoading } = useSelector(state => state.user);
// table // table
const data: dataProps[] = [ // const data: dataProps[] = [
{ // {
id: 1, // id: 1,
userName: "Janith Gamage", // userName: "Janith Gamage",
userContactNumber: "0768523525", // userContactNumber: "0768523525",
userEmailAddress: "janithgamage1.ed@gmail.com", // userEmailAddress: "janithgamage1.ed@gmail.com",
userType: "Admin", // userType: "Admin",
userStatus: "Active" // userStatus: "Active"
}, // },
{ // {
id: 1, // id: 1,
userName: "Nuwan Gamage", // userName: "Nuwan Gamage",
userContactNumber: "0768523456", // userContactNumber: "0768523456",
userEmailAddress: "nuwangamage@gmail.com", // userEmailAddress: "nuwangamage@gmail.com",
userType: "Member", // userType: "Member",
userStatus: "Active" // userStatus: "Active"
} // }
] // ]
// table
const [data, setData] = useState<Users[]>([])
//dialog model //dialog model
const [addEdit, setAddEdit] = useState<boolean>(false); const [addEdit, setAddEdit] = useState<boolean>(false);
...@@ -173,28 +185,64 @@ const List = () => { ...@@ -173,28 +185,64 @@ const List = () => {
}, },
{ {
Header: 'User Name', Header: 'User Name',
accessor: 'userName', accessor: 'firstName',
className: 'cell-center', Cell: ({ row }: { row: Row }) => {
if (row.values.firstName === undefined || row.values.firstName === null || row.values.firstName === '') {
return <>-</>
}
if (typeof row.values.firstName === 'string') {
//@ts-ignore
return <>{row.values.firstName + " " + row.original.lastName}</>;
}
if (typeof row.values.firstName === 'number') {
return <>{row.values.firstName}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
}, },
{ {
Header: 'User Email', Header: 'User Email',
accessor: 'userEmailAddress', accessor: 'email',
className: 'cell-center',
}, },
{ {
Header: 'User Contact Number', Header: 'User Contact Number',
accessor: 'userContactNumber', accessor: 'contactNumber',
className: 'cell-center',
}, },
{ {
Header: 'User Type', Header: 'User Type',
accessor: 'userType', accessor: 'type',
className: 'cell-center', Cell: ({ row }: { row: Row }) => {
if (row.values.type === undefined || row.values.type === null || row.values.type === '') {
return <>-</>
}
if (typeof row.values.type === 'string') {
return <>{row.values.type.toUpperCase()}</>;
}
if (typeof row.values.type === 'number') {
return <>{row.values.type}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
}, },
{ {
Header: 'User Status', Header: 'User Status',
accessor: 'userStatus', accessor: 'states',
className: 'cell-center', className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.values.states === undefined || row.values.states === null || row.values.states === '') {
return <>-</>
}
if (typeof row.values.states === 'string') {
return <>{status.find(status => status.id === parseInt(row.values.states))?.description || "-"}</>;
}
if (typeof row.values.states === 'number') {
return <>{status.find(status => status.id === row.values.states)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
}, },
{ {
id: "actions", id: "actions",
...@@ -243,6 +291,59 @@ const List = () => { ...@@ -243,6 +291,59 @@ const List = () => {
[] []
); );
/**
* API Config
* User API
*/
useEffect(() => {
dispatch(fetchUsersByType());
}, [dispatch]);
useEffect(() => {
setData(users);
}, [users])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return ( return (
<> <>
<MainCard content={false}> <MainCard content={false}>
......
import { Column } from 'react-table'; import { Column } from 'react-table';
import { Users } from 'types/user';
export interface dataProps { export interface dataProps {
id: number | string | undefined id: number | string | undefined
...@@ -11,7 +12,7 @@ export interface dataProps { ...@@ -11,7 +12,7 @@ export interface dataProps {
export interface ReactTableProps { export interface ReactTableProps {
columns: Column[] columns: Column[]
data: dataProps[] data: Users[]
handleAddEdit: () => void handleAddEdit: () => void
} }
......
...@@ -37,8 +37,8 @@ const LearningDashboard = Loadable(lazy(() => import('pages/learning-management/ ...@@ -37,8 +37,8 @@ const LearningDashboard = Loadable(lazy(() => import('pages/learning-management/
const LearningCurriculums = Loadable(lazy(() => import('pages/learning-management/learning-curriculums/list/list'))); const LearningCurriculums = Loadable(lazy(() => import('pages/learning-management/learning-curriculums/list/list')));
const LearningCurriculumsSubscribed = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/list/list'))); const LearningCurriculumsSubscribed = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/list/list')));
const LearningCurriculumsSubscribedTutorial = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/tutorial/tutorial'))); const LearningCurriculumsSubscribedTutorial = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/tutorial/tutorial')));
const LearningLeadBoard = Loadable(lazy(() => import('pages/learning-management/learning-lead-board/list/list'))); // const LearningLeadBoard = Loadable(lazy(() => import('pages/learning-management/learning-lead-board/list/list')));
const LearningFeedBack = Loadable(lazy(() => import('pages/learning-management/learning-feedback/list/list'))); // const LearningFeedBack = Loadable(lazy(() => import('pages/learning-management/learning-feedback/list/list')));
// render - parameter curriculum management page // render - parameter curriculum management page
const CurriculumManagementList = Loadable(lazy(() => import('pages/parameter/curriculum-management/list/list'))); const CurriculumManagementList = Loadable(lazy(() => import('pages/parameter/curriculum-management/list/list')));
...@@ -148,15 +148,25 @@ const MainRoutes = { ...@@ -148,15 +148,25 @@ const MainRoutes = {
}, },
{ {
path: 'curriculums-subscribed-tutorial', path: 'curriculums-subscribed-tutorial',
element: <LearningCurriculumsSubscribedTutorial /> children: [
{
path: ':curriculumIndex', // Parameter for curriculum index
children: [
{
path: ':tutorialIndex', // Parameter for tutorial index
element: <LearningCurriculumsSubscribedTutorial />
}
]
}
]
}, },
{ {
path: 'lead-board', path: 'lead-board',
element: <LearningLeadBoard /> element: <MaintenanceUnderConstruction />
}, },
{ {
path: 'feedback', path: 'feedback',
element: <LearningFeedBack /> element: <MaintenanceUnderConstruction />
} }
] ]
}, },
......
...@@ -25,7 +25,7 @@ import ReportCard from 'components/cards/statistics/ReportCard'; ...@@ -25,7 +25,7 @@ import ReportCard from 'components/cards/statistics/ReportCard';
// ==============================|| Curriculum - Section ||============================== // // ==============================|| Curriculum - Section ||============================== //
const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgress }) => { const CurriculumSection = ({ curriculum, curriculumIndex }: { curriculum: curriculumTypeUserProgress, curriculumIndex: number }) => {
const theme = useTheme(); const theme = useTheme();
return ( return (
...@@ -53,10 +53,10 @@ const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgr ...@@ -53,10 +53,10 @@ const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgr
Your learning capacity is 80% as daily analytics Your learning capacity is 80% as daily analytics
</Typography> </Typography>
<Typography variant="h4" color="white" sx={{ pt: 8, pb: 1, zIndex: 1 }}> <Typography variant="h4" color="white" sx={{ pt: 8, pb: 1, zIndex: 1 }}>
{(curriculum.curriculumMarkUser / curriculum.curriculumMark) * 100}% Completed {(curriculum?.curriculumMarkUser! / curriculum?.curriculumMark!) * 100}% Completed
</Typography> </Typography>
<Box sx={{ maxWidth: '60%' }}> <Box sx={{ maxWidth: '60%' }}>
<LinearProgress variant="determinate" color="success" value={(curriculum.curriculumMarkUser / curriculum.curriculumMark) * 100} /> <LinearProgress variant="determinate" color="success" value={(curriculum?.curriculumMarkUser! / curriculum?.curriculumMark!) * 100} />
</Box> </Box>
<Box <Box
sx={{ sx={{
...@@ -84,8 +84,8 @@ const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgr ...@@ -84,8 +84,8 @@ const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgr
</MainCard> </MainCard>
<MainCard title="Tutorials"> <MainCard title="Tutorials">
<Grid container spacing={2}> <Grid container spacing={2}>
{curriculum.tutorials.map((tutorial, index) => { {curriculum.tutorials && curriculum.tutorials?.map((tutorial, index) => {
return (<TutorialSection tutorial={tutorial!} />) return (<TutorialSection tutorial={tutorial!} curriculumIndex={curriculumIndex} tutorialIndex={index} />)
})} })}
</Grid> </Grid>
</MainCard> </MainCard>
......
...@@ -25,7 +25,7 @@ import MainCard from 'components/MainCard'; ...@@ -25,7 +25,7 @@ import MainCard from 'components/MainCard';
// ==============================|| Tutorial - Section ||============================== // // ==============================|| Tutorial - Section ||============================== //
const TutorialSection = ({ tutorial }: { tutorial: tutorialTypeUserProgress }) => { const TutorialSection = ({ tutorial, tutorialIndex, curriculumIndex }: { tutorial: tutorialTypeUserProgress, curriculumIndex: number, tutorialIndex: number }) => {
let navigation = useNavigate() let navigation = useNavigate()
const [desc, setDesc] = useState(tutorial.tutorialDescription?.slice(0, 100)) const [desc, setDesc] = useState(tutorial.tutorialDescription?.slice(0, 100))
...@@ -70,7 +70,7 @@ const TutorialSection = ({ tutorial }: { tutorial: tutorialTypeUserProgress }) = ...@@ -70,7 +70,7 @@ const TutorialSection = ({ tutorial }: { tutorial: tutorialTypeUserProgress }) =
variant="outlined" variant="outlined"
endIcon={<PlaySquareOutlined />} endIcon={<PlaySquareOutlined />}
sx={{ my: 2 }} sx={{ my: 2 }}
onClick={() => { navigation(`/learning-management/curriculums-subscribed-tutorial`) }} onClick={() => { navigation(`/learning-management/curriculums-subscribed-tutorial/${curriculumIndex}/${tutorialIndex}`) }}
> >
Start Tutorial Start Tutorial
</Button> </Button>
......
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router';
// material-ui // material-ui
import { import {
Box, Box,
Button, Button,
CircularProgress,
Grid, Grid,
Typography Typography
} from '@mui/material'; } from '@mui/material';
...@@ -15,8 +15,12 @@ import { ...@@ -15,8 +15,12 @@ import {
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
// assets // assets
import { PlusOutlined, SendOutlined } from '@ant-design/icons'; import { MinusOutlined, PlusOutlined, SendOutlined } from '@ant-design/icons';
import AnimateButton from 'components/@extended/AnimateButton'; import AnimateButton from 'components/@extended/AnimateButton';
import useAuth from 'hooks/useAuth';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { addUserProgress, toInitialState } from 'store/reducers/userProgress';
import { curriculumType } from 'types/curriculum'; import { curriculumType } from 'types/curriculum';
import Animation from './Animation'; import Animation from './Animation';
import CurriculumPreview from './CurriculumPreview'; import CurriculumPreview from './CurriculumPreview';
...@@ -26,7 +30,9 @@ import CurriculumPreview from './CurriculumPreview'; ...@@ -26,7 +30,9 @@ import CurriculumPreview from './CurriculumPreview';
// ==============================|| CURRICULUM - CARD ||============================== // // ==============================|| CURRICULUM - CARD ||============================== //
const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => { const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => {
const navigate = useNavigate() let dispatch = useDispatch()
const { error, success, isLoading } = useSelector(state => state.userProgress);
const { user } = useAuth();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
...@@ -40,6 +46,109 @@ const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => { ...@@ -40,6 +46,109 @@ const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => {
const [desc, setDesc] = useState(curriculum.curriculumDescription?.slice(0, 100)) const [desc, setDesc] = useState(curriculum.curriculumDescription?.slice(0, 100))
const [readMore, setReadMore] = useState(false) const [readMore, setReadMore] = useState(false)
const [isSubscribed, setIsSubscribed] = useState(false)
/**
* API Config
* User Progress API
*/
const FollowCurriculum = () => {
if (!user) {
// User is missing
dispatch(
openSnackbar({
open: true,
message: 'User data is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else if (!user.id) {
// User ID is missing
dispatch(
openSnackbar({
open: true,
message: 'User ID is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else if (!curriculum) {
// Curriculum data is missing
dispatch(
openSnackbar({
open: true,
message: 'Curriculum data is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else {
// All required data is present, dispatch addUserProgress
dispatch(addUserProgress(user.id, curriculum));
}
}
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
useEffect(() => {
if (curriculum.subscribedUser) {
const userId = user?.id; // Assuming user.id is how you get the user's ID
const isUserSubscribed = curriculum.subscribedUser.includes(userId!);
if (isUserSubscribed) {
setIsSubscribed(true)
}
}
}, [curriculum, user]);
return ( return (
<> <>
...@@ -74,26 +183,33 @@ const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => { ...@@ -74,26 +183,33 @@ const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => {
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>
<Box sx={{ display: 'inline-block' }}> <Box sx={{ display: 'inline-block', width: "100%" }}>
<AnimateButton> <AnimateButton>
<Button <Button
fullWidth fullWidth
size='small'
variant="outlined" variant="outlined"
endIcon={<PlusOutlined />} endIcon={isSubscribed ? <MinusOutlined /> : <PlusOutlined />}
sx={{ my: 2, width: "100%" }} sx={{ my: 2, width: "100%" }}
onClick={() => { navigate(`/learning-management/curriculums-subscribed`) }} onClick={() => {
color='success' if (!isSubscribed) {
FollowCurriculum()
}
}}
color={isSubscribed ? 'secondary' : 'success'}
disabled={isLoading}
> >
Follow Curriculum {isLoading ? <CircularProgress /> : <>{isSubscribed ? 'Un Follow Curriculum' : 'Follow Curriculum'} </>}
</Button> </Button>
</AnimateButton> </AnimateButton>
</Box> </Box>
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>
<Box sx={{ display: 'inline-block' }}> <Box sx={{ display: 'inline-block', width: "100%" }}>
<AnimateButton> <AnimateButton>
<Button <Button
fullWidth fullWidth
size='small'
variant="outlined" variant="outlined"
endIcon={<SendOutlined />} endIcon={<SendOutlined />}
sx={{ my: 2, width: "100%" }} sx={{ my: 2, width: "100%" }}
......
import { useState } from 'react'; import { useEffect, useState } from 'react';
// material-ui // material-ui
import { import {
...@@ -33,6 +33,12 @@ import IconButton from 'components/@extended/IconButton'; ...@@ -33,6 +33,12 @@ import IconButton from 'components/@extended/IconButton';
import { DeleteFilled } from '@ant-design/icons'; import { DeleteFilled } from '@ant-design/icons';
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
import curriculumLevels, { CurriculumLevelsType } from 'data/curriculumLevels'; import curriculumLevels, { CurriculumLevelsType } from 'data/curriculumLevels';
import useAuth from 'hooks/useAuth';
import { dispatch, useSelector } from 'store';
import { addCurriculum } from 'store/reducers/curriculum';
import { fetchTutorials } from 'store/reducers/tutorial';
import { Curriculum } from 'types/curriculum';
import { Tutorial } from 'types/tutorial';
import AlertCurriculumDelete from './AlertCurriculumDelete'; import AlertCurriculumDelete from './AlertCurriculumDelete';
// types // types
...@@ -60,25 +66,13 @@ const getInitialValues = (curriculum: FormikValues | null) => { ...@@ -60,25 +66,13 @@ const getInitialValues = (curriculum: FormikValues | null) => {
// ==============================|| CUSTOMER ADD / EDIT ||============================== // // ==============================|| CUSTOMER ADD / EDIT ||============================== //
export interface Props { export interface Props {
curriculum?: { curriculum: Curriculum;
_id: number | string | undefined;
curriculumCode: string;
curriculumLevel: number;
curriculumTitle: string;
curriculumDescription: string;
curriculumImage: string;
tutorials: string[];
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
};
onCancel: () => void; onCancel: () => void;
} }
const AddEditCurriculum = ({ curriculum, onCancel }: Props) => { const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
const theme = useTheme(); const theme = useTheme();
const { user } = useAuth();
const isCreating = !curriculum; const isCreating = !curriculum;
...@@ -95,12 +89,27 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => { ...@@ -95,12 +89,27 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
initialValues: getInitialValues(curriculum!), initialValues: getInitialValues(curriculum!),
validationSchema: CurriculumSchema, validationSchema: CurriculumSchema,
enableReinitialize: true, enableReinitialize: true,
onSubmit: (values, { setSubmitting, resetForm }) => { onSubmit: (values, { setSubmitting, resetForm }) => {
try { try {
if (curriculum) { if (curriculum) {
// PUT API // PUT API
} else { } else {
// POST API // POST API
// const dummyObject: Curriculum = curriculumReqOb
// dispatch(addCurriculum(dummyObject))
const req: Curriculum = {
curriculumCode: values.curriculumCode,
curriculumLevel: values.curriculumLevel!,
curriculumTitle: values.curriculumTitle,
curriculumDescription: values.curriculumDescription,
curriculumImage: values.curriculumImage,
tutorials: values.tutorials!,
createdBy: user?.name!,
};
dispatch(addCurriculum(req))
} }
resetForm() resetForm()
setSubmitting(false); setSubmitting(false);
...@@ -114,6 +123,16 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => { ...@@ -114,6 +123,16 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
const { errors, touched, handleSubmit, isSubmitting, getFieldProps, values } = formik; const { errors, touched, handleSubmit, isSubmitting, getFieldProps, values } = formik;
// const { handleSubmit, isSubmitting } = formik; // const { handleSubmit, isSubmitting } = formik;
const { tutorials } = useSelector(state => state.tutorial);
/**
* API Config
* Tutorial API
*/
useEffect(() => {
dispatch(fetchTutorials());
}, [dispatch])
return ( return (
<> <>
<FormikProvider value={formik}> <FormikProvider value={formik}>
...@@ -232,18 +251,18 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => { ...@@ -232,18 +251,18 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
values.tutorials.map((item, index) => { values.tutorials.map((item, index) => {
return ( return (
<> <>
<Grid item xs={11}> <Grid item xs={11}>
<Stack spacing={1.25}> <Stack spacing={1.25}>
<InputLabel htmlFor={`tutorials.${index}`}>Tutorial</InputLabel> <InputLabel htmlFor={`tutorials.${index}`}>Tutorial</InputLabel>
<Autocomplete <Autocomplete
fullWidth fullWidth
id={`tutorials.${index}`} id={`tutorials.${index}`}
// value={tutorials.find((option) => option.id === formik.values.curriculumLevel) || null} value={tutorials.find((option) => option._id === formik.values.tutorials[index]) || null}
// onChange={(event: any, newValue: CurriculumLevelsType | null) => { onChange={(event: any, newValue: Tutorial | null) => {
// formik.setFieldValue(`tutorials.${index}`, newValue?.id); formik.setFieldValue(`tutorials.${index}`, newValue?._id);
// }} }}
options={[]} options={tutorials}
// getOptionLabel={(item) => `${item.description}`} getOptionLabel={(item) => `${item.tutorialTitle}`}
renderInput={(params) => { renderInput={(params) => {
return ( return (
<TextField <TextField
......
...@@ -7,20 +7,22 @@ import { PopupTransition } from 'components/@extended/Transitions'; ...@@ -7,20 +7,22 @@ import { PopupTransition } from 'components/@extended/Transitions';
// assets // assets
import { DeleteFilled } from '@ant-design/icons'; import { DeleteFilled } from '@ant-design/icons';
import { useDispatch } from 'store';
import { deleteCurriculum } from 'store/reducers/curriculum';
// types // types
interface Props { interface Props {
title: string; title: string;
open: boolean; open: boolean;
handleClose: (status: boolean) => void; handleClose: (status: boolean) => void;
deleteId: number | string | undefined; deleteId: string | undefined;
} }
// ==============================|| Curriculum - DELETE ||============================== // // ==============================|| Curriculum - DELETE ||============================== //
export default function AlertCurriculumDelete({ title, open, handleClose, deleteId }: Props) { export default function AlertCurriculumDelete({ title, open, handleClose, deleteId }: Props) {
// const dispatch = useDispatch(); const dispatch = useDispatch();
return ( return (
<Dialog <Dialog
...@@ -41,6 +43,14 @@ export default function AlertCurriculumDelete({ title, open, handleClose, delete ...@@ -41,6 +43,14 @@ export default function AlertCurriculumDelete({ title, open, handleClose, delete
<Typography variant="h4" align="center"> <Typography variant="h4" align="center">
Are you sure you want to delete? Are you sure you want to delete?
</Typography> </Typography>
<Typography align="center">
By deleting
<Typography variant="subtitle1" component="span">
{' '}
"{title}"{' '}
</Typography>
Curriculum, Its details & tasks will also be deleted.
</Typography>
</Stack> </Stack>
<Stack direction="row" spacing={2} sx={{ width: 1 }}> <Stack direction="row" spacing={2} sx={{ width: 1 }}>
...@@ -48,7 +58,7 @@ export default function AlertCurriculumDelete({ title, open, handleClose, delete ...@@ -48,7 +58,7 @@ export default function AlertCurriculumDelete({ title, open, handleClose, delete
Cancel Cancel
</Button> </Button>
<Button fullWidth color="error" variant="contained" onClick={() => { <Button fullWidth color="error" variant="contained" onClick={() => {
// dispatch(deleteNutrition(deleteId!)) dispatch(deleteCurriculum(deleteId!))
handleClose(true) handleClose(true)
}} autoFocus> }} autoFocus>
Delete Delete
......
...@@ -7,20 +7,22 @@ import { PopupTransition } from 'components/@extended/Transitions'; ...@@ -7,20 +7,22 @@ import { PopupTransition } from 'components/@extended/Transitions';
// assets // assets
import { DeleteFilled } from '@ant-design/icons'; import { DeleteFilled } from '@ant-design/icons';
import { useDispatch } from 'store';
import { deleteTutorial } from 'store/reducers/tutorial';
// types // types
interface Props { interface Props {
title: string; title: string;
open: boolean; open: boolean;
handleClose: (status: boolean) => void; handleClose: (status: boolean) => void;
deleteId: number | string | undefined; deleteId: string | undefined;
} }
// ==============================|| Tutorial - DELETE ||============================== // // ==============================|| Tutorial - DELETE ||============================== //
export default function AlertTutorialDelete({ title, open, handleClose, deleteId }: Props) { export default function AlertTutorialDelete({ title, open, handleClose, deleteId }: Props) {
// const dispatch = useDispatch(); const dispatch = useDispatch();
return ( return (
<Dialog <Dialog
...@@ -41,6 +43,14 @@ export default function AlertTutorialDelete({ title, open, handleClose, deleteId ...@@ -41,6 +43,14 @@ export default function AlertTutorialDelete({ title, open, handleClose, deleteId
<Typography variant="h4" align="center"> <Typography variant="h4" align="center">
Are you sure you want to delete? Are you sure you want to delete?
</Typography> </Typography>
<Typography align="center">
By deleting
<Typography variant="subtitle1" component="span">
{' '}
"{title}"{' '}
</Typography>
tutorial, Its details & tasks will also be deleted.
</Typography>
</Stack> </Stack>
<Stack direction="row" spacing={2} sx={{ width: 1 }}> <Stack direction="row" spacing={2} sx={{ width: 1 }}>
...@@ -48,7 +58,7 @@ export default function AlertTutorialDelete({ title, open, handleClose, deleteId ...@@ -48,7 +58,7 @@ export default function AlertTutorialDelete({ title, open, handleClose, deleteId
Cancel Cancel
</Button> </Button>
<Button fullWidth color="error" variant="contained" onClick={() => { <Button fullWidth color="error" variant="contained" onClick={() => {
// dispatch(deleteNutrition(deleteId!)) dispatch(deleteTutorial(deleteId!))
handleClose(true) handleClose(true)
}} autoFocus> }} autoFocus>
Delete Delete
......
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { Curriculum, DefaultRootStateProps } from 'types/curriculum';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['curriculum'] = {
error: null,
success: null,
curriculums: [],
curriculum: null,
isLoading: false
};
const slice = createSlice({
name: 'curriculum',
initialState,
reducers: {
// TO INITIAL STATE
hasInitialState(state) {
state.error = null;
state.success = null;
state.isLoading = false;
},
// HAS ERROR
hasError(state, action) {
state.error = action.payload;
},
startLoading(state) {
state.isLoading = true;
},
finishLoading(state) {
state.isLoading = false;
},
// POST CURRICULUM
addCurriculumSuccess(state, action) {
state.curriculums.push(action.payload);
state.success = "Curriculum created successfully."
},
// GET CURRICULUM
fetchCurriculumSuccess(state, action) {
state.curriculum = action.payload;
state.success = null
},
// GET ALL CURRICULUM
fetchCurriculumsSuccess(state, action) {
state.curriculums = action.payload;
state.success = null
},
// UPDATE CURRICULUM
updateCurriculumSuccess(state, action) {
const updatedCurriculumIndex = state.curriculums.findIndex(curriculum => curriculum._id === action.payload._id);
if (updatedCurriculumIndex !== -1) {
state.curriculums[updatedCurriculumIndex] = action.payload;
}
state.success = "Curriculum updated successfully."
},
// DELETE CURRICULUM
deleteCurriculumSuccess(state, action) {
state.curriculums = state.curriculums.filter(curriculum => curriculum._id !== action.payload);
state.success = "Curriculum deleted successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST CURRICULUM
* @param newCurriculum
* @returns
*/
export function addCurriculum(newCurriculum: Curriculum) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.post('/rest_node/curriculum', newCurriculum);
dispatch(slice.actions.addCurriculumSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET CURRICULUM
* @param id
* @returns
*/
export function fetchCurriculum(id: number) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/rest_node/curriculum/${id}`);
dispatch(slice.actions.fetchCurriculumSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET ALL CURRICULUMS
* @returns
*/
export function fetchCurriculums() {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get('/rest_node/curriculum');
dispatch(slice.actions.fetchCurriculumsSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* UPDATE CURRICULUM
* @param updatedCurriculum
* @returns
*/
export function updateCurriculum(updatedCurriculum: Curriculum) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.put(`/rest_node/curriculum/${updatedCurriculum._id}`, updatedCurriculum);
dispatch(slice.actions.updateCurriculumSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* DELETE CURRICULUM
* @param curriculumId
* @returns
*/
export function deleteCurriculum(curriculumId: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
await axiosServices.delete(`/rest_node/curriculum/${curriculumId}`);
dispatch(slice.actions.deleteCurriculumSuccess(curriculumId));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
...@@ -7,15 +7,19 @@ import storage from 'redux-persist/lib/storage'; ...@@ -7,15 +7,19 @@ import storage from 'redux-persist/lib/storage';
import calendar from './calendar'; import calendar from './calendar';
import cartReducer from './cart'; import cartReducer from './cart';
import chat from './chat'; import chat from './chat';
import curriculum from './curriculum';
import ingredient from './ingredient'; import ingredient from './ingredient';
import invoice from './invoice'; import invoice from './invoice';
import kanban from './kanban'; import kanban from './kanban';
import marksCalculator from './marksCalculator';
import menu from './menu'; import menu from './menu';
import nutrition from './nutrition'; import nutrition from './nutrition';
import productReducer from './product'; import productReducer from './product';
import snackbar from './snackbar'; import snackbar from './snackbar';
import subscription from './subscription'; import subscription from './subscription';
import tutorial from './tutorial';
import user from './user';
import userProgress from './userProgress';
// ==============================|| COMBINE REDUCERS ||============================== // // ==============================|| COMBINE REDUCERS ||============================== //
...@@ -37,7 +41,12 @@ const reducers = combineReducers({ ...@@ -37,7 +41,12 @@ const reducers = combineReducers({
invoice, invoice,
nutrition, nutrition,
ingredient, ingredient,
subscription subscription,
marksCalculator,
tutorial,
curriculum,
user,
userProgress
}); });
export default reducers; export default reducers;
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { DefaultRootStateProps } from 'types/marksCalculator';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['marksCalculator'] = {
error: null,
success: null,
marksCalculator: null,
isLoading: false
};
const slice = createSlice({
name: 'marksCalculator',
initialState,
reducers: {
// TO INITIAL STATE
hasInitialState(state) {
state.error = null;
state.success = null;
state.isLoading = false;
},
// HAS ERROR
hasError(state, action) {
state.error = action.payload;
},
startLoading(state) {
state.isLoading = true;
},
finishLoading(state) {
state.isLoading = false;
},
// POST USER
marksCalculatorSuccess(state, action) {
state.marksCalculator = action.payload.result;
state.success = "Marks Calculated Successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST Marks Calculator
* @param curriculumIndex
* @param tutorialIndex
* @param imageData
* @param targetClass
* @returns
*/
export function CalculateMarks(curriculumIndex: number, tutorialIndex: number, imageData: any, targetClass: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
// Construct the request body as needed (e.g., for formData)
const formData = new FormData();
formData.append('image', imageData);
formData.append('class', targetClass);
const response = await axiosServices.post(`/rest_node/marks-calculator/curriculum/${curriculumIndex}/tutorial/${tutorialIndex}`, formData);
dispatch(slice.actions.marksCalculatorSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
};
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { DefaultRootStateProps, Tutorial } from 'types/tutorial';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['tutorial'] = {
error: null,
success: null,
tutorials: [],
tutorial: null,
isLoading: false
};
const slice = createSlice({
name: 'tutorial',
initialState,
reducers: {
// TO INITIAL STATE
hasInitialState(state) {
state.error = null;
state.success = null;
state.isLoading = false;
},
// HAS ERROR
hasError(state, action) {
state.error = action.payload;
},
startLoading(state) {
state.isLoading = true;
},
finishLoading(state) {
state.isLoading = false;
},
// POST TUTORIAL
addTutorialSuccess(state, action) {
state.tutorials.push(action.payload);
state.success = "Tutorial created successfully."
},
// GET TUTORIAL
fetchTutorialSuccess(state, action) {
state.tutorial = action.payload;
state.success = null
},
// GET ALL TUTORIAL
fetchTutorialsSuccess(state, action) {
state.tutorials = action.payload;
state.success = null
},
// UPDATE TUTORIAL
updateTutorialSuccess(state, action) {
const updatedTutorialIndex = state.tutorials.findIndex(tutorial => tutorial._id === action.payload._id);
if (updatedTutorialIndex !== -1) {
state.tutorials[updatedTutorialIndex] = action.payload;
}
state.success = "Tutorial updated successfully."
},
// DELETE TUTORIAL
deleteTutorialSuccess(state, action) {
state.tutorials = state.tutorials.filter(tutorial => tutorial._id !== action.payload);
state.success = "Tutorial deleted successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST TUTORIAL
* @param newTutorial
* @returns
*/
export function addTutorial(newTutorial: Tutorial) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.post('/rest_node/tutorial', newTutorial);
dispatch(slice.actions.addTutorialSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET TUTORIAL
* @param id
* @returns
*/
export function fetchTutorial(id: number) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/rest_node/tutorial/${id}`);
dispatch(slice.actions.fetchTutorialSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET ALL TUTORIALS
* @returns
*/
export function fetchTutorials() {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get('/rest_node/tutorial');
dispatch(slice.actions.fetchTutorialsSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* UPDATE TUTORIAL
* @param updatedTutorial
* @returns
*/
export function updateTutorial(updatedTutorial: Tutorial) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.put(`/rest_node/tutorial/${updatedTutorial._id}`, updatedTutorial);
dispatch(slice.actions.updateTutorialSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* DELETE TUTORIAL
* @param tutorialId
* @returns
*/
export function deleteTutorial(tutorialId: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
await axiosServices.delete(`/rest_node/tutorial/${tutorialId}`);
dispatch(slice.actions.deleteTutorialSuccess(tutorialId));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
...@@ -103,7 +103,7 @@ export function addUser(newUser: User) { ...@@ -103,7 +103,7 @@ export function addUser(newUser: User) {
dispatch(slice.actions.startLoading()); dispatch(slice.actions.startLoading());
try { try {
const response = await axiosServices.post('/user/sign-up', newUser); const response = await axiosServices.post('/rest_node/user/sign-up', newUser);
dispatch(slice.actions.addUserSuccess(response.data)); dispatch(slice.actions.addUserSuccess(response.data));
} catch (error) { } catch (error) {
dispatch(slice.actions.hasError(error)); dispatch(slice.actions.hasError(error));
...@@ -123,7 +123,7 @@ export function fetchUser(id: number) { ...@@ -123,7 +123,7 @@ export function fetchUser(id: number) {
dispatch(slice.actions.startLoading()); dispatch(slice.actions.startLoading());
try { try {
const response = await axiosServices.get(`/user/${id}`); const response = await axiosServices.get(`/rest_node/user/${id}`);
dispatch(slice.actions.fetchUserSuccess(response.data)); dispatch(slice.actions.fetchUserSuccess(response.data));
} catch (error) { } catch (error) {
dispatch(slice.actions.hasError(error)); dispatch(slice.actions.hasError(error));
...@@ -143,7 +143,7 @@ export function fetchUsers(userType: string) { ...@@ -143,7 +143,7 @@ export function fetchUsers(userType: string) {
dispatch(slice.actions.startLoading()); dispatch(slice.actions.startLoading());
try { try {
const response = await axiosServices.get(`/user/all/type/${userType}`); const response = await axiosServices.get(`/rest_node/user/all/type/${userType}`);
dispatch(slice.actions.fetchUsersSuccess(response.data)); dispatch(slice.actions.fetchUsersSuccess(response.data));
} catch (error) { } catch (error) {
dispatch(slice.actions.hasError(error)); dispatch(slice.actions.hasError(error));
...@@ -162,7 +162,7 @@ export function fetchUsersByType() { ...@@ -162,7 +162,7 @@ export function fetchUsersByType() {
dispatch(slice.actions.startLoading()); dispatch(slice.actions.startLoading());
try { try {
const response = await axiosServices.get('/user/all'); const response = await axiosServices.get('/rest_node/user/all');
dispatch(slice.actions.fetchUsersSuccess(response.data)); dispatch(slice.actions.fetchUsersSuccess(response.data));
} catch (error) { } catch (error) {
dispatch(slice.actions.hasError(error)); dispatch(slice.actions.hasError(error));
...@@ -182,7 +182,7 @@ export function updateUser(updatedUser: User) { ...@@ -182,7 +182,7 @@ export function updateUser(updatedUser: User) {
dispatch(slice.actions.startLoading()); dispatch(slice.actions.startLoading());
try { try {
const response = await axiosServices.put(`/user/${updatedUser._id}`, updateUser); const response = await axiosServices.put(`/rest_node/user/${updatedUser._id}`, updateUser);
dispatch(slice.actions.updateUserSuccess(response.data)); dispatch(slice.actions.updateUserSuccess(response.data));
} catch (error) { } catch (error) {
dispatch(slice.actions.hasError(error)); dispatch(slice.actions.hasError(error));
...@@ -202,7 +202,7 @@ export function deleteNutrition(userId: number) { ...@@ -202,7 +202,7 @@ export function deleteNutrition(userId: number) {
dispatch(slice.actions.startLoading()); dispatch(slice.actions.startLoading());
try { try {
await axiosServices.delete(`/user/${userId}`); await axiosServices.delete(`/rest_node/user/${userId}`);
dispatch(slice.actions.deleteUserSuccess(userId)); dispatch(slice.actions.deleteUserSuccess(userId));
} catch (error) { } catch (error) {
dispatch(slice.actions.hasError(error)); dispatch(slice.actions.hasError(error));
......
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { DefaultRootStateProps, curriculumTypeUserProgress } from 'types/userProgress';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['userProgress'] = {
error: null,
success: null,
userProgresses: [],
userProgress: null,
isLoading: false
};
const slice = createSlice({
name: 'userProgress',
initialState,
reducers: {
// TO INITIAL STATE
hasInitialState(state) {
state.error = null;
state.success = null;
state.isLoading = false;
},
// HAS ERROR
hasError(state, action) {
state.error = action.payload;
},
startLoading(state) {
state.isLoading = true;
},
finishLoading(state) {
state.isLoading = false;
},
// POST USER_PROGRESS
addUpdateUserProgressSuccess(state, action) {
// state.userProgresses.push(action.payload);
state.success = "User Progress created or updated successfully."
},
// GET USER_PROGRESS
fetchUserProgressSuccess(state, action) {
state.userProgress = action.payload.userProgress;
state.success = null
},
// UPDATE USER_PROGRESS
updateUserProgressSuccess(state, action) {
// const updatedUserProgressIndex = state.userProgresses.findIndex(userProgress => userProgress._id === action.payload._id);
// if (updatedUserProgressIndex !== -1) {
// state.userProgresses[updatedUserProgressIndex] = action.payload;
// }
state.success = "UserProgress updated successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST USER_PROGRESS
* @param newUserProgress
* @returns
*/
export function addUserProgress(userId: string, curriculum: curriculumTypeUserProgress) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.post('/rest_node/user-progress/subscribe-curriculum', { userId, curriculum });
dispatch(slice.actions.addUpdateUserProgressSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET USER_PROGRESS
* @param userId
* @returns
*/
export function fetchUserProgress(userId: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/rest_node/user-progress/${userId}`);
dispatch(slice.actions.fetchUserProgressSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* UPDATE USER_PROGRESS
* @param updatedUserProgress
* @returns
*/
export function updateUserProgress(userId: string, curriculumCode: string, tutorialCode: string, taskItemTitle: string, taskItemMarkUser: number, taskItemSpentTime: number) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.put(`/rest_node/user-progress/update-task-item-progress`, { userId, curriculumCode, tutorialCode, taskItemTitle, taskItemMarkUser, taskItemSpentTime });
dispatch(slice.actions.updateUserProgressSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
import { tutorialType } from "./tutorial" import { tutorialType } from "./tutorial"
export interface curriculumType { export interface curriculumType {
_id?: string
curriculumCode: string
curriculumLevel?: number
curriculumTitle?: string
curriculumDescription?: string
curriculumImage?: string
curriculumMark?: number
tutorials?: tutorialType[],
status?: number
createdBy?: string
createdAt?: Date
updatedBy?: string
updatedAt?: Date
subscribedUser?: string[]
}
export type Curriculum = {
_id?: string
curriculumCode: string
curriculumLevel: number
curriculumTitle: string
curriculumDescription: string
curriculumImage: string
curriculumMark?: number
tutorials: string[],
status?: number
createdBy: string
createdAt?: Date
updatedBy?: string
updatedAt?: Date
};
export type Curriculums = {
_id?: string _id?: string
curriculumCode: string curriculumCode: string
curriculumLevel: number curriculumLevel: number
...@@ -14,4 +47,17 @@ export interface curriculumType { ...@@ -14,4 +47,17 @@ export interface curriculumType {
createdAt: Date createdAt: Date
updatedBy?: string updatedBy?: string
updatedAt?: Date updatedAt?: Date
};
export interface CurriculumStateProps {
curriculums: Curriculums[];
curriculum: Curriculum | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
curriculum: CurriculumStateProps;
} }
\ No newline at end of file
// Marks Calculator Type
export type MarksCalculator = {
predicted_class_name: string,
confidence: string,
status: string
};
export interface MarksCalculatorProps {
marksCalculator: MarksCalculator | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
marksCalculator: MarksCalculatorProps;
}
\ No newline at end of file
export interface taskItemType { export interface taskItemType {
_id?: string _id?: string
title: string, title: string,
description: string, description?: string,
howToDo: string[], howToDo?: string[],
referenceImage: string, referenceImage?: string,
referenceVideo: string, referenceVideo?: string,
taskItemMark: number taskItemMark?: number
} }
\ No newline at end of file
import { taskItemType } from "./taskItem" import { taskItemType } from "./taskItem"
export interface tutorialType { export interface tutorialType {
_id?: string
tutorialCode: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMark?: number
taskItems?: taskItemType[]
status?: number
createdBy?: string
createdAt?: Date
updatedBy?: string
updatedAt?: Date
}
export type Tutorial = {
_id?: string
tutorialCode?: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMark?: number
taskItems: taskItemType[]
status?: number
createdBy: string
createdAt?: Date
updatedBy?: string
updatedAt?: Date
};
export type Tutorials = {
_id?: string _id?: string
tutorialCode?: string tutorialCode?: string
tutorialTitle?: string tutorialTitle?: string
...@@ -13,4 +43,17 @@ export interface tutorialType { ...@@ -13,4 +43,17 @@ export interface tutorialType {
createdAt: Date createdAt: Date
updatedBy?: string updatedBy?: string
updatedAt?: Date updatedAt?: Date
};
export interface TutorialStateProps {
tutorials: Tutorials[];
tutorial: Tutorial | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
tutorial: TutorialStateProps;
} }
\ No newline at end of file
...@@ -14,34 +14,72 @@ export interface userProgressType { ...@@ -14,34 +14,72 @@ export interface userProgressType {
export interface curriculumTypeUserProgress { export interface curriculumTypeUserProgress {
curriculumCode: string curriculumCode: string
curriculumLevel: number curriculumLevel?: number
curriculumTitle: string curriculumTitle?: string
curriculumDescription: string curriculumDescription?: string
curriculumImage: string curriculumImage?: string
curriculumMark: number curriculumMark?: number
curriculumMarkUser: number curriculumMarkUser?: number
curriculumSpentTime: number curriculumSpentTime?: number
tutorials: tutorialTypeUserProgress[], tutorials?: tutorialTypeUserProgress[],
} }
export interface tutorialTypeUserProgress { export interface tutorialTypeUserProgress {
tutorialCode?: string tutorialCode: string
tutorialTitle?: string tutorialTitle?: string
tutorialDescription?: string tutorialDescription?: string
tutorialImage?: string tutorialImage?: string
tutorialMark: number tutorialMarks?: number
tutorialMarkUser: number tutorialMarkUser?: number
tutorialSpentTime: number tutorialSpentTime?: number
taskItems: taskItemTypeUserProgress[] taskItems?: taskItemTypeUserProgress[]
} }
export interface taskItemTypeUserProgress { export interface taskItemTypeUserProgress {
title: string, title: string,
description: string, description?: string,
howToDo: string[], howToDo?: string[],
referenceImage: string, referenceImage?: string,
referenceVideo: string, referenceVideo?: string,
taskItemMark: number taskItemMark?: number
taskItemMarkUser: number taskItemMarkUser?: number
taskItemSpentTime: number taskItemSpentTime?: number
}
export type UserProgress = {
_id: string
userId: string
curriculums?: curriculumTypeUserProgress[]
totalCurriculumsMarks: number
totalCurriculumSpentTime: number
status: number
createdBy?: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
};
export type UserProgresses = {
_id?: string
userId: string
curriculums?: curriculumTypeUserProgress[]
totalCurriculumsMarks: number
totalCurriculumSpentTime: number
status: number
createdBy?: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
};
export interface UserProgressStateProps {
userProgresses: UserProgresses[];
userProgress: UserProgress | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
userProgress: UserProgressStateProps;
} }
\ No newline at end of file
# Project ID: 2023-029 # Project ID: 2023-029
### test
# Project Name: “Sign Language Translation with emotional base multi model,3D Avatar and Adaptive Learning.” # Project Name: “Sign Language Translation with emotional base multi model,3D Avatar and Adaptive Learning.”
## Main Objective ## Main Objective
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment