Commit d073333a authored by Gamage B.G.J's avatar Gamage B.G.J

Merge branch 'fix/project-re-structure' into 'master'

Fix/project re structure

See merge request !11
parents cbd507de d77b6044
{
"cSpell.words": [
"Janith",
"leaderboard",
"SLIIT"
]
}
\ No newline at end of file
import Curriculum from '../models/curriculum.model.js';
export const getAllCurriculums = async (req, res) => {
try {
const curriculums = await Curriculum.find();
res.status(200).json(curriculums);
} catch (error) {
res.status(500).json({ message: error.message });
}
}
export const getCurriculumById = async (req, res) => {
const { id } = req.params;
try {
const curriculum = await Curriculum.findById(id);
res.status(200).json(curriculum);
} catch (error) {
res.status(404).json({ message: 'Curriculum not found' });
}
}
export const createCurriculum = async (req, res) => {
const curriculumData = req.body;
try {
const newCurriculum = new Curriculum(curriculumData);
await newCurriculum.save();
res.status(201).json(newCurriculum);
} catch (error) {
res.status(400).json({ message: error.message });
}
}
export const updateCurriculum = async (req, res) => {
const { id } = req.params;
const updatedCurriculum = req.body;
try {
const result = await Curriculum.findByIdAndUpdate(id, updatedCurriculum, { new: true });
res.status(200).json(result);
} catch (error) {
res.status(404).json({ message: 'Curriculum not found' });
}
}
export const deleteCurriculum = async (req, res) => {
const { id } = req.params;
try {
await Curriculum.findByIdAndDelete(id);
res.status(200).json({ message: 'Curriculum deleted successfully' });
} catch (error) {
res.status(404).json({ message: 'Curriculum not found' });
}
}
import Feedback from '../models/feedback.model.js';
export const createFeedback = async (req, res) => {
const { userId, entityId, rating, comment } = req.body;
try {
const feedback = new Feedback({
userId,
entityId,
rating,
comment
});
await feedback.save();
res.status(201).json({ code: '01', message: 'Feedback submitted successfully' });
} catch (error) {
res.status(500).json({ code: '00', message: 'Error submitting feedback' });
}
}
export const getFeedbackForEntity = async (req, res) => {
const entityId = req.params.entityId;
try {
const feedback = await Feedback.find({ entityId }).populate('userId', 'firstName lastName');
res.status(200).json(feedback);
} catch (error) {
res.status(500).json({ code: '00', message: 'Error fetching feedback' });
}
}
import UserProgress from '../models/userProgress.model.js';
export const getGlobalLeaderboard = async (req, res) => {
try {
const userProgressList = await UserProgress.find({}, 'userId curriculumId tutorialProgress.marks').populate('userId', 'firstName lastName');
// Aggregate user progress data to calculate total marks and progress
const leaderboard = userProgressList.map(userProgress => {
const totalMarks = userProgress.curriculumId.reduce((total, curriculum) => {
return total + curriculum.tutorialProgress.reduce((totalTutMarks, tutorial) => {
return totalTutMarks + (tutorial.completed ? tutorial.marks : 0);
}, 0);
}, 0);
const totalTutorials = userProgress.curriculumId.reduce((totalTut, curriculum) => {
return totalTut + curriculum.tutorialProgress.length;
}, 0);
return {
user: userProgress.userId,
totalMarks,
totalTutorials
};
});
// Sort leaderboard based on total marks
leaderboard.sort((a, b) => b.totalMarks - a.totalMarks);
res.status(200).json(leaderboard);
} catch (error) {
res.status(500).json({ message: 'Error fetching global leaderboard' });
}
}
import Tutorial from '../models/tutorial.model.js';
export const getAllTutorials = async (req, res) => {
try {
const tutorials = await Tutorial.find();
res.status(200).json(tutorials);
} catch (error) {
res.status(500).json({ message: error.message });
}
}
export const getTutorialById = async (req, res) => {
const { id } = req.params;
try {
const tutorial = await Tutorial.findById(id);
res.status(200).json(tutorial);
} catch (error) {
res.status(404).json({ message: 'Tutorial not found' });
}
}
export const createTutorial = async (req, res) => {
const tutorialData = req.body;
try {
const newTutorial = new Tutorial(tutorialData);
await newTutorial.save();
res.status(201).json(newTutorial);
} catch (error) {
res.status(400).json({ message: error.message });
}
}
export const updateTutorial = async (req, res) => {
const { id } = req.params;
const updatedTutorial = req.body;
try {
const result = await Tutorial.findByIdAndUpdate(id, updatedTutorial, { new: true });
res.status(200).json(result);
} catch (error) {
res.status(404).json({ message: 'Tutorial not found' });
}
}
export const deleteTutorial = async (req, res) => {
const { id } = req.params;
try {
await Tutorial.findByIdAndDelete(id);
res.status(200).json({ message: 'Tutorial deleted successfully' });
} catch (error) {
res.status(404).json({ message: 'Tutorial not found' });
}
}
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import mongoose from 'mongoose';
import nodemailer from "nodemailer";
import User from '../models/user.model.js';
export const signIn = async (req, res) => {
......@@ -128,54 +127,12 @@ export const updateUser = async (req, res) => {
return res.status(404).json({ code: "02", message: `No User for this id: ${id}` });
}
if (data.type == "buyer" || data.type == "admin") {
const updateUser = { ...data, _id: id }
await User.findByIdAndUpdate(id, updateUser, { new: true })
res.status(200);
res.json({ code: "01", result: updateUser })
} else if (data.type == "trader") {
var password = Math.random().toString(36).slice(-8);
const hashPassword = await bcrypt.hash(password, 12)
const updateUser = { ...data, password: hashPassword, _id: id }
await User.findByIdAndUpdate(id, updateUser, { new: true })
//call email service
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAuth2',
user: process.env.MAIL_USERNAME,
pass: process.env.MAIL_PASSWORD,
clientId: process.env.OAUTH_CLIENTID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
refreshToken: process.env.OAUTH_REFRESH_TOKEN
}
});
let mailOptions = {
from: "janithgamage1.ed@gmail.com",
to: updateUser.email,
subject: 'Shop House Project',
text: `You are Successfully Approved, you're username: ${updateUser.email} , you're password : ${password}`
};
transporter.sendMail(mailOptions, function (err, data) {
if (err) {
console.log("Error " + err);
} else {
console.log("Email sent successfully");
}
});
res.status(200);
res.json({ code: "01", result: updateUser })
}
} catch (error) {
res.status(404);
......@@ -193,7 +150,7 @@ export const deleteUser = async (req, res) => {
await User.findByIdAndDelete(id);
res.status(200);
res.json({ code: "01", "message": "User Deleted Successfully" });
res.json({ code: "01", result: id, "message": "User Deleted Successfully" });
} catch (error) {
res.status(404);
res.json({ code: "00", "message": error.message });
......
import UserProgress from '../models/userProgress.model.js';
export const getUserProgress = async (req, res) => {
const userId = req.params.userId;
try {
const userProgress = await UserProgress.findOne({ userId }).populate('curriculumId tutorialProgress.tutorialId');
res.status(200).json(userProgress);
} catch (error) {
res.status(500).json({ message: error.message });
}
}
export const updateUserProgress = async (req, res) => {
const userId = req.params.userId;
const { curriculumId, tutorialId, completed, marks } = req.body;
try {
let userProgress = await UserProgress.findOne({ userId });
if (!userProgress) {
userProgress = new UserProgress({ userId });
}
const curriculumProgress = userProgress.curriculumId.find(prog => prog.curriculumId.equals(curriculumId));
if (!curriculumProgress) {
userProgress.curriculumId.push({ curriculumId });
}
const tutorialProgress = curriculumProgress.tutorialProgress.find(prog => prog.tutorialId.equals(tutorialId));
if (!tutorialProgress) {
curriculumProgress.tutorialProgress.push({ tutorialId, completed });
} else {
tutorialProgress.completed = completed;
}
userProgress.marks = marks;
await userProgress.save();
res.status(200).json(userProgress);
} catch (error) {
res.status(500).json({ message: error.message });
}
}
import mongoose from "mongoose";
const tutorialSchema = new mongoose.Schema({
tutorialCode: String,
tutorialTitle: String,
tutorialImage: String,
// Additional fields for tutorial content
});
const curriculumSchema = new mongoose.Schema({
curriculumCode: String,
curriculumLevel: String,
curriculumName: String,
curriculumImage: String,
tutorials: [tutorialSchema], // Embed tutorials as subdocuments
// Additional fields for curriculum details
status: {
type: Number,
default: 1, // Default status as active (1)
},
createdBy: String,
updatedBy: String,
createdAt: {
type: Date,
default: new Date(),
},
updatedAt: {
type: Date,
default: new Date(),
},
});
const Curriculum = mongoose.model("Curriculum", curriculumSchema);
export default Curriculum;
import mongoose from 'mongoose';
const feedbackSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User' // Reference to the User model
},
entityId: {
type: mongoose.Schema.Types.ObjectId,
required: true, // This could be the tutorial or curriculum ID
ref: 'Tutorial' // Reference to the Tutorial model (or Curriculum model)
},
rating: {
type: Number,
required: true
},
comment: String,
createdAt: {
type: Date,
default: new Date()
}
});
const Feedback = mongoose.model('Feedback', feedbackSchema);
export default Feedback;
import mongoose from "mongoose";
const taskItemSchema = new mongoose.Schema({
title: String,
description: String,
howToDo: String,
referenceImage: String,
referenceVideo: String,
// Additional fields for task items
});
const tutorialSchema = new mongoose.Schema({
tutorialCode: String,
tutorialTitle: String,
tutorialImage: String,
taskItems: [taskItemSchema], // Embed task items as subdocuments
// Additional fields for tutorial details
status: {
type: Number,
default: 1, // Default status as active (1)
},
createdBy: String,
updatedBy: String,
createdAt: {
type: Date,
default: new Date(),
},
updatedAt: {
type: Date,
default: new Date(),
},
});
const Tutorial = mongoose.model("Tutorial", tutorialSchema);
export default Tutorial;
import mongoose from "mongoose";
const userProgressSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User' // Reference to the User model
},
curriculumId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Curriculum' // Reference to the Curriculum model
},
tutorialProgress: [
{
tutorialId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Tutorial' // Reference to the Tutorial model
},
completed: {
type: Boolean,
default: false
}
}
],
marks: {
type: Number,
default: 0
}
});
const UserProgress = mongoose.model("UserProgress", userProgressSchema);
export default UserProgress;
import express from "express";
import {
createCurriculum,
deleteCurriculum,
getAllCurriculums,
getCurriculumById,
updateCurriculum
} from "../controllers/curriculum.controller.js";
const router = express.Router();
router.get('/', getAllCurriculums);
router.get('/:id', getCurriculumById);
router.post('/', createCurriculum);
router.put('/:id', updateCurriculum);
router.delete('/:id', deleteCurriculum);
export default router;
import express from 'express';
import { createFeedback, getFeedbackForEntity } from '../controllers/feedback.controller.js';
const router = express.Router();
router.post('/', createFeedback);
router.get('/:entityId', getFeedbackForEntity);
export default router;
import express from 'express';
import { getGlobalLeaderboard } from '../controllers/leaderboard.controller.js';
const router = express.Router();
router.get('/global', getGlobalLeaderboard);
export default router;
import express from "express";
import {
getAllTutorials,
getTutorialById,
createTutorial,
updateTutorial,
deleteTutorial
} from "../controllers/tutorial.controller.js";
const router = express.Router();
router.get('/', getAllTutorials);
router.get('/:id', getTutorialById);
router.post('/', createTutorial);
router.put('/:id', updateTutorial);
router.delete('/:id', deleteTutorial);
export default router;
......@@ -7,7 +7,7 @@ router.post('/sign-in', signIn)
router.post('/sign-up', signUp)
router.get('/all', getUsers);
router.get('/:id', getUser);
router.get('/test/:userType', getUserAccordingToType);
router.get('/all/type/:userType', getUserAccordingToType);
router.put('/:id', updateUser);
router.delete('/:id', deleteUser);
......
import express from "express";
import { getUserProgress, updateUserProgress } from "../controllers/userProgress.controller.js";
const router = express.Router();
router.get('/:userId', getUserProgress);
router.post('/:userId', updateUserProgress);
export default router;
......@@ -5,8 +5,13 @@ import express from "express";
import mongoose from "mongoose";
//import routes
import curriculumRoutes from "./routes/curriculum.routes.js";
import feedbackRoutes from "./routes/feedback.routes.js";
import leaderboardRoutes from "./routes/leaderboard.routes.js";
import translateRoutes from "./routes/translate.routes.js";
import tutorialRoutes from "./routes/tutorial.routes.js";
import userRoutes from "./routes/user.routes.js";
import userProgressRoutes from "./routes/userProgress.routes.js";
dotenv.config();
const app = express();
......@@ -23,6 +28,11 @@ app.get("/", (req, res) => {
//implement routes
app.use("/rest_node/ssl", translateRoutes);
app.use("/rest_node/user", userRoutes);
app.use("/rest_node/curriculum", curriculumRoutes);
app.use("/rest_node/tutorial", tutorialRoutes);
app.use("/rest_node/user-progress", userProgressRoutes);
app.use("/rest_node/feedback", feedbackRoutes);
app.use("/rest_node/leaderboard", leaderboardRoutes);
const CONNECTION_URL = `mongodb+srv://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@researchmanagement-appl.vzhn4.mongodb.net/?retryWrites=true&w=majority`;
const PORT = process.env.PORT || 5000;
......
......@@ -56,15 +56,17 @@ export const JWTProvider = ({ children }: { children: React.ReactElement }) => {
const init = async () => {
try {
const serviceToken = window.localStorage.getItem('serviceToken');
console.log(verifyToken(serviceToken!));
if (serviceToken && verifyToken(serviceToken)) {
setSession(serviceToken);
const response = await axios.get('/api/account/me');
const { user } = response.data;
// const response = await axios.get('/api/account/me');
// const { user } = response.data;
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user
// user
}
});
} else {
......
......@@ -86,6 +86,13 @@ const application: NavItemType = {
// type: 'item',
// url: '/learning-management/list',
// },
{
id: 'learning-dashboard',
title: <FormattedMessage id="learning-dashboard" />,
type: 'item',
url: '/learning-management/dashboard',
breadcrumbs: false
},
{
id: 'learning-curriculums',
title: <FormattedMessage id="learning-curriculums" />,
......
// material-ui
import { Grid } from '@mui/material';
// third-party
// project import
import WelcomeBanner from 'sections/learning-management/WelcomeBanner';
// assets
//types
// ==============================|| Dashboard ||============================== //
const Dashboard = () => {
return (
<Grid container rowSpacing={4.5} columnSpacing={3}>
<Grid item xs={12}>
<WelcomeBanner />
</Grid>
</Grid>
)
}
export default Dashboard;
......@@ -19,8 +19,6 @@ const List = () => {
return (
<MainCard content={false}>
<ScrollX>
{/* content here */}
<h1>Sample Page 2</h1>
</ScrollX>
</MainCard>
)
......
......@@ -18,8 +18,6 @@ const List = () => {
return (
<MainCard content={false}>
<ScrollX>
{/* content here */}
<h1>Sample Page</h1>
</ScrollX>
</MainCard>
)
......
// material-ui
// third-party
// project import
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
// assets
//types
// ==============================|| List ||============================== //
const List = () => {
return (
<MainCard content={false}>
<ScrollX>
</ScrollX>
</MainCard>
)
}
export default List;
// material-ui
// third-party
// project import
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
// assets
//types
// ==============================|| List ||============================== //
const List = () => {
return (
<MainCard content={false}>
<ScrollX>
</ScrollX>
</MainCard>
)
}
export default List;
import { Column } from 'react-table';
export interface dataProps {
_id: number | string | undefined;
curriculumCode: String;
curriculumLevel: String;
curriculumName: String;
curriculumImage: String;
tutorials: tutorialItemProps[];
status: Number;
createdBy: String;
updatedBy: String;
createdAt: Date;
updatedAt: Date;
}
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
handleAddEdit: () => void
}
export interface curriculumProps {
_id: number | string | undefined;
curriculumCode: String;
curriculumLevel: String;
curriculumName: String;
curriculumImage: String;
tutorials: tutorialItemProps[];
status: Number;
createdBy: String;
updatedBy: String;
createdAt: Date;
updatedAt: Date;
}
export interface tutorialItemProps {
_id: number | string | undefined;
tutorialCode: String;
tutorialTitle: String;
tutorialImage: String;
status: Number;
createdBy: String;
updatedBy: String;
createdAt: Date;
updatedAt: Date;
taskItems: taskItemProps[]
}
export interface taskItemProps {
_id: number | string | undefined;
title: String;
description: String;
howToDo: String;
referenceImage: String;
referenceVideo: String;
}
\ No newline at end of file
import { MouseEvent, useMemo, useState } from 'react';
// material-ui
import {
Button,
IconButton,
Stack,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Tooltip,
useTheme
} from '@mui/material';
// third-party
import { Cell, Column, HeaderGroup, Row, useFilters, useGlobalFilter, usePagination, useTable } from 'react-table';
// project import
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import { CSVExport, EmptyTable, TablePagination } from 'components/third-party/ReactTable';
import {
DefaultColumnFilter,
GlobalFilter,
renderFilterTypes
} from 'utils/react-table';
// assets
import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-design/icons';
//types
import AlertTutorialDelete from 'sections/parameters/tutorial-management/AlertTutorialDelete';
import { ReactTableProps, dataProps, tutorialProps } from './types/types';
// ==============================|| REACT TABLE ||============================== //
function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
const filterTypes = useMemo(() => renderFilterTypes, []);
const defaultColumn = useMemo(() => ({ Filter: DefaultColumnFilter }), []);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
preGlobalFilteredRows,
setGlobalFilter,
globalFilter,
page,
gotoPage,
setPageSize,
state: { pageIndex, pageSize }
} = useTable(
{
columns,
data,
defaultColumn,
filterTypes,
initialState: { pageIndex: 0, pageSize: 10 }
},
useGlobalFilter,
useFilters,
usePagination
);
return (
<>
<Stack direction="row" spacing={2} justifyContent="space-between" sx={{ padding: 2 }}>
<GlobalFilter preGlobalFilteredRows={preGlobalFilteredRows} globalFilter={globalFilter} setGlobalFilter={setGlobalFilter} />
<Stack direction="row" alignItems="center" spacing={1}>
<CSVExport data={rows.map((d: Row) => d.original)} filename={'filtering-table.csv'} />
<Button variant="contained" startIcon={<PlusOutlined />} onClick={handleAddEdit}>
Add New Tutorial
</Button>
</Stack>
</Stack>
<Table {...getTableProps()}>
<TableHead sx={{ borderTopWidth: 2 }}>
{headerGroups.map((headerGroup) => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column: HeaderGroup) => (
<TableCell {...column.getHeaderProps([{ className: column.className }])}>{column.render('Header')}</TableCell>
))}
</TableRow>
))}
</TableHead>
<TableBody {...getTableBodyProps()}>
{page.length > 0 ? (
page.map((row, i) => {
prepareRow(row);
return (
<TableRow {...row.getRowProps()}>
{row.cells.map((cell: Cell) => (
<TableCell {...cell.getCellProps([{ className: cell.column.className }])}>{cell.render('Cell')}</TableCell>
))}
</TableRow>
);
})
) : (
<EmptyTable msg="No Data" colSpan={10} />
)}
<TableRow>
<TableCell sx={{ p: 2 }} colSpan={10}>
<TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} />
</TableCell>
</TableRow>
</TableBody>
</Table>
</>
);
}
// ==============================|| List ||============================== //
const List = () => {
const theme = useTheme();
// table
const data: dataProps[] = []
const columns = useMemo(
() =>
[
{
Header: '#',
accessor: 'id',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.id === undefined || row.id === null || row.id === '') {
return <>-</>
}
if (typeof row.id === 'string') {
return <>{(parseInt(row.id) + 1).toString()}</>;
}
if (typeof row.id === 'number') {
return <>{row.id + 1}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'Tutorial Code',
accessor: 'tutorialCode'
},
{
Header: 'Tutorial Title',
accessor: 'tutorialTitle'
},
{
Header: 'Status',
accessor: 'status'
},
{
Header: 'Created By',
accessor: 'createdBy'
},
{
id: "actions",
Header: 'Actions',
accessor: 'actions',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
return (
<>
<Stack direction="row" alignItems="center" justifyContent="center" spacing={0}>
<Tooltip title="View">
<IconButton
color="secondary"
onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
}}
>
<EyeTwoTone twoToneColor={theme.palette.secondary.main} />
</IconButton>
</Tooltip>
<Tooltip title="Edit">
<IconButton
color="primary"
onClick={(e: MouseEvent<HTMLButtonElement>) => {
// handleAddEdit()
// setTutorial({
// id: row.values.id,
// nutritionName: row.values.nutritionName,
// nutritionCategory: row.values.nutritionCategory.id,
// nutritionUnitOfMeasure: row.values.nutritionUnitOfMeasure.id
// })
e.stopPropagation();
}}
>
<EditTwoTone twoToneColor={theme.palette.primary.main} />
</IconButton>
</Tooltip>
<Tooltip title="Delete">
<IconButton
color="error"
onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
// setTutorialId(row.values.id)
// setOpenAlert(true)
}}
>
<DeleteTwoTone twoToneColor={theme.palette.error.main} />
</IconButton>
</Tooltip>
</Stack>
</>
)
}
}
] as Column[],
[]
);
//dialog model
const [addEdit, setAddEdit] = useState<boolean>(false);
const [tutorial, setTutorial] = useState<tutorialProps>();
const handleAddEdit = () => {
setAddEdit(!addEdit);
if (tutorial && !addEdit) setTutorial(undefined);
};
//alert model
const [openAlert, setOpenAlert] = useState(false);
// const [tutorialId, setTutorialId] = useState<number | null>(null)
const tutorialId: number | null = null
const handleAlertClose = () => {
setOpenAlert(!openAlert);
};
const List = () => (
return (
<>
<MainCard content={false}>
<ScrollX>
<ReactTable columns={columns} data={data} handleAddEdit={handleAddEdit} />
</ScrollX>
{/* alert model */}
{!tutorial && <AlertTutorialDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={tutorialId} />}
</MainCard>
</>
);
)
}
export default List;
import { Column } from 'react-table';
export interface dataProps {
_id: number | string | undefined;
tutorialCode: String;
tutorialTitle: String;
tutorialImage: String;
status: Number;
createdBy: String;
updatedBy: String;
createdAt: Date;
updatedAt: Date;
taskItems: taskItemProps[]
}
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
handleAddEdit: () => void
}
export interface tutorialProps {
_id: number | string | undefined;
tutorialCode: String;
tutorialTitle: String;
tutorialImage: String;
status: Number;
createdBy: String;
updatedBy: String;
createdAt: Date;
updatedAt: Date;
taskItems: taskItemProps[]
}
export interface taskItemProps {
_id: number | string | undefined;
title: String;
description: String;
howToDo: String;
referenceImage: String;
referenceVideo: String;
}
\ No newline at end of file
......@@ -29,8 +29,15 @@ const MemberManagementList = Loadable(lazy(() => import('pages/member-management
// render - user management page
const UserManagementList = Loadable(lazy(() => import('pages/user-management/list/list')));
// render - learning management page
// const LearningManagementList = Loadable(lazy(() => import('pages/learning-management/list/list')));
// render - ssl translate process page
const SSLTranslateProcess = Loadable(lazy(() => import('pages/ssl-translate/process/process')));
// render - learning-management pages
const LearningDashboard = Loadable(lazy(() => import('pages/learning-management/dashboard')));
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 LearningLeadBoard = Loadable(lazy(() => import('pages/learning-management/learning-lead-board/list/list')));
const LearningFeedBack = Loadable(lazy(() => import('pages/learning-management/learning-feedback/list/list')));
// render - parameter curriculum management page
const CurriculumManagementList = Loadable(lazy(() => import('pages/parameter/curriculum-management/list/list')));
......@@ -38,14 +45,6 @@ const CurriculumManagementList = Loadable(lazy(() => import('pages/parameter/cur
// render - parameter tutorial management page
const TutorialManagementList = Loadable(lazy(() => import('pages/parameter/tutorial-management/list/list')));
// render - ssl translate process page
const SSLTranslateProcess = Loadable(lazy(() => import('pages/ssl-translate/process/process')));
// render - learning-curriculums-subscribed page
const LearningCurriculumsSubscribed = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/list/list')));
// render - learning-curriculums page
const LearningCurriculums = Loadable(lazy(() => import('pages/learning-management/learning-curriculums/list/list')));
// ==============================|| MAIN ROUTING ||============================== //
......@@ -99,6 +98,10 @@ const MainRoutes = {
{
path: 'learning-management',
children: [
{
path: 'dashboard',
element: <LearningDashboard />
},
{
path: 'curriculums',
element: <LearningCurriculums />
......@@ -106,6 +109,14 @@ const MainRoutes = {
{
path: 'curriculums-subscribed',
element: <LearningCurriculumsSubscribed />
},
{
path: 'lead-board',
element: <LearningLeadBoard />
},
{
path: 'feedback',
element: <LearningFeedBack />
}
]
},
......
import { useState } from 'react';
// material-ui
import { AvatarGroup, Box, Button, Divider, Grid, LinearProgress, Stack, Tooltip, Typography } from '@mui/material';
import { useTheme } from '@mui/material/styles';
// project import
import Avatar from 'components/@extended/Avatar';
import MainCard from 'components/MainCard';
// assets
import { PlusOutlined } from '@ant-design/icons';
import Reader from 'assets/images/analytics/reader.svg';
// types
import { ThemeDirection } from 'types/config';
const avatarImage = require.context('assets/images/users', true);
// ==============================|| READER CARD ||============================== //
function ReaderCard() {
const theme = useTheme();
const [show, setShow] = useState<boolean>(false);
return (
<Grid item xs={12}>
<Grid container>
<Grid
item
xs={12}
sm={7}
sx={{
bgcolor: `${theme.palette.primary.main}`,
position: 'relative',
p: 2.75,
borderRadius: { xs: 2, sm: '8px 0px 0px 8px' },
overflow: 'hidden'
}}
>
<Stack>
<Typography variant="h5" color="white">
What would you want to learn today
</Typography>
<Typography color={theme.palette.grey[0]} variant="caption" sx={{ maxWidth: '55%', pt: 1 }}>
Your learning capacity is 80% as daily analytics
</Typography>
<Typography variant="h4" color="white" sx={{ pt: 8, pb: 1, zIndex: 1 }}>
35% Completed
</Typography>
<Box sx={{ maxWidth: '60%' }}>
<LinearProgress variant="determinate" color="success" value={35} />
</Box>
<Box
sx={{
position: 'absolute',
bottom: -7,
right: 0,
...(theme.direction === ThemeDirection.RTL && { transform: { xs: 'rotateY(180deg)', sm: 'inherit' } })
}}
>
<img alt="reder" src={Reader} />
</Box>
</Stack>
</Grid>
<Grid item xs={12} sm={5}>
<MainCard sx={{ borderRadius: { xs: 2, sm: '0px 8px 8px 0px' }, height: '100%', mt: { xs: 2.5, sm: 0 } }}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Stack>
<Typography>Get started with new basic skills</Typography>
<Typography color="textSecondary" sx={{ pt: 1 }}>
Last Date 5th Nov 2020
</Typography>
<Divider sx={{ pt: 3, width: '100%' }} />
</Stack>
</Grid>
<Grid item xs={12}>
<Stack direction="row" alignItems="center" justifyContent="space-between" spacing={3}>
<Box sx={{ width: 186 }}>
<Tooltip
open={show}
placement="top-end"
title={
<AvatarGroup max={10}>
<Avatar alt="Agnes Walker" src={avatarImage(`./avatar-4.png`)} />
<Avatar alt="Trevor Henderson" src={avatarImage(`./avatar-5.png`)} />
<Avatar alt="Jone Doe" src={avatarImage(`./avatar-6.png`)} />
</AvatarGroup>
}
>
<AvatarGroup
sx={{
'& .MuiAvatarGroup-avatar': { bgcolor: theme.palette.primary.main, cursor: 'pointer' },
justifyContent: 'start',
'& .MuiAvatar-root': { width: 32, height: 32, fontSize: '0.875rem', bgcolor: 'secondary.400' }
}}
max={4}
componentsProps={{
additionalAvatar: {
onMouseEnter: () => {
setShow(true);
},
onMouseLeave: () => {
setShow(false);
}
}
}}
>
<Avatar alt="Remy Sharp" src={avatarImage(`./avatar-1.png`)} />
<Avatar alt="Travis Howard" src={avatarImage(`./avatar-2.png`)} />
<Avatar alt="Cindy Baker" src={avatarImage(`./avatar-3.png`)} />
<Avatar alt="Agnes Walker" src={avatarImage(`./avatar-4.png`)} />
<Avatar alt="Trevor Henderson" src={avatarImage(`./avatar-5.png`)} />
</AvatarGroup>
</Tooltip>
</Box>
<Button size="small" variant="contained" sx={{ minWidth: 'max-content', p: 1.5 }}>
<PlusOutlined />
</Button>
</Stack>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" color="secondary">
Chrome fixed the bug several versions ago, thus rendering this...
</Typography>
</Grid>
</Grid>
</MainCard>
</Grid>
</Grid>
</Grid>
);
}
export default ReaderCard;
// material-ui
import { Box, Button, Grid, Stack, Typography } from '@mui/material';
import { useTheme } from '@mui/material/styles';
// project import
import MainCard from 'components/MainCard';
//asset
import WelcomeImageArrow from 'assets/images/analytics/welcome-arrow.png';
import WelcomeImage from 'assets/images/analytics/welcome-banner.png';
// types
import { ThemeDirection } from 'types/config';
import { useNavigate } from 'react-router';
// ==============================|| ANALYTICS - WELCOME ||============================== //
const WelcomeBanner = () => {
const theme = useTheme();
const navigate = useNavigate()
return (
<MainCard
border={false}
sx={{
background:
theme.direction === ThemeDirection.RTL
? `linear-gradient(60.38deg, ${theme.palette.primary.lighter} 114%, ${theme.palette.primary.light} 34.42%, ${theme.palette.primary.main} 60.95%, ${theme.palette.primary.dark} 84.83%, ${theme.palette.primary.darker} 104.37%)`
: `linear-gradient(250.38deg, ${theme.palette.primary.lighter} 2.39%, ${theme.palette.primary.light} 34.42%, ${theme.palette.primary.main} 60.95%, ${theme.palette.primary.dark} 84.83%, ${theme.palette.primary.darker} 104.37%)`
}}
>
<Grid container>
<Grid item md={6} sm={6} xs={12}>
<Stack spacing={2} sx={{ padding: 3.4 }}>
<Typography variant="h2" color={theme.palette.background.paper}>
Welcome to SSL Sign Language Learning Platform
</Typography>
<Typography variant="h6" color={theme.palette.background.paper}>
Welcome to the SSL Sign Language Learning Platform. Your path to mastering sign language starts here. Whether you're a beginner or refining skills, our platform offers diverse courses with step-by-step tutorials. Begin your journey to fluent communication in SSL Sign Language today.
</Typography>
<Box>
<Button
variant="outlined"
color="secondary"
sx={{ color: theme.palette.background.paper, borderColor: theme.palette.background.paper }}
onClick={() => {navigate('/learning-management/curriculums')}}
>
View full Courses
</Button>
</Box>
</Stack>
</Grid>
<Grid item sm={6} xs={12} sx={{ display: { xs: 'none', sm: 'initial' } }}>
<Stack sx={{ position: 'relative', pr: { sm: 3, md: 8 } }} justifyContent="center" alignItems="flex-end">
<img src={WelcomeImage} alt="Welcome" />
<Box sx={{ position: 'absolute', bottom: 0, right: '10%' }}>
<img src={WelcomeImageArrow} alt="Welcome Arrow" />
</Box>
</Stack>
</Grid>
</Grid>
</MainCard>
);
};
export default WelcomeBanner;
// material-ui
import { Button, Dialog, DialogContent, Stack, Typography } from '@mui/material';
// project import
import Avatar from 'components/@extended/Avatar';
import { PopupTransition } from 'components/@extended/Transitions';
// assets
import { DeleteFilled } from '@ant-design/icons';
// types
interface Props {
title: string;
open: boolean;
handleClose: (status: boolean) => void;
deleteId: number | null;
}
// ==============================|| Curriculum - DELETE ||============================== //
export default function AlertCurriculumDelete({ title, open, handleClose, deleteId }: Props) {
// const dispatch = useDispatch();
return (
<Dialog
open={open}
onClose={() => handleClose(false)}
keepMounted
TransitionComponent={PopupTransition}
maxWidth="xs"
aria-labelledby="column-delete-title"
aria-describedby="column-delete-description"
>
<DialogContent sx={{ mt: 2, my: 1 }}>
<Stack alignItems="center" spacing={3.5}>
<Avatar color="error" sx={{ width: 72, height: 72, fontSize: '1.75rem' }}>
<DeleteFilled />
</Avatar>
<Stack spacing={2}>
<Typography variant="h4" align="center">
Are you sure you want to delete?
</Typography>
</Stack>
<Stack direction="row" spacing={2} sx={{ width: 1 }}>
<Button fullWidth onClick={() => handleClose(false)} color="secondary" variant="outlined">
Cancel
</Button>
<Button fullWidth color="error" variant="contained" onClick={() => {
// dispatch(deleteNutrition(deleteId!))
handleClose(true)
}} autoFocus>
Delete
</Button>
</Stack>
</Stack>
</DialogContent>
</Dialog >
);
}
// material-ui
import { Button, Dialog, DialogContent, Stack, Typography } from '@mui/material';
// project import
import Avatar from 'components/@extended/Avatar';
import { PopupTransition } from 'components/@extended/Transitions';
// assets
import { DeleteFilled } from '@ant-design/icons';
// types
interface Props {
title: string;
open: boolean;
handleClose: (status: boolean) => void;
deleteId: number | null;
}
// ==============================|| Tutorial - DELETE ||============================== //
export default function AlertTutorialDelete({ title, open, handleClose, deleteId }: Props) {
// const dispatch = useDispatch();
return (
<Dialog
open={open}
onClose={() => handleClose(false)}
keepMounted
TransitionComponent={PopupTransition}
maxWidth="xs"
aria-labelledby="column-delete-title"
aria-describedby="column-delete-description"
>
<DialogContent sx={{ mt: 2, my: 1 }}>
<Stack alignItems="center" spacing={3.5}>
<Avatar color="error" sx={{ width: 72, height: 72, fontSize: '1.75rem' }}>
<DeleteFilled />
</Avatar>
<Stack spacing={2}>
<Typography variant="h4" align="center">
Are you sure you want to delete?
</Typography>
</Stack>
<Stack direction="row" spacing={2} sx={{ width: 1 }}>
<Button fullWidth onClick={() => handleClose(false)} color="secondary" variant="outlined">
Cancel
</Button>
<Button fullWidth color="error" variant="contained" onClick={() => {
// dispatch(deleteNutrition(deleteId!))
handleClose(true)
}} autoFocus>
Delete
</Button>
</Stack>
</Stack>
</DialogContent>
</Dialog >
);
}
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { DefaultRootStateProps, User } from 'types/user';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['user'] = {
error: null,
success: null,
users: [],
user: null,
isLoading: false
};
const slice = createSlice({
name: 'user',
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
addUserSuccess(state, action) {
state.users.push(action.payload.result);
state.success = "User created successfully."
},
// GET USER
fetchUserSuccess(state, action) {
state.user = action.payload.result;
state.success = null
},
// GET ALL USERS
fetchUsersSuccess(state, action) {
state.users = action.payload;
state.success = null
},
// UPDATE USER
updateUserSuccess(state, action) {
const updatedUserIndex = state.users.findIndex(user => user._id === action.payload.result._id);
if (updatedUserIndex !== -1) {
state.users[updatedUserIndex] = action.payload;
}
state.success = "User updated successfully."
},
// DELETE USER
deleteUserSuccess(state, action) {
state.users = state.users.filter(user => user._id !== action.payload.result);
state.success = "User deleted successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST USER
* @param newUser
* @returns
*/
export function addUser(newUser: User) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.post('/user/sign-up', newUser);
dispatch(slice.actions.addUserSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET USER
* @param id
* @returns
*/
export function fetchUser(id: number) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/user/${id}`);
dispatch(slice.actions.fetchUserSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET ALL USERS
* @param userType
* @returns
*/
export function fetchUsers(userType: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/user/all/type/${userType}`);
dispatch(slice.actions.fetchUsersSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET ALL USERS BY TYPE
* @returns
*/
export function fetchUsersByType() {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get('/user/all');
dispatch(slice.actions.fetchUsersSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* UPDATE USER
* @param updatedUser
* @returns
*/
export function updateUser(updatedUser: User) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.put(`/user/${updatedUser._id}`, updateUser);
dispatch(slice.actions.updateUserSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* DELETE USER
* @param userId
* @returns
*/
export function deleteNutrition(userId: number) {
return async () => {
dispatch(slice.actions.startLoading());
try {
await axiosServices.delete(`/user/${userId}`);
dispatch(slice.actions.deleteUserSuccess(userId));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
// Nutritions list
// Nutrition list
export type Nutrition = {
id: string | number | undefined;
......
// User Type
export type User = {
_id: string | number | undefined;
firstName: string;
lastName: string;
email: string;
contactNumber: string;
type: string;
states: number;
createdAt: Date;
updatedAt: Date;
};
export type Users = {
_id: string | number | undefined;
firstName: string;
lastName: string;
email: string;
contactNumber: string;
type: string;
states: number;
createdAt: Date;
updatedAt: Date;
};
export interface UserStateProps {
users: Users[];
user: User | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
user: UserStateProps;
}
\ No newline at end of file
......@@ -157,9 +157,10 @@
"curriculum-management": "Curriculum Management",
"learning-management": "Learning Management",
"learning-list": "Learning List",
"learning-curriculums": "Course",
"learning-curriculums": "Courses",
"learning-curriculums-subscribed": "My Courses",
"learning-lead-board": "Lead Board",
"learning-feedback": "Feedback",
"ssl-translate": "SSL Translate"
"ssl-translate": "SSL Translate",
"learning-dashboard": "Dashboard"
}
\ No newline at end of file
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