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) => { ...@@ -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>
......
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
// material-ui // material-ui
import { Alert, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Divider, Grid, LinearProgress, List, ListItemAvatar, ListItemButton, ListItemSecondaryAction, ListItemText, Stack, Typography } from "@mui/material"; import { Alert, Box, Button, ButtonGroup, Dialog, DialogActions, DialogContent, DialogTitle, Divider, Grid, LinearProgress, List, ListItemAvatar, ListItemButton, ListItemSecondaryAction, ListItemText, Stack, Typography } from "@mui/material";
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
// project import // project import
import AntAvatar from 'components/@extended/Avatar'; import AntAvatar from 'components/@extended/Avatar';
import MainCard from "components/MainCard"; import MainCard from "components/MainCard";
import UploadSingleFile from 'components/third-party/dropzone/SingleFile';
// types // types
import { tutorialTypeUserProgress } from "types/userProgress"; import { tutorialTypeUserProgress } from "types/userProgress";
// assets // assets
import { CheckCircleOutlined, CheckOutlined, ClockCircleOutlined, FileImageOutlined, FileMarkdownFilled, LeftOutlined, PlayCircleOutlined, RightOutlined, TrophyOutlined, UserOutlined, VideoCameraOutlined } from "@ant-design/icons"; import { CheckCircleOutlined, CheckOutlined, ClockCircleOutlined, FileMarkdownFilled, LeftOutlined, PlayCircleOutlined, RightOutlined, TrophyOutlined, UserOutlined } from "@ant-design/icons";
import { PopupTransition } from "components/@extended/Transitions"; import { PopupTransition } from "components/@extended/Transitions";
import ReportCard from "components/cards/statistics/ReportCard"; import ReportCard from "components/cards/statistics/ReportCard";
import { userProgress } from "data/userProgress"; import { itemResultProps, selectedCommonDataProps, selectedItemContentProps } from "./types/types";
import { selectedCommonDataProps, selectedItemContentProps } from "./types/types";
import CircularWithPath from "components/@extended/progress/CircularWithPath";
import useAuth from "hooks/useAuth";
import { useParams } from "react-router";
import Webcam from 'react-webcam'; import Webcam from 'react-webcam';
import { useDispatch, useSelector } from "store";
import { CalculateMarks, toInitialState } from "store/reducers/marksCalculator";
import { openSnackbar } from "store/reducers/snackbar";
import { fetchUserProgress, updateUserProgress, toInitialState as userProgressToInitialState } from "store/reducers/userProgress";
// action style // action style
const actionSX = { const actionSX = {
...@@ -42,6 +49,11 @@ function convertMinutesToHMS(minutes: number) { ...@@ -42,6 +49,11 @@ function convertMinutesToHMS(minutes: number) {
const Tutorial = () => { const Tutorial = () => {
const theme = useTheme(); const theme = useTheme();
const dispatch = useDispatch();
const { marksCalculator, error, success, isLoading } = useSelector(state => state.marksCalculator);
const { userProgress, error: errorUserProgress, isLoading: isLoadingUserProgress, success: successUserProgress } = useSelector(state => state.userProgress);
const { curriculumIndex, tutorialIndex } = useParams();
const { user } = useAuth()
const [data, setData] = useState<tutorialTypeUserProgress>() const [data, setData] = useState<tutorialTypeUserProgress>()
const [selectedItem, setSelectedItem] = useState<{ selectedCommonData: selectedCommonDataProps | null, backgroundColor: any | null }>({ const [selectedItem, setSelectedItem] = useState<{ selectedCommonData: selectedCommonDataProps | null, backgroundColor: any | null }>({
...@@ -49,11 +61,28 @@ const Tutorial = () => { ...@@ -49,11 +61,28 @@ const Tutorial = () => {
backgroundColor: 'white', // Initial background color backgroundColor: 'white', // Initial background color
}); });
const [selectedItemContent, setSelectedItemContent] = useState<selectedItemContentProps | null>(null) const [selectedItemContent, setSelectedItemContent] = useState<selectedItemContentProps | null>(null)
const [desc, setDesc] = useState("") const [desc, setDesc] = useState("")
const [readMore, setReadMore] = useState(false) const [readMore, setReadMore] = useState(false)
const [itemDesc, setItemDesc] = useState("") const [itemDesc, setItemDesc] = useState("")
const [itemReadMore, setItemReadMore] = useState(false) const [itemReadMore, setItemReadMore] = useState(false)
const [itemReferenceData, setItemReferenceData] = useState<"image" | "video">("image") const [itemReferenceData, setItemReferenceData] = useState<"image" | "video">("image")
const [itemPracticeReferenceData, setItemPracticeReferenceData] = useState<"capture" | "upload">("capture")
const [itemResult, setItemResult] = useState<itemResultProps>({
itemMarkUser: 0,
status: "Pending"
})
useEffect(() => {
if (!curriculumIndex || !tutorialIndex) return
if (userProgress) {
const selectedTutorial = userProgress?.curriculums?.[parseInt(curriculumIndex)]?.tutorials?.[parseInt(tutorialIndex)];
if (selectedTutorial) {
setData(selectedTutorial);
}
}
}, [userProgress]);
const handleItemClick = (item: selectedCommonDataProps, backgroundColor: any) => { const handleItemClick = (item: selectedCommonDataProps, backgroundColor: any) => {
setSelectedItem({ setSelectedItem({
...@@ -67,11 +96,6 @@ const Tutorial = () => { ...@@ -67,11 +96,6 @@ const Tutorial = () => {
}); });
}; };
useEffect(() => {
// Set your data here
setData(userProgress.curriculums![0].tutorials[0]);
}, []);
useEffect(() => { useEffect(() => {
setDesc(data?.tutorialDescription?.slice(0, 200)!) setDesc(data?.tutorialDescription?.slice(0, 200)!)
}, [data]) }, [data])
...@@ -84,7 +108,7 @@ const Tutorial = () => { ...@@ -84,7 +108,7 @@ const Tutorial = () => {
useEffect(() => { useEffect(() => {
// Filter data based on selectedItem.title // Filter data based on selectedItem.title
if (selectedItem && data) { if (selectedItem && data) {
const filteredItem = data.taskItems.find((item) => item.title === selectedItem.selectedCommonData?.title); const filteredItem = data?.taskItems!.find((item) => item.title === selectedItem.selectedCommonData?.title);
setSelectedItemContent({ setSelectedItemContent({
userId: selectedItem.selectedCommonData?.userId!, userId: selectedItem.selectedCommonData?.userId!,
curriculumCode: selectedItem.selectedCommonData?.curriculumCode!, curriculumCode: selectedItem.selectedCommonData?.curriculumCode!,
...@@ -96,13 +120,27 @@ const Tutorial = () => { ...@@ -96,13 +120,27 @@ const Tutorial = () => {
} }
}, [selectedItem]); }, [selectedItem]);
useEffect(() => {
setItemResult({
itemMarkUser: ((selectedItemContent?.taskItemMark! * parseInt(marksCalculator?.confidence!)) / 100)!,
status: marksCalculator?.status.toUpperCase()!
})
}, [marksCalculator])
//alert model //alert model
const [openModel, setOpenModel] = useState(false); const [openModel, setOpenModel] = useState(false);
const [modelOpenFor, setModelOpenFor] = useState<"Practice" | "Complete">("Practice")
const handleModelClose = () => { const handleModelClose = () => {
setOpenModel(!openModel); setOpenModel(!openModel);
}; };
const handleModelOpen = (modelFor: "Practice" | "Complete") => {
setModelOpenFor(modelFor)
setOpenModel(true);
};
// web cam // web cam
const webcamRef = useRef<Webcam>(null); const webcamRef = useRef<Webcam>(null);
const [capturedImage, setCapturedImage] = useState<string | null>(null); const [capturedImage, setCapturedImage] = useState<string | null>(null);
...@@ -114,6 +152,220 @@ const Tutorial = () => { ...@@ -114,6 +152,220 @@ const Tutorial = () => {
} }
}; };
// upload image
const [uploadImage, setUploadImage] = useState<any>();
const TaskTrigger = (uploadImage: any, imgData: any, title: string) => {
if (itemPracticeReferenceData == "capture") {
if (typeof imgData === 'string' && imgData.trim() !== '') {
const curriculumIndex: number = 1;
const tutorialIndex: number = 1;
const imageData: any = imgData; // Your image data
let targetClass: string = ""; // Your target class
switch (title) {
case "Learn Number One":
targetClass = "One";
break;
case "Learn Number Two":
targetClass = "Two";
break;
case "Learn Number Three":
targetClass = "Three";
break;
case "Learn Number Four":
targetClass = "Four";
break;
case "Learn Number Five":
targetClass = "Five";
break;
case "Learn Number Six":
targetClass = "Six";
break;
case "Learn Number Seven":
targetClass = "Seven";
break;
case "Learn Number Eight":
targetClass = "Eight";
break;
case "Learn Number Nine":
targetClass = "Nine";
break;
case "Learn Number Ten":
targetClass = "Ten";
break;
default:
targetClass = "Initial";
break;
}
// Assuming imageData is a base64-encoded image
const base64Image = imageData; // Replace with your actual base64 data
const byteCharacters = atob(base64Image.split(',')[1]);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: 'image/jpeg' });
const file = new File([blob], 'image.jpg', { type: 'image/jpeg' });
dispatch(CalculateMarks(curriculumIndex, tutorialIndex, file, targetClass));
} else {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: "Invalid Image Data",
variant: 'alert',
alert: {
color: 'warning'
},
close: true
})
);
}
} else if (itemPracticeReferenceData == "upload") {
const curriculumIndex: number = 1;
const tutorialIndex: number = 1;
const imageData: any = uploadImage[0]; // Your image data
let targetClass: string = ""; // Your target class
switch (title) {
case "Learn Number One":
targetClass = "One";
break;
case "Learn Number Two":
targetClass = "Two";
break;
case "Learn Number Three":
targetClass = "Three";
break;
case "Learn Number Four":
targetClass = "Four";
break;
case "Learn Number Five":
targetClass = "Five";
break;
case "Learn Number Six":
targetClass = "Six";
break;
case "Learn Number Seven":
targetClass = "Seven";
break;
case "Learn Number Eight":
targetClass = "Eight";
break;
case "Learn Number Nine":
targetClass = "Nine";
break;
case "Learn Number Ten":
targetClass = "Ten";
break;
default:
targetClass = "Initial";
break;
}
dispatch(CalculateMarks(curriculumIndex, tutorialIndex, imageData, targetClass));
}
}
/**
* API Config
* Marks Calculator API
*/
// 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
})
);
if (modelOpenFor == "Complete") {
dispatch(updateUserProgress(
user?.id!,
userProgress?.curriculums?.[parseInt(curriculumIndex!)].curriculumCode!,
data?.tutorialCode!,
selectedItemContent?.title!,
((selectedItemContent?.taskItemMark! * parseInt(marksCalculator?.confidence!)) / 100)!,
0
))
}
dispatch(toInitialState())
}
}, [success])
// handel error
useEffect(() => {
if (errorUserProgress != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: errorUserProgress ? errorUserProgress.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(userProgressToInitialState())
}
}, [errorUserProgress])
// handel success
useEffect(() => {
if (successUserProgress != null) {
dispatch(
openSnackbar({
open: true,
message: successUserProgress,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(fetchUserProgress(user?.id!));
dispatch(userProgressToInitialState())
}
}, [successUserProgress])
return ( return (
<> <>
<Grid container spacing={2}> <Grid container spacing={2}>
...@@ -146,10 +398,10 @@ const Tutorial = () => { ...@@ -146,10 +398,10 @@ const Tutorial = () => {
</span> </span>
</Typography> </Typography>
<Typography variant="h4" sx={{ pt: 3, pb: 1, zIndex: 1 }}> <Typography variant="h4" sx={{ pt: 3, pb: 1, zIndex: 1 }}>
{(data?.tutorialMarkUser! / data?.tutorialMark!) * 100}% Completed {(data?.tutorialMarkUser! / data?.tutorialMarks!) * 100}% Completed
</Typography> </Typography>
<Box sx={{ maxWidth: '60%' }}> <Box sx={{ maxWidth: '60%' }}>
<LinearProgress variant="determinate" color="success" value={(data?.tutorialMarkUser! / data?.tutorialMark!) * 100} /> <LinearProgress variant="determinate" color="success" value={(data?.tutorialMarkUser! / data?.tutorialMarks!) * 100} />
</Box> </Box>
</Stack> </Stack>
</Grid> </Grid>
...@@ -182,7 +434,7 @@ const Tutorial = () => { ...@@ -182,7 +434,7 @@ const Tutorial = () => {
} }
}} }}
> >
{data?.taskItems.map((item, index) => { {data?.taskItems?.map((item, index) => {
const isSelected = selectedItem.selectedCommonData?.title === item.title; const isSelected = selectedItem.selectedCommonData?.title === item.title;
const backgroundColor = isSelected ? theme.palette.primary.lighter : 'white'; const backgroundColor = isSelected ? theme.palette.primary.lighter : 'white';
const iconColor = isSelected ? 'warning' : 'success'; const iconColor = isSelected ? 'warning' : 'success';
...@@ -280,29 +532,11 @@ const Tutorial = () => { ...@@ -280,29 +532,11 @@ const Tutorial = () => {
</Grid> </Grid>
<Grid item xs={4}> <Grid item xs={4}>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item md={6}> <Grid item md={12}>
<Button <ButtonGroup aria-label="outlined button group" sx={{ width: "100%" }}>
variant="outlined" <Button fullWidth sx={{ width: "100%" }} size="small" variant={itemReferenceData == "image" ? "contained" : "outlined"} onClick={() => { setItemReferenceData("image") }}>Image Reference</Button>
endIcon={<FileImageOutlined />} <Button fullWidth sx={{ width: "100%" }} size="small" variant={itemReferenceData == "video" ? "contained" : "outlined"} onClick={() => { setItemReferenceData("video") }}>Vide Reference</Button>
sx={{ my: 2 }} </ButtonGroup>
onClick={() => { setItemReferenceData("image") }}
color="success"
size="small"
>
Image Reference
</Button>
</Grid>
<Grid item md={6}>
<Button
variant="outlined"
endIcon={<VideoCameraOutlined />}
sx={{ my: 2 }}
onClick={() => { setItemReferenceData("video") }}
color="warning"
size="small"
>
Vide Reference
</Button>
</Grid> </Grid>
<Grid item md={12} xs={4} sx={{ height: '246px', '& img': { mb: 0, width: '100%', height: '100%', objectFit: 'contain' } }}> <Grid item md={12} xs={4} sx={{ height: '246px', '& img': { mb: 0, width: '100%', height: '100%', objectFit: 'contain' } }}>
{itemReferenceData == "image" && <img src={selectedItemContent?.referenceImage} alt="feature" style={{ width: '100%', height: '100%' }} />} {itemReferenceData == "image" && <img src={selectedItemContent?.referenceImage} alt="feature" style={{ width: '100%', height: '100%' }} />}
...@@ -330,7 +564,7 @@ const Tutorial = () => { ...@@ -330,7 +564,7 @@ const Tutorial = () => {
</Grid> </Grid>
<Grid item md={12}> <Grid item md={12}>
<ReportCard <ReportCard
primary={`Pass Mark : ${(selectedItemContent.taskItemMark * (85 / 100))?.toLocaleString('en-US', { primary={`Pass Mark : ${(selectedItemContent?.taskItemMark! * (85 / 100))?.toLocaleString('en-US', {
minimumFractionDigits: 2, minimumFractionDigits: 2,
maximumFractionDigits: 2, maximumFractionDigits: 2,
})}`} })}`}
...@@ -347,7 +581,7 @@ const Tutorial = () => { ...@@ -347,7 +581,7 @@ const Tutorial = () => {
endIcon={<PlayCircleOutlined />} endIcon={<PlayCircleOutlined />}
fullWidth fullWidth
sx={{ my: 1, width: "100%", height: "100%" }} sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { setOpenModel(true) }} onClick={() => { handleModelOpen("Practice") }}
color="warning" color="warning"
size="small" size="small"
> >
...@@ -360,7 +594,7 @@ const Tutorial = () => { ...@@ -360,7 +594,7 @@ const Tutorial = () => {
endIcon={<CheckOutlined />} endIcon={<CheckOutlined />}
fullWidth fullWidth
sx={{ my: 1, width: "100%", height: "100%" }} sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { }} onClick={() => { handleModelOpen("Complete") }}
color="success" color="success"
size="small" size="small"
> >
...@@ -415,28 +649,46 @@ const Tutorial = () => { ...@@ -415,28 +649,46 @@ const Tutorial = () => {
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"
> >
<DialogTitle>Practice</DialogTitle> <DialogTitle>{modelOpenFor} Item</DialogTitle>
<Divider /> <Divider />
<DialogContent sx={{ p: 2.5 }}> <DialogContent sx={{ p: 2.5 }}>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item md={8}> <Grid item md={8}>
<MainCard title="Capture / Upload Image"> <MainCard title="Capture / Upload Image">
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item md={6}> <Grid item md={12}>
<Webcam <ButtonGroup aria-label="outlined button group" >
audio={false} <Button fullWidth sx={{ whiteSpace: "nowrap" }} size="small" variant={itemPracticeReferenceData == "capture" ? "contained" : "outlined"} onClick={() => { setItemPracticeReferenceData("capture") }}>Capture Image</Button>
ref={webcamRef} <Button fullWidth sx={{ whiteSpace: "nowrap" }} size="small" variant={itemPracticeReferenceData == "upload" ? "contained" : "outlined"} onClick={() => { setItemPracticeReferenceData("upload") }}>Upload Image</Button>
screenshotFormat="image/jpeg" </ButtonGroup>
height={300}
width={400}
// videoConstraints={videoConstraints}
/>
</Grid>
<Grid item md={6}>
{capturedImage && (
<img src={capturedImage} alt="Captured" style={{ width: 400, height: 300 }} />
)}
</Grid> </Grid>
{itemPracticeReferenceData == "capture" && <>
<Grid item md={6}>
<Webcam
audio={false}
ref={webcamRef}
screenshotFormat="image/jpeg"
height={300}
width={400}
// videoConstraints={videoConstraints}
/>
</Grid>
<Grid item md={6}>
{capturedImage && (
<img src={capturedImage} alt="Captured" style={{ width: 400, height: 300 }} />
)}
</Grid>
</>}
{itemPracticeReferenceData == "upload" && <>
<Grid item md={12}>
<UploadSingleFile
file={uploadImage}
setFieldValue={function (field: string, value: any): void {
setUploadImage(value)
}}
/>
</Grid>
</>}
</Grid> </Grid>
</MainCard> </MainCard>
</Grid> </Grid>
...@@ -445,26 +697,27 @@ const Tutorial = () => { ...@@ -445,26 +697,27 @@ const Tutorial = () => {
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item md={12}> <Grid item md={12}>
<ReportCard <ReportCard
primary={`Result : N/A`} primary={`Result : ${itemResult.itemMarkUser ? itemResult.itemMarkUser : "N/A"}`}
secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} /> secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} />
</Grid> </Grid>
<Grid item md={12}> <Grid item md={12}>
<ReportCard <ReportCard
primary={`Status : N/A`} primary={`Status : ${itemResult.status ? itemResult.status : 'N/A'}`}
secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} /> secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} />
</Grid> </Grid>
<Grid item md={12}> <Grid item md={12}>
<Button variant="contained" color="success" onClick={() => { }} fullWidth> <Button variant="contained" color="success" onClick={() => { TaskTrigger(uploadImage, webcamRef.current?.getScreenshot(), selectedItemContent?.title!) }} fullWidth disabled={isLoading && isLoadingUserProgress}>
Get Result {isLoading && isLoadingUserProgress ? <CircularWithPath /> : <>{modelOpenFor} Task</>}
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>
</MainCard> </MainCard>
</Grid> </Grid>
<Grid item md={8}> <Grid item md={8}>
<Button size="small" variant="contained" onClick={capture} fullWidth> {itemPracticeReferenceData == "capture" && <Button size="small" variant="contained" onClick={capture} fullWidth>
Capture Capture
</Button> </Button>
}
</Grid> </Grid>
<Grid item md={4} /> <Grid item md={4} />
</Grid> </Grid>
......
...@@ -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 { MouseEvent, useMemo, useState } from 'react'; import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui // material-ui
import { import {
...@@ -34,9 +34,14 @@ import { ...@@ -34,9 +34,14 @@ import {
import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-design/icons'; import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-design/icons';
//types //types
import status from 'data/status';
import AddEditTutorial from 'sections/parameters/tutorial-management/AddEditTutorial'; import AddEditTutorial from 'sections/parameters/tutorial-management/AddEditTutorial';
import AlertTutorialDelete from 'sections/parameters/tutorial-management/AlertTutorialDelete'; import AlertTutorialDelete from 'sections/parameters/tutorial-management/AlertTutorialDelete';
import { ReactTableProps, dataProps, tutorialProps } from './types/types'; import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { fetchTutorials, toInitialState } from 'store/reducers/tutorial';
import { Tutorial, Tutorials } from 'types/tutorial';
import { ReactTableProps } from './types/types';
// ==============================|| REACT TABLE ||============================== // // ==============================|| REACT TABLE ||============================== //
...@@ -122,152 +127,158 @@ function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) { ...@@ -122,152 +127,158 @@ function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
// ==============================|| List ||============================== // // ==============================|| List ||============================== //
const List = () => { const List = () => {
const theme = useTheme(); const theme = useTheme();
const dispatch = useDispatch();
const { tutorials, error, success, isLoading } = useSelector(state => state.tutorial);
// table // table
const data: dataProps[] = [ // const data: dataProps[] = [
{ // {
"_id": "", // "_id": "",
"tutorialCode": "01", // "tutorialCode": "01",
"tutorialTitle": "Numbers and Counting in Sign Language", // "tutorialTitle": "Numbers and Counting in Sign Language",
"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=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc", // "tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"taskItems": [ // "taskItems": [
{ // {
"_id": "", // "_id": "",
"title": "Learn Number One", // "title": "Learn Number One",
"description": "Learn how to sign the number one in sign language.", // "description": "Learn how to sign the number one in sign language.",
"howToDo": "- Extend your index finger straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend your index finger straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_one.jpg", // "referenceImage": "https://example.com/number_one.jpg",
"referenceVideo": "https://example.com/number_one_video.mp4", // "referenceVideo": "https://example.com/number_one_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Two", // "title": "Learn Number Two",
"description": "Learn how to sign the number two in sign language.", // "description": "Learn how to sign the number two in sign language.",
"howToDo": "- Extend your index and middle fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend your index and middle fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_two.jpg", // "referenceImage": "https://example.com/number_two.jpg",
"referenceVideo": "https://example.com/number_two_video.mp4", // "referenceVideo": "https://example.com/number_two_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Three", // "title": "Learn Number Three",
"description": "Learn how to sign the number three in sign language.", // "description": "Learn how to sign the number three in sign language.",
"howToDo": "- Extend your index, middle, and ring fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend your index, middle, and ring fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_three.jpg", // "referenceImage": "https://example.com/number_three.jpg",
"referenceVideo": "https://example.com/number_three_video.mp4", // "referenceVideo": "https://example.com/number_three_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Four", // "title": "Learn Number Four",
"description": "Learn how to sign the number four in sign language.", // "description": "Learn how to sign the number four in sign language.",
"howToDo": "- Extend your thumb, index, middle, and ring fingers straight up.\n- Keep your pinky finger folded.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend your thumb, index, middle, and ring fingers straight up.\n- Keep your pinky finger folded.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_four.jpg", // "referenceImage": "https://example.com/number_four.jpg",
"referenceVideo": "https://example.com/number_four_video.mp4", // "referenceVideo": "https://example.com/number_four_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Five", // "title": "Learn Number Five",
"description": "Learn how to sign the number five in sign language.", // "description": "Learn how to sign the number five in sign language.",
"howToDo": "- Extend all your fingers straight up.\n- Keep your thumb resting on the side of your palm.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend all your fingers straight up.\n- Keep your thumb resting on the side of your palm.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_five.jpg", // "referenceImage": "https://example.com/number_five.jpg",
"referenceVideo": "https://example.com/number_five_video.mp4", // "referenceVideo": "https://example.com/number_five_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Six", // "title": "Learn Number Six",
"description": "Learn how to sign the number six in sign language.", // "description": "Learn how to sign the number six in sign language.",
"howToDo": "- Extend your thumb and pinky finger straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend your thumb and pinky finger straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_six.jpg", // "referenceImage": "https://example.com/number_six.jpg",
"referenceVideo": "https://example.com/number_six_video.mp4", // "referenceVideo": "https://example.com/number_six_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Seven", // "title": "Learn Number Seven",
"description": "Learn how to sign the number seven in sign language.", // "description": "Learn how to sign the number seven in sign language.",
"howToDo": "- Extend your index, middle, and ring fingers straight up.\n- Keep your thumb, pinky, and pinky finger folded.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend your index, middle, and ring fingers straight up.\n- Keep your thumb, pinky, and pinky finger folded.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_seven.jpg", // "referenceImage": "https://example.com/number_seven.jpg",
"referenceVideo": "https://example.com/number_seven_video.mp4", // "referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Eight", // "title": "Learn Number Eight",
"description": "Learn how to sign the number eight in sign language.", // "description": "Learn how to sign the number eight in sign language.",
"howToDo": "- Extend all your fingers straight up.\n- Cross your index and middle fingers over your ring and pinky fingers.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend all your fingers straight up.\n- Cross your index and middle fingers over your ring and pinky fingers.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_eight.jpg", // "referenceImage": "https://example.com/number_eight.jpg",
"referenceVideo": "https://example.com/number_eight_video.mp4", // "referenceVideo": "https://example.com/number_eight_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Nine", // "title": "Learn Number Nine",
"description": "Learn how to sign the number nine in sign language.", // "description": "Learn how to sign the number nine in sign language.",
"howToDo": "- Extend your thumb and all your fingers straight up.\n- Keep your pinky finger folded.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend your thumb and all your fingers straight up.\n- Keep your pinky finger folded.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_nine.jpg", // "referenceImage": "https://example.com/number_nine.jpg",
"referenceVideo": "https://example.com/number_nine_video.mp4", // "referenceVideo": "https://example.com/number_nine_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
}, // },
{ // {
"_id": "", // "_id": "",
"title": "Learn Number Ten", // "title": "Learn Number Ten",
"description": "Learn how to sign the number ten in sign language.", // "description": "Learn how to sign the number ten in sign language.",
"howToDo": "- Extend your thumb, index, and middle fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.", // "howToDo": "- Extend your thumb, index, and middle fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_ten.jpg", // "referenceImage": "https://example.com/number_ten.jpg",
"referenceVideo": "https://example.com/number_ten_video.mp4", // "referenceVideo": "https://example.com/number_ten_video.mp4",
"taskItemMark": 10 // "taskItemMark": 10
} // }
], // ],
"status": 1, // "status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"), // "createdAt": new Date("2023-08-30T12:00:00Z"),
"createdBy": "Nuwan Gamage" // "createdBy": "Nuwan Gamage"
}, // },
{ // {
"_id": "", // "_id": "",
"tutorialCode": "02", // "tutorialCode": "02",
"tutorialTitle": "Everyday Vocabulary in Sign Language", // "tutorialTitle": "Everyday Vocabulary in Sign Language",
"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=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc", // "tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"taskItems": [], // "taskItems": [],
"status": 1, // "status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"), // "createdAt": new Date("2023-08-30T12:00:00Z"),
"createdBy": "Nuwan Gamage" // "createdBy": "Nuwan Gamage"
}, // },
{ // {
"_id": "", // "_id": "",
"tutorialCode": "03", // "tutorialCode": "03",
"tutorialTitle": "Family Signs in Sign Language", // "tutorialTitle": "Family Signs in Sign Language",
"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=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc", // "tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"taskItems": [], // "taskItems": [],
"status": 1, // "status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"), // "createdAt": new Date("2023-08-30T12:00:00Z"),
"createdBy": "Nuwan Gamage" // "createdBy": "Nuwan Gamage"
}, // },
{ // {
"_id": "", // "_id": "",
"tutorialCode": "04", // "tutorialCode": "04",
"tutorialTitle": "Basic Conversational Phrases in Sign Language", // "tutorialTitle": "Basic Conversational Phrases in Sign Language",
"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=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc", // "tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"taskItems": [], // "taskItems": [],
"status": 1, // "status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"), // "createdAt": new Date("2023-08-30T12:00:00Z"),
"createdBy": "Nuwan Gamage" // "createdBy": "Nuwan Gamage"
} // }
] // ]
// table
const [data, setData] = useState<Tutorials[]>([])
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 === '') {
...@@ -293,11 +304,38 @@ const List = () => { ...@@ -293,11 +304,38 @@ const List = () => {
}, },
{ {
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",
...@@ -340,6 +378,7 @@ const List = () => { ...@@ -340,6 +378,7 @@ const List = () => {
color="error" color="error"
onClick={(e: MouseEvent<HTMLButtonElement>) => { onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation(); e.stopPropagation();
setTutorialTitle(row.values.tutorialTitle)
setTutorialId(row.values._id) setTutorialId(row.values._id)
setOpenAlert(true) setOpenAlert(true)
}} }}
...@@ -358,7 +397,7 @@ const List = () => { ...@@ -358,7 +397,7 @@ const List = () => {
//dialog model //dialog model
const [addEdit, setAddEdit] = useState<boolean>(false); const [addEdit, setAddEdit] = useState<boolean>(false);
const [tutorial, setTutorial] = useState<tutorialProps>(); const [tutorial, setTutorial] = useState<Tutorial>();
const handleAddEdit = () => { const handleAddEdit = () => {
setAddEdit(!addEdit); setAddEdit(!addEdit);
...@@ -367,12 +406,66 @@ const List = () => { ...@@ -367,12 +406,66 @@ const List = () => {
//alert model //alert model
const [openAlert, setOpenAlert] = useState(false); const [openAlert, setOpenAlert] = useState(false);
const [tutorialId, setTutorialId] = useState<number | string | undefined>(undefined) const [tutorialId, setTutorialId] = useState<string | undefined>(undefined)
const [tutorialTitle, setTutorialTitle] = useState<string | undefined>(undefined)
const handleAlertClose = () => { const handleAlertClose = () => {
setOpenAlert(!openAlert); setOpenAlert(!openAlert);
}; };
/**
* API Config
* Tutorial API
*/
useEffect(() => {
dispatch(fetchTutorials());
}, [dispatch]);
useEffect(() => {
setData(tutorials);
}, [tutorials])
// 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}>
...@@ -390,10 +483,10 @@ const List = () => { ...@@ -390,10 +483,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"
> >
<AddEditTutorial tutorial={tutorial} onCancel={handleAddEdit} /> <AddEditTutorial tutorial={tutorial!} onCancel={handleAddEdit} />
</Dialog> </Dialog>
{/* alert model */} {/* alert model */}
{!tutorial && <AlertTutorialDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={tutorialId} />} {!tutorial && <AlertTutorialDelete title={tutorialTitle!} open={openAlert} handleClose={handleAlertClose} deleteId={tutorialId} />}
</MainCard> </MainCard>
</> </>
) )
......
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
......
...@@ -32,6 +32,10 @@ import IconButton from 'components/@extended/IconButton'; ...@@ -32,6 +32,10 @@ import IconButton from 'components/@extended/IconButton';
import { DeleteFilled } from '@ant-design/icons'; import { DeleteFilled } from '@ant-design/icons';
import { AccordionSummary, InputLabel, TextField } from '@mui/material'; import { AccordionSummary, InputLabel, TextField } from '@mui/material';
import MainCard from 'components/MainCard'; import MainCard from 'components/MainCard';
import useAuth from 'hooks/useAuth';
import { dispatch } from 'store';
import { addTutorial } from 'store/reducers/tutorial';
import { Tutorial } from 'types/tutorial';
import AlertTutorialDelete from './AlertTutorialDelete'; import AlertTutorialDelete from './AlertTutorialDelete';
// types // types
...@@ -53,6 +57,7 @@ const getInitialValues = (tutorial: FormikValues | null) => { ...@@ -53,6 +57,7 @@ const getInitialValues = (tutorial: FormikValues | null) => {
howToDo: "", howToDo: "",
referenceImage: "", referenceImage: "",
referenceVideo: "", referenceVideo: "",
taskItemMark: 0,
} }
] ]
} }
...@@ -67,37 +72,37 @@ const getInitialValues = (tutorial: FormikValues | null) => { ...@@ -67,37 +72,37 @@ const getInitialValues = (tutorial: FormikValues | null) => {
// ==============================|| CUSTOMER ADD / EDIT ||============================== // // ==============================|| CUSTOMER ADD / EDIT ||============================== //
export interface Props { export interface Props {
tutorial?: { tutorial?: Tutorial
_id: number | string | undefined
tutorialCode: string;
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
taskItems: taskItemProps[]
};
onCancel: () => void; onCancel: () => void;
} }
export interface taskItemProps {
_id: number | string | undefined;
title: string;
description: string;
howToDo: string;
referenceImage: string;
referenceVideo: string;
}
const AddEditTutorial = ({ tutorial, onCancel }: Props) => { const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
const theme = useTheme(); const theme = useTheme();
const { user } = useAuth();
const isCreating = !tutorial; const isCreating = !tutorial;
const TutorialSchema = Yup.object().shape({}); const TutorialSchema = Yup.object().shape({
tutorialCode: Yup.string().required(`Tutorial Code is required`),
tutorialTitle: Yup.string().required(`Tutorial Title is required`),
tutorialDescription: Yup.string().required(`Tutorial Description is required`),
tutorialImage: Yup.string().required(`Tutorial Image Ref is required`),
taskItems: Yup.array().of(
Yup.object().shape({
title: Yup.string().required(`Task Title is required`),
description: Yup.string().required(`Task Description is required`),
howToDo: Yup.string().required(`Task How To Do is required`),
referenceImage: Yup.string().required(`Task Reference Image is required`),
referenceVideo: Yup.string(),
taskItemMark: Yup.number().required(`Task Mark is required`).min(1, 'Task Mark must be greater than 0'),
})
).required(`Tutorial Task Item is required`),
status: Yup.number(),
createdBy: Yup.string(),
createdAt: Yup.date(),
updatedBy: Yup.string(),
updatedAt: Yup.date(),
});
const [openAlert, setOpenAlert] = useState(false); const [openAlert, setOpenAlert] = useState(false);
...@@ -115,7 +120,23 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => { ...@@ -115,7 +120,23 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
if (tutorial) { if (tutorial) {
// PUT API // PUT API
} else { } else {
// POST API // POST API
// const dummyObject: Tutorial = tutorialReqOb
// dispatch(addTutorial(dummyObject))
const req: Tutorial = {
tutorialCode: values.tutorialCode,
tutorialTitle: values.tutorialTitle,
tutorialImage: values.tutorialImage,
tutorialDescription: values.tutorialDescription,
taskItems: values.taskItems.map((item) => ({
...item,
howToDo: item.howToDo.split(','), // Split the comma-separated string into an array
})),
createdBy: user?.name!,
};
dispatch(addTutorial(req))
} }
resetForm() resetForm()
setSubmitting(false); setSubmitting(false);
...@@ -266,7 +287,7 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => { ...@@ -266,7 +287,7 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={12}> <Grid item xs={6}>
<Stack spacing={1.25}> <Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.title`}>Task Title</InputLabel> <InputLabel htmlFor={`taskItems.${index}.title`}>Task Title</InputLabel>
<TextField <TextField
...@@ -274,8 +295,37 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => { ...@@ -274,8 +295,37 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.title`} id={`taskItems.${index}.title`}
placeholder="Enter Task Title" placeholder="Enter Task Title"
{...getFieldProps(`taskItems.${index}.title`)} {...getFieldProps(`taskItems.${index}.title`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)} error={Boolean(
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory} touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].title
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].title
}
/>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.taskItemMark`}>Task Mark</InputLabel>
<TextField
fullWidth
id={`taskItems.${index}.taskItemMark`}
placeholder="Enter Task Mark"
{...getFieldProps(`taskItems.${index}.taskItemMark`)}
error={Boolean(
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].taskItemMark
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].taskItemMark
}
/> />
</Stack> </Stack>
</Grid> </Grid>
...@@ -287,8 +337,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => { ...@@ -287,8 +337,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.referenceImage`} id={`taskItems.${index}.referenceImage`}
placeholder="Enter Task Reference Image" placeholder="Enter Task Reference Image"
{...getFieldProps(`taskItems.${index}.referenceImage`)} {...getFieldProps(`taskItems.${index}.referenceImage`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)} error={Boolean(
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory} touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].referenceImage
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].referenceImage
}
/> />
</Stack> </Stack>
</Grid> </Grid>
...@@ -300,8 +358,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => { ...@@ -300,8 +358,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.referenceVideo`} id={`taskItems.${index}.referenceVideo`}
placeholder="Enter Task Reference Video" placeholder="Enter Task Reference Video"
{...getFieldProps(`taskItems.${index}.referenceVideo`)} {...getFieldProps(`taskItems.${index}.referenceVideo`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)} error={Boolean(
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory} touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].referenceVideo
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].referenceVideo
}
/> />
</Stack> </Stack>
</Grid> </Grid>
...@@ -313,8 +379,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => { ...@@ -313,8 +379,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.description`} id={`taskItems.${index}.description`}
placeholder="Enter Task Description" placeholder="Enter Task Description"
{...getFieldProps(`taskItems.${index}.description`)} {...getFieldProps(`taskItems.${index}.description`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)} error={Boolean(
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory} touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].description
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].description
}
/> />
</Stack> </Stack>
</Grid> </Grid>
...@@ -326,8 +400,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => { ...@@ -326,8 +400,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.howToDo`} id={`taskItems.${index}.howToDo`}
placeholder="Enter Task How To Do" placeholder="Enter Task How To Do"
{...getFieldProps(`taskItems.${index}.howToDo`)} {...getFieldProps(`taskItems.${index}.howToDo`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)} error={Boolean(
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory} touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].howToDo
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].howToDo
}
/> />
</Stack> </Stack>
</Grid> </Grid>
......
...@@ -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