Commit 129c8db5 authored by janithgamage1.ed's avatar janithgamage1.ed

Merge branch 'master' into IT20254384

parents 1839062b 60d92c17
......@@ -32,7 +32,8 @@ export const createCurriculum = async (req, res) => {
let totalTutorialMark = 0;
for (const tutorialId of curriculumData.tutorials) {
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;
......@@ -55,7 +56,8 @@ export const updateCurriculum = async (req, res) => {
let totalTutorialMark = 0;
for (const tutorialId of updatedCurriculum.tutorials) {
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;
......@@ -71,7 +73,7 @@ export const deleteCurriculum = async (req, res) => {
const { id } = req.params;
try {
await Curriculum.findByIdAndDelete(id);
res.status(200).json({ message: 'Curriculum deleted successfully' });
res.status(200).json({ _id: id, message: 'Curriculum deleted successfully' });
} catch (error) {
res.status(404).json({ message: 'Curriculum not found' });
}
......
import { exec } from "child_process";
import multer from "multer"
export const marksCalculator = async (req, res) => {
try {
// console.log(req.file);
// if (!req.file || !req.body.class || !req.params.curriculumIndex || !req.params.tutorialIndex) {
// return res.status(400).json({ code: "02", message: "Missing required data" });
// }
const imageData = req.file.buffer.toString('base64');
const targetClass = req.body.class;
const { curriculumIndex, tutorialIndex } = req.params;
const status = "";
// console.log(curriculumIndex, tutorialIndex);
try {
if (curriculumIndex == 1 && tutorialIndex == 1) {
// Run Python script to perform prediction
const pythonProcess = exec('python prediction_config/C1T1/predict.py', (error, stdout, stderr) => {
if (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(',');
......@@ -43,9 +45,10 @@ export const marksCalculator = async (req, res) => {
pythonProcess.stdin.write(`${imageData}\n${targetClass}`);
pythonProcess.stdin.end();
} 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) {
res.status(500).json({ code: "00", message: "Something went wrong" })
console.error(error);
res.status(500).json({ code: "00", message: "Something went wrong" });
}
}
......@@ -23,7 +23,11 @@ export const createTutorial = async (req, res) => {
const tutorialData = req.body;
// 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;
try {
......@@ -31,32 +35,50 @@ export const createTutorial = async (req, res) => {
await newTutorial.save();
res.status(201).json(newTutorial);
} 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) => {
const { id } = req.params;
const updatedTutorialData = req.body;
// 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;
try {
const result = await Tutorial.findByIdAndUpdate(id, updatedTutorialData, { new: true });
res.status(200).json(result);
const tutorial = await Tutorial.findById(id);
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) {
res.status(404).json({ message: 'Tutorial not found' });
res.status(500).json({ message: error.message });
}
}
};
export const deleteTutorial = async (req, res) => {
const { id } = req.params;
try {
const tutorial = await Tutorial.findById(id);
if (!tutorial) {
return res.status(404).json({ message: 'Tutorial not found' });
}
await Tutorial.findByIdAndDelete(id);
res.status(200).json({ message: 'Tutorial deleted successfully' });
res.status(200).json({ _id: id, message: 'Tutorial deleted successfully' });
} 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) => {
}
}
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";
// Logic to subscribe to a curriculum for a user
export const subscribeCurriculum = async (req, res) => {
const { userId, curriculum } = req.body;
try {
// Check if the user is already subscribed to the curriculum
let userProgress = await UserProgress.findOne({ userId });
if (!userProgress) {
// If there is no user progress record for this user, create a new one
userProgress = new UserProgress({
userId,
curriculums: [curriculum], // Add the curriculum to the curriculums array
curriculums: [curriculum],
});
} else {
// If there is an existing user progress record, check if the curriculum already exists
......@@ -19,7 +20,7 @@ export const subscribeCurriculum = async (req, res) => {
if (existingCurriculumIndex !== -1) {
// If the curriculum exists, update it
userProgress.curriculums[existingCurriculumIndex] = curriculum;
return res.status(400).json({ error: 'User already subscribed to this curriculum' });
} else {
// If the curriculum doesn't exist, add it to the array
userProgress.curriculums.push(curriculum);
......@@ -27,9 +28,21 @@ export const subscribeCurriculum = async (req, res) => {
}
// 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;
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
await userProgress.save();
......@@ -86,8 +99,9 @@ export const updateTaskItemProgress = async (req, res) => {
}
// Update task item progress
userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex].taskItemMarkUser = taskItemMarkUser;
userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex].taskItemSpentTime = taskItemSpentTime;
const taskItem = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex];
taskItem.taskItemMarkUser = typeof taskItemMarkUser === 'string' ? parseFloat(taskItemMarkUser) : taskItemMarkUser;
taskItem.taskItemSpentTime = taskItemSpentTime;
// Calculate total task marks and spent time for the tutorial
const tutorial = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex];
......@@ -112,3 +126,4 @@ export const updateTaskItemProgress = async (req, res) => {
res.status(500).json({ error: 'Internal server error' });
}
};
......@@ -35,7 +35,11 @@ const curriculumSchema = new mongoose.Schema({
type: Number,
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);
......
......@@ -16,7 +16,7 @@ const commonFields = {
const taskItemSchema = new mongoose.Schema({
title: String,
description: String,
howToDo: String,
howToDo: [String],
referenceImage: String,
referenceVideo: String,
taskItemMark : {
......
......@@ -31,7 +31,7 @@ const tutorialTypeUserProgressSchema = new mongoose.Schema({
tutorialTitle: String,
tutorialDescription: String,
tutorialImage: String,
tutorialMark: {
tutorialMarks: {
type: Number,
default: 0
},
......
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();
router.post('/sign-in', signIn)
router.post('/sign-up', signUp)
router.get('/all', getUsers);
router.get('/current-user', auth, currentUser);
router.get('/:id', getUser);
router.get('/all/type/:userType', getUserAccordingToType);
router.put('/:id', updateUser);
......
......@@ -23,9 +23,13 @@ import userProgressRoutes from "./routes/userProgress.routes.js";
dotenv.config();
const app = express();
const corsOptions = {
origin: 'http://localhost:3000',
};
app.use(bodyParser.json({ limit: "30mb", extended: true }));
app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
app.use(cors());
app.use(cors(corsOptions));
//end
app.get("/", (req, res) => {
......
......@@ -56,17 +56,27 @@ export const JWTProvider = ({ children }: { children: React.ReactElement }) => {
const init = async () => {
try {
const serviceToken = window.localStorage.getItem('serviceToken');
console.log(verifyToken(serviceToken!));
if (serviceToken && verifyToken(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({
type: LOGIN,
payload: {
isLoggedIn: true,
// user
user: {
id: user._id,
email: user.email,
name: `${user.firstName} ${user.lastName}`,
role: user.type
}
}
});
} else {
......
import { tutorialType } from "types/tutorial";
import { Tutorial, tutorialType } from "types/tutorial";
export const tutorials: tutorialType[] = [
{
......@@ -181,3 +181,25 @@ export const tutorials: tutorialType[] = [
"tutorialMark": 0,
}
]
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 = {
"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.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"tutorialMark": 100,
"tutorialMarks": 100,
"tutorialMarkUser": 24,
"tutorialSpentTime": 15,
"taskItems": [
......@@ -170,7 +170,7 @@ export const userProgress: userProgressType = {
"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.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy",
"tutorialMark": 0,
"tutorialMarks": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
......@@ -180,7 +180,7 @@ export const userProgress: userProgressType = {
"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.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg",
"tutorialMark": 0,
"tutorialMarks": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
......@@ -190,7 +190,7 @@ export const userProgress: userProgressType = {
"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.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE",
"tutorialMark": 0,
"tutorialMarks": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
......
......@@ -157,7 +157,7 @@ const application: NavItemType = {
id: 'learning-curriculums-subscribed-tutorial',
title: <FormattedMessage id="learning-curriculums-subscribed-tutorial" />,
type: 'item',
url: '/learning-management/curriculums-subscribed-tutorial',
url: '/learning-management/curriculums-subscribed-tutorial/0/0',
},
{
id: 'learning-lead-board',
......
// material-ui
// project import
// ==============================|| Dashboard ||============================== //
const Dashboard = () => (
const Dashboard = () => {
return (
<>
</>
);
);
}
export default Dashboard;
import { useState } from 'react';
// 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
......@@ -15,11 +17,67 @@ import WelcomeBanner from 'sections/learning-management/WelcomeBanner';
// ==============================|| Dashboard ||============================== //
const Dashboard = () => {
const [expanded, setExpanded] = useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};
return (
<Grid container rowSpacing={4.5} columnSpacing={3}>
<Grid container rowSpacing={2} columnSpacing={3}>
<Grid item xs={12}>
<WelcomeBanner />
</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>
)
}
......
......@@ -26,9 +26,12 @@ import { GlobalFilter } from 'utils/react-table';
// assets
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 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';
// ==============================|| List ||============================== //
......@@ -46,6 +49,9 @@ const allColumns = [
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { userProgress, error, success, isLoading } = useSelector(state => state.userProgress);
const { user } = useAuth();
const [data, setData] = useState<userProgressType["curriculums"]>([])
......@@ -62,6 +68,8 @@ const List = () => {
// search
useEffect(() => {
if (!data || data.length == 0) return
const newData = data?.filter((value: any) => {
if (globalFilter) {
return value.curriculumTitle.toLowerCase().includes(globalFilter.toLowerCase());
......@@ -83,16 +91,96 @@ const List = () => {
_DATA.jump(p);
};
useEffect(() => {
setData(userProgress.curriculums)
}, [])
const [expanded, setExpanded] = useState<string | false>('panel0');
const handleAccordionChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
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 (
<>
<Box sx={{ position: 'relative', marginBottom: 3 }}>
......@@ -173,7 +261,7 @@ const List = () => {
</Stack>
</AccordionSummary>
<AccordionDetails>
<CurriculumSection curriculum={curriculum} />
<CurriculumSection curriculum={curriculum} curriculumIndex={index} />
</AccordionDetails>
</Accordion>
</Box>
......
......@@ -12,3 +12,8 @@ export interface selectedCommonDataProps {
tutorialCode: string
title: string
}
export interface itemResultProps {
itemMarkUser: number
status: string
}
\ No newline at end of file
......@@ -21,13 +21,15 @@ import {
import usePagination from 'hooks/usePagination';
import CurriculumCard from 'sections/learning-management/learning-curriculums/CurriculumCard';
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 { GlobalFilter } from 'utils/react-table';
// types
// assets
import { curriculums } from 'data/curriculums';
// ==============================|| List ||============================== //
......@@ -43,6 +45,10 @@ const allColumns = [
];
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 matchDownSM = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
......@@ -79,9 +85,59 @@ const List = () => {
_DATA.jump(p);
};
/**
* API Config
* Curriculum API
*/
useEffect(() => {
dispatch(fetchCurriculums());
}, [dispatch, userProgressSuccess]);
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(() => {
setData(curriculums)
}, [])
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 (
<>
......
import { MouseEvent, useMemo } from 'react';
import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui
import {
......@@ -31,7 +31,12 @@ import {
import { DeleteTwoTone, EditTwoTone } from '@ant-design/icons';
//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 ||============================== //
......@@ -101,10 +106,10 @@ function ReactTable({ columns, data }: ReactTableProps) {
);
})
) : (
<EmptyTable msg="No Data" colSpan={5} />
<EmptyTable msg="No Data" colSpan={12} />
)}
<TableRow>
<TableCell sx={{ p: 2 }} colSpan={5}>
<TableCell sx={{ p: 2 }} colSpan={12}>
<TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} />
</TableCell>
</TableRow>
......@@ -119,18 +124,22 @@ function ReactTable({ columns, data }: ReactTableProps) {
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { users, error, success, isLoading } = useSelector(state => state.user);
// table
const data: dataProps[] = [
{
id: 1,
userName: "Nuwan Gamage",
userContactNumber: "0768523456",
userEmailAddress: "nuwangamage@gmail.com",
userType: "Member",
userStatus: "Active"
}
]
// const data: dataProps[] = [
// {
// id: 1,
// userName: "Nuwan Gamage",
// userContactNumber: "0768523456",
// userEmailAddress: "nuwangamage@gmail.com",
// userType: "Member",
// userStatus: "Active"
// }
// ]
const [data, setData] = useState<Users[]>([])
const columns = useMemo(
() =>
......@@ -155,28 +164,64 @@ const List = () => {
},
{
Header: 'User Name',
accessor: 'userName',
className: 'cell-center',
accessor: 'firstName',
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',
accessor: 'userEmailAddress',
className: 'cell-center',
accessor: 'email',
},
{
Header: 'User Contact Number',
accessor: 'userContactNumber',
className: 'cell-center',
accessor: 'contactNumber',
},
{
Header: 'User Type',
accessor: 'userType',
className: 'cell-center',
accessor: 'type',
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',
accessor: 'userStatus',
accessor: 'states',
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",
......@@ -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 (
<>
<MainCard content={false}>
......
import { Column } from 'react-table';
import { Users } from 'types/user';
export interface dataProps {
id: number | string | undefined
......@@ -11,7 +12,7 @@ export interface dataProps {
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
data: Users[]
}
export interface userProps {
......
import { MouseEvent, useMemo, useState } from 'react';
import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui
import {
......@@ -34,9 +34,15 @@ import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-desig
//types
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 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 ||============================== //
......@@ -122,16 +128,21 @@ function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
// ==============================|| List ||============================== //
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { curriculums, error, success, isLoading } = useSelector(state => state.curriculum);
// table
const data: dataProps[] = []
// const data: dataProps[] = []
// table
const [data, setData] = useState<Curriculums[]>([])
const columns = useMemo(
() =>
[
{
Header: '#',
accessor: 'id',
accessor: '_id',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.id === undefined || row.id === null || row.id === '') {
......@@ -153,19 +164,72 @@ const List = () => {
},
{
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',
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',
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',
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",
......@@ -209,6 +273,7 @@ const List = () => {
onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setCurriculumId(row.values._id)
setCurriculumTitle(row.values.curriculumTitle)
setOpenAlert(true)
}}
>
......@@ -226,7 +291,7 @@ const List = () => {
//dialog model
const [addEdit, setAddEdit] = useState<boolean>(false);
const [curriculum, setCurriculum] = useState<curriculumProps>();
const [curriculum, setCurriculum] = useState<Curriculum>();
const handleAddEdit = () => {
setAddEdit(!addEdit);
......@@ -235,12 +300,66 @@ const List = () => {
//alert model
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 = () => {
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 (
<>
<MainCard content={false}>
......@@ -258,10 +377,10 @@ const List = () => {
sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }}
aria-describedby="alert-dialog-slide-description"
>
<AddEditCurriculum curriculum={curriculum} onCancel={handleAddEdit} />
<AddEditCurriculum curriculum={curriculum!} onCancel={handleAddEdit} />
</Dialog>
{/* alert model */}
{!curriculum && <AlertCurriculumDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={curriculumId} />}
{!curriculum && <AlertCurriculumDelete title={curriculumTitle!} open={openAlert} handleClose={handleAlertClose} deleteId={curriculumId} />}
</MainCard>
</>
)
......
import { Column } from 'react-table';
import { Curriculums } from 'types/curriculum';
export interface dataProps {
_id: number | string | undefined;
......@@ -17,7 +18,7 @@ export interface dataProps {
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
data: Curriculums[]
handleAddEdit: () => void
}
......
import { Column } from 'react-table';
import { Tutorials } from 'types/tutorial';
export interface dataProps {
_id: number | string | undefined;
......@@ -16,7 +17,7 @@ export interface dataProps {
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
data: Tutorials[]
handleAddEdit: () => void
}
......
import { MouseEvent, useMemo, useState } from 'react';
import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui
import {
......@@ -32,11 +32,16 @@ import {
import { DeleteTwoTone, EditTwoTone, PlusOutlined } from '@ant-design/icons';
//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 ||============================== //
function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) {
function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
const filterTypes = useMemo(() => renderFilterTypes, []);
const defaultColumn = useMemo(() => ({ Filter: DefaultColumnFilter }), []);
......@@ -102,10 +107,10 @@ function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) {
);
})
) : (
<EmptyTable msg="No Data" colSpan={5} />
<EmptyTable msg="No Data" colSpan={12} />
)}
<TableRow>
<TableCell sx={{ p: 2 }} colSpan={5}>
<TableCell sx={{ p: 2 }} colSpan={12}>
<TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} />
</TableCell>
</TableRow>
......@@ -120,26 +125,33 @@ function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) {
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { users, error, success, isLoading } = useSelector(state => state.user);
// table
// const data: dataProps[] = [
// {
// id: 1,
// userName: "Janith Gamage",
// userContactNumber: "0768523525",
// userEmailAddress: "janithgamage1.ed@gmail.com",
// userType: "Admin",
// userStatus: "Active"
// },
// {
// id: 1,
// userName: "Nuwan Gamage",
// userContactNumber: "0768523456",
// userEmailAddress: "nuwangamage@gmail.com",
// userType: "Member",
// userStatus: "Active"
// }
// ]
// table
const data: dataProps[] = [
{
id: 1,
userName: "Janith Gamage",
userContactNumber: "0768523525",
userEmailAddress: "janithgamage1.ed@gmail.com",
userType: "Admin",
userStatus: "Active"
},
{
id: 1,
userName: "Nuwan Gamage",
userContactNumber: "0768523456",
userEmailAddress: "nuwangamage@gmail.com",
userType: "Member",
userStatus: "Active"
}
]
const [data, setData] = useState<Users[]>([])
//dialog model
const [addEdit, setAddEdit] = useState<boolean>(false);
......@@ -173,28 +185,64 @@ const List = () => {
},
{
Header: 'User Name',
accessor: 'userName',
className: 'cell-center',
accessor: 'firstName',
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',
accessor: 'userEmailAddress',
className: 'cell-center',
accessor: 'email',
},
{
Header: 'User Contact Number',
accessor: 'userContactNumber',
className: 'cell-center',
accessor: 'contactNumber',
},
{
Header: 'User Type',
accessor: 'userType',
className: 'cell-center',
accessor: 'type',
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',
accessor: 'userStatus',
accessor: 'states',
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",
......@@ -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 (
<>
<MainCard content={false}>
......
import { Column } from 'react-table';
import { Users } from 'types/user';
export interface dataProps {
id: number | string | undefined
......@@ -11,7 +12,7 @@ export interface dataProps {
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
data: Users[]
handleAddEdit: () => void
}
......
......@@ -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 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 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 LearningLeadBoard = Loadable(lazy(() => import('pages/learning-management/learning-lead-board/list/list')));
// const LearningFeedBack = Loadable(lazy(() => import('pages/learning-management/learning-feedback/list/list')));
// render - parameter curriculum management page
const CurriculumManagementList = Loadable(lazy(() => import('pages/parameter/curriculum-management/list/list')));
......@@ -148,15 +148,25 @@ const MainRoutes = {
},
{
path: 'curriculums-subscribed-tutorial',
children: [
{
path: ':curriculumIndex', // Parameter for curriculum index
children: [
{
path: ':tutorialIndex', // Parameter for tutorial index
element: <LearningCurriculumsSubscribedTutorial />
}
]
}
]
},
{
path: 'lead-board',
element: <LearningLeadBoard />
element: <MaintenanceUnderConstruction />
},
{
path: 'feedback',
element: <LearningFeedBack />
element: <MaintenanceUnderConstruction />
}
]
},
......
......@@ -25,7 +25,7 @@ import ReportCard from 'components/cards/statistics/ReportCard';
// ==============================|| Curriculum - Section ||============================== //
const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgress }) => {
const CurriculumSection = ({ curriculum, curriculumIndex }: { curriculum: curriculumTypeUserProgress, curriculumIndex: number }) => {
const theme = useTheme();
return (
......@@ -53,10 +53,10 @@ const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgr
Your learning capacity is 80% as daily analytics
</Typography>
<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>
<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
sx={{
......@@ -84,8 +84,8 @@ const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgr
</MainCard>
<MainCard title="Tutorials">
<Grid container spacing={2}>
{curriculum.tutorials.map((tutorial, index) => {
return (<TutorialSection tutorial={tutorial!} />)
{curriculum.tutorials && curriculum.tutorials?.map((tutorial, index) => {
return (<TutorialSection tutorial={tutorial!} curriculumIndex={curriculumIndex} tutorialIndex={index} />)
})}
</Grid>
</MainCard>
......
......@@ -25,7 +25,7 @@ import MainCard from 'components/MainCard';
// ==============================|| Tutorial - Section ||============================== //
const TutorialSection = ({ tutorial }: { tutorial: tutorialTypeUserProgress }) => {
const TutorialSection = ({ tutorial, tutorialIndex, curriculumIndex }: { tutorial: tutorialTypeUserProgress, curriculumIndex: number, tutorialIndex: number }) => {
let navigation = useNavigate()
const [desc, setDesc] = useState(tutorial.tutorialDescription?.slice(0, 100))
......@@ -70,7 +70,7 @@ const TutorialSection = ({ tutorial }: { tutorial: tutorialTypeUserProgress }) =
variant="outlined"
endIcon={<PlaySquareOutlined />}
sx={{ my: 2 }}
onClick={() => { navigation(`/learning-management/curriculums-subscribed-tutorial`) }}
onClick={() => { navigation(`/learning-management/curriculums-subscribed-tutorial/${curriculumIndex}/${tutorialIndex}`) }}
>
Start Tutorial
</Button>
......
import { useState } from 'react';
import { useNavigate } from 'react-router';
import { useEffect, useState } from 'react';
// material-ui
import {
Box,
Button,
CircularProgress,
Grid,
Typography
} from '@mui/material';
......@@ -15,8 +15,12 @@ import {
import MainCard from 'components/MainCard';
// assets
import { PlusOutlined, SendOutlined } from '@ant-design/icons';
import { MinusOutlined, PlusOutlined, SendOutlined } from '@ant-design/icons';
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 Animation from './Animation';
import CurriculumPreview from './CurriculumPreview';
......@@ -26,7 +30,9 @@ import CurriculumPreview from './CurriculumPreview';
// ==============================|| CURRICULUM - CARD ||============================== //
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);
......@@ -40,6 +46,109 @@ const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => {
const [desc, setDesc] = useState(curriculum.curriculumDescription?.slice(0, 100))
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 (
<>
......@@ -74,26 +183,33 @@ const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => {
</Typography>
</Grid>
<Grid item xs={6}>
<Box sx={{ display: 'inline-block' }}>
<Box sx={{ display: 'inline-block', width: "100%" }}>
<AnimateButton>
<Button
fullWidth
size='small'
variant="outlined"
endIcon={<PlusOutlined />}
endIcon={isSubscribed ? <MinusOutlined /> : <PlusOutlined />}
sx={{ my: 2, width: "100%" }}
onClick={() => { navigate(`/learning-management/curriculums-subscribed`) }}
color='success'
onClick={() => {
if (!isSubscribed) {
FollowCurriculum()
}
}}
color={isSubscribed ? 'secondary' : 'success'}
disabled={isLoading}
>
Follow Curriculum
{isLoading ? <CircularProgress /> : <>{isSubscribed ? 'Un Follow Curriculum' : 'Follow Curriculum'} </>}
</Button>
</AnimateButton>
</Box>
</Grid>
<Grid item xs={6}>
<Box sx={{ display: 'inline-block' }}>
<Box sx={{ display: 'inline-block', width: "100%" }}>
<AnimateButton>
<Button
fullWidth
size='small'
variant="outlined"
endIcon={<SendOutlined />}
sx={{ my: 2, width: "100%" }}
......
import { useState } from 'react';
import { useEffect, useState } from 'react';
// material-ui
import {
......@@ -33,6 +33,12 @@ import IconButton from 'components/@extended/IconButton';
import { DeleteFilled } from '@ant-design/icons';
import MainCard from 'components/MainCard';
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';
// types
......@@ -60,25 +66,13 @@ const getInitialValues = (curriculum: FormikValues | null) => {
// ==============================|| CUSTOMER ADD / EDIT ||============================== //
export interface Props {
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;
};
curriculum: Curriculum;
onCancel: () => void;
}
const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
const theme = useTheme();
const { user } = useAuth();
const isCreating = !curriculum;
......@@ -96,11 +90,26 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
validationSchema: CurriculumSchema,
enableReinitialize: true,
onSubmit: (values, { setSubmitting, resetForm }) => {
try {
if (curriculum) {
// PUT API
} else {
// 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()
setSubmitting(false);
......@@ -114,6 +123,16 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
const { errors, touched, handleSubmit, isSubmitting, getFieldProps, values } = formik;
// const { handleSubmit, isSubmitting } = formik;
const { tutorials } = useSelector(state => state.tutorial);
/**
* API Config
* Tutorial API
*/
useEffect(() => {
dispatch(fetchTutorials());
}, [dispatch])
return (
<>
<FormikProvider value={formik}>
......@@ -238,12 +257,12 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
<Autocomplete
fullWidth
id={`tutorials.${index}`}
// value={tutorials.find((option) => option.id === formik.values.curriculumLevel) || null}
// onChange={(event: any, newValue: CurriculumLevelsType | null) => {
// formik.setFieldValue(`tutorials.${index}`, newValue?.id);
// }}
options={[]}
// getOptionLabel={(item) => `${item.description}`}
value={tutorials.find((option) => option._id === formik.values.tutorials[index]) || null}
onChange={(event: any, newValue: Tutorial | null) => {
formik.setFieldValue(`tutorials.${index}`, newValue?._id);
}}
options={tutorials}
getOptionLabel={(item) => `${item.tutorialTitle}`}
renderInput={(params) => {
return (
<TextField
......
......@@ -7,20 +7,22 @@ import { PopupTransition } from 'components/@extended/Transitions';
// assets
import { DeleteFilled } from '@ant-design/icons';
import { useDispatch } from 'store';
import { deleteCurriculum } from 'store/reducers/curriculum';
// types
interface Props {
title: string;
open: boolean;
handleClose: (status: boolean) => void;
deleteId: number | string | undefined;
deleteId: string | undefined;
}
// ==============================|| Curriculum - DELETE ||============================== //
export default function AlertCurriculumDelete({ title, open, handleClose, deleteId }: Props) {
// const dispatch = useDispatch();
const dispatch = useDispatch();
return (
<Dialog
......@@ -41,6 +43,14 @@ export default function AlertCurriculumDelete({ title, open, handleClose, delete
<Typography variant="h4" align="center">
Are you sure you want to delete?
</Typography>
<Typography align="center">
By deleting
<Typography variant="subtitle1" component="span">
{' '}
"{title}"{' '}
</Typography>
Curriculum, Its details & tasks will also be deleted.
</Typography>
</Stack>
<Stack direction="row" spacing={2} sx={{ width: 1 }}>
......@@ -48,7 +58,7 @@ export default function AlertCurriculumDelete({ title, open, handleClose, delete
Cancel
</Button>
<Button fullWidth color="error" variant="contained" onClick={() => {
// dispatch(deleteNutrition(deleteId!))
dispatch(deleteCurriculum(deleteId!))
handleClose(true)
}} autoFocus>
Delete
......
......@@ -7,20 +7,22 @@ import { PopupTransition } from 'components/@extended/Transitions';
// assets
import { DeleteFilled } from '@ant-design/icons';
import { useDispatch } from 'store';
import { deleteTutorial } from 'store/reducers/tutorial';
// types
interface Props {
title: string;
open: boolean;
handleClose: (status: boolean) => void;
deleteId: number | string | undefined;
deleteId: string | undefined;
}
// ==============================|| Tutorial - DELETE ||============================== //
export default function AlertTutorialDelete({ title, open, handleClose, deleteId }: Props) {
// const dispatch = useDispatch();
const dispatch = useDispatch();
return (
<Dialog
......@@ -41,6 +43,14 @@ export default function AlertTutorialDelete({ title, open, handleClose, deleteId
<Typography variant="h4" align="center">
Are you sure you want to delete?
</Typography>
<Typography align="center">
By deleting
<Typography variant="subtitle1" component="span">
{' '}
"{title}"{' '}
</Typography>
tutorial, Its details & tasks will also be deleted.
</Typography>
</Stack>
<Stack direction="row" spacing={2} sx={{ width: 1 }}>
......@@ -48,7 +58,7 @@ export default function AlertTutorialDelete({ title, open, handleClose, deleteId
Cancel
</Button>
<Button fullWidth color="error" variant="contained" onClick={() => {
// dispatch(deleteNutrition(deleteId!))
dispatch(deleteTutorial(deleteId!))
handleClose(true)
}} autoFocus>
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';
import calendar from './calendar';
import cartReducer from './cart';
import chat from './chat';
import curriculum from './curriculum';
import ingredient from './ingredient';
import invoice from './invoice';
import kanban from './kanban';
import marksCalculator from './marksCalculator';
import menu from './menu';
import nutrition from './nutrition';
import productReducer from './product';
import snackbar from './snackbar';
import subscription from './subscription';
import tutorial from './tutorial';
import user from './user';
import userProgress from './userProgress';
// ==============================|| COMBINE REDUCERS ||============================== //
......@@ -37,7 +41,12 @@ const reducers = combineReducers({
invoice,
nutrition,
ingredient,
subscription
subscription,
marksCalculator,
tutorial,
curriculum,
user,
userProgress
});
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) {
dispatch(slice.actions.startLoading());
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));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -123,7 +123,7 @@ export function fetchUser(id: number) {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/user/${id}`);
const response = await axiosServices.get(`/rest_node/user/${id}`);
dispatch(slice.actions.fetchUserSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -143,7 +143,7 @@ export function fetchUsers(userType: string) {
dispatch(slice.actions.startLoading());
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));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -162,7 +162,7 @@ export function fetchUsersByType() {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get('/user/all');
const response = await axiosServices.get('/rest_node/user/all');
dispatch(slice.actions.fetchUsersSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -182,7 +182,7 @@ export function updateUser(updatedUser: User) {
dispatch(slice.actions.startLoading());
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));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -202,7 +202,7 @@ export function deleteNutrition(userId: number) {
dispatch(slice.actions.startLoading());
try {
await axiosServices.delete(`/user/${userId}`);
await axiosServices.delete(`/rest_node/user/${userId}`);
dispatch(slice.actions.deleteUserSuccess(userId));
} catch (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"
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
curriculumCode: string
curriculumLevel: number
......@@ -14,4 +47,17 @@ export interface curriculumType {
createdAt: Date
updatedBy?: string
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 {
_id?: string
title: string,
description: string,
howToDo: string[],
referenceImage: string,
referenceVideo: string,
taskItemMark: number
description?: string,
howToDo?: string[],
referenceImage?: string,
referenceVideo?: string,
taskItemMark?: number
}
\ No newline at end of file
import { taskItemType } from "./taskItem"
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
tutorialCode?: string
tutorialTitle?: string
......@@ -13,4 +43,17 @@ export interface tutorialType {
createdAt: Date
updatedBy?: string
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 {
export interface curriculumTypeUserProgress {
curriculumCode: string
curriculumLevel: number
curriculumTitle: string
curriculumDescription: string
curriculumImage: string
curriculumMark: number
curriculumMarkUser: number
curriculumSpentTime: number
tutorials: tutorialTypeUserProgress[],
curriculumLevel?: number
curriculumTitle?: string
curriculumDescription?: string
curriculumImage?: string
curriculumMark?: number
curriculumMarkUser?: number
curriculumSpentTime?: number
tutorials?: tutorialTypeUserProgress[],
}
export interface tutorialTypeUserProgress {
tutorialCode?: string
tutorialCode: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMark: number
tutorialMarkUser: number
tutorialSpentTime: number
taskItems: taskItemTypeUserProgress[]
tutorialMarks?: number
tutorialMarkUser?: number
tutorialSpentTime?: number
taskItems?: taskItemTypeUserProgress[]
}
export interface taskItemTypeUserProgress {
title: string,
description: string,
howToDo: string[],
referenceImage: string,
referenceVideo: string,
taskItemMark: number
taskItemMarkUser: number
taskItemSpentTime: number
description?: string,
howToDo?: string[],
referenceImage?: string,
referenceVideo?: string,
taskItemMark?: number
taskItemMarkUser?: 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
### test
# Project Name: “Sign Language Translation with emotional base multi model,3D Avatar and Adaptive Learning.”
## 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