Compare commits

..

1 Commits

Author SHA1 Message Date
Flatlogic Bot
5104e75922 Forced merge: merge ai-dev into master 2026-03-23 10:33:51 +00:00
9 changed files with 7778 additions and 5941 deletions

View File

@ -1,36 +0,0 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
const express = require('express'); const express = require('express');
const cors = require('cors'); const cors = require('cors');
const app = express(); const app = express();
@ -13,10 +14,15 @@ const swaggerJsDoc = require('swagger-jsdoc');
const authRoutes = require('./routes/auth'); const authRoutes = require('./routes/auth');
const fileRoutes = require('./routes/file'); const fileRoutes = require('./routes/file');
const searchRoutes = require('./routes/search'); const searchRoutes = require('./routes/search');
const sqlRoutes = require('./routes/sql');
const pexelsRoutes = require('./routes/pexels'); const pexelsRoutes = require('./routes/pexels');
const openaiRoutes = require('./routes/openai'); const openaiRoutes = require('./routes/openai');
const usersRoutes = require('./routes/users');
const rolesRoutes = require('./routes/roles'); const rolesRoutes = require('./routes/roles');
const permissionsRoutes = require('./routes/permissions'); const permissionsRoutes = require('./routes/permissions');
@ -53,7 +59,6 @@ const pricing_plansRoutes = require('./routes/pricing_plans');
const plan_featuresRoutes = require('./routes/plan_features'); const plan_featuresRoutes = require('./routes/plan_features');
const usersRoutes = require('./routes/users');
const getBaseUrl = (url) => { const getBaseUrl = (url) => {
if (!url) return ''; if (!url) return '';
@ -62,18 +67,17 @@ const getBaseUrl = (url) => {
const options = { const options = {
definition: { definition: {
openapi: '3.0.0', openapi: "3.0.0",
info: { info: {
version: '1.0.0', version: "1.0.0",
title: 'Course LMS', title: "Course LMS",
description: description: "Course LMS Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.",
'Course LMS Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.', },
},
servers: [ servers: [
{ {
url: getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || config.swaggerUrl, url: getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || config.swaggerUrl,
description: 'Development server', description: "Development server",
}, }
], ],
components: { components: {
securitySchemes: { securitySchemes: {
@ -81,36 +85,28 @@ const options = {
type: 'http', type: 'http',
scheme: 'bearer', scheme: 'bearer',
bearerFormat: 'JWT', bearerFormat: 'JWT',
}, }
}, },
responses: { responses: {
UnauthorizedError: { UnauthorizedError: {
description: 'Access token is missing or invalid', description: "Access token is missing or invalid"
}, }
}, }
}, },
security: [ security: [{
{ bearerAuth: []
bearerAuth: [], }]
},
],
}, },
apis: ['./src/routes/*.js'], apis: ["./src/routes/*.js"],
}; };
const specs = swaggerJsDoc(options); const specs = swaggerJsDoc(options);
app.use( app.use('/api-docs', function (req, res, next) {
'/api-docs', swaggerUI.host = getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || req.get('host');
function (req, res, next) { next()
swaggerUI.host = }, swaggerUI.serve, swaggerUI.setup(specs))
getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || req.get('host');
next();
},
swaggerUI.serve,
swaggerUI.setup(specs),
);
app.use(cors({ origin: true })); app.use(cors({origin: true}));
require('./auth/auth'); require('./auth/auth');
app.use(bodyParser.json()); app.use(bodyParser.json());
@ -120,148 +116,85 @@ app.use('/api/file', fileRoutes);
app.use('/api/pexels', pexelsRoutes); app.use('/api/pexels', pexelsRoutes);
app.enable('trust proxy'); app.enable('trust proxy');
app.use(
'/api/roles', app.use('/api/users', passport.authenticate('jwt', {session: false}), usersRoutes);
passport.authenticate('jwt', { session: false }),
rolesRoutes, app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoutes);
);
app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes);
app.use('/api/courses', passport.authenticate('jwt', {session: false}), coursesRoutes);
app.use('/api/course_sections', passport.authenticate('jwt', {session: false}), course_sectionsRoutes);
app.use('/api/lessons', passport.authenticate('jwt', {session: false}), lessonsRoutes);
app.use('/api/enrollments', passport.authenticate('jwt', {session: false}), enrollmentsRoutes);
app.use('/api/lesson_progress', passport.authenticate('jwt', {session: false}), lesson_progressRoutes);
app.use('/api/course_reviews', passport.authenticate('jwt', {session: false}), course_reviewsRoutes);
app.use('/api/course_announcements', passport.authenticate('jwt', {session: false}), course_announcementsRoutes);
app.use('/api/quizzes', passport.authenticate('jwt', {session: false}), quizzesRoutes);
app.use('/api/quiz_questions', passport.authenticate('jwt', {session: false}), quiz_questionsRoutes);
app.use('/api/quiz_options', passport.authenticate('jwt', {session: false}), quiz_optionsRoutes);
app.use('/api/quiz_attempts', passport.authenticate('jwt', {session: false}), quiz_attemptsRoutes);
app.use('/api/assignment_submissions', passport.authenticate('jwt', {session: false}), assignment_submissionsRoutes);
app.use('/api/marketing_pages', passport.authenticate('jwt', {session: false}), marketing_pagesRoutes);
app.use('/api/contact_messages', passport.authenticate('jwt', {session: false}), contact_messagesRoutes);
app.use('/api/pricing_plans', passport.authenticate('jwt', {session: false}), pricing_plansRoutes);
app.use('/api/plan_features', passport.authenticate('jwt', {session: false}), plan_featuresRoutes);
app.use( app.use(
'/api/permissions', '/api/openai',
passport.authenticate('jwt', { session: false }), passport.authenticate('jwt', { session: false }),
permissionsRoutes, openaiRoutes,
); );
app.use( app.use(
'/api/courses', '/api/ai',
passport.authenticate('jwt', { session: false }), passport.authenticate('jwt', { session: false }),
coursesRoutes, openaiRoutes,
);
app.use(
'/api/course_sections',
passport.authenticate('jwt', { session: false }),
course_sectionsRoutes,
);
app.use(
'/api/lessons',
passport.authenticate('jwt', { session: false }),
lessonsRoutes,
);
app.use(
'/api/enrollments',
passport.authenticate('jwt', { session: false }),
enrollmentsRoutes,
);
app.use(
'/api/lesson_progress',
passport.authenticate('jwt', { session: false }),
lesson_progressRoutes,
);
app.use(
'/api/course_reviews',
passport.authenticate('jwt', { session: false }),
course_reviewsRoutes,
);
app.use(
'/api/course_announcements',
passport.authenticate('jwt', { session: false }),
course_announcementsRoutes,
);
app.use(
'/api/quizzes',
passport.authenticate('jwt', { session: false }),
quizzesRoutes,
);
app.use(
'/api/quiz_questions',
passport.authenticate('jwt', { session: false }),
quiz_questionsRoutes,
);
app.use(
'/api/quiz_options',
passport.authenticate('jwt', { session: false }),
quiz_optionsRoutes,
);
app.use(
'/api/quiz_attempts',
passport.authenticate('jwt', { session: false }),
quiz_attemptsRoutes,
);
app.use(
'/api/assignment_submissions',
passport.authenticate('jwt', { session: false }),
assignment_submissionsRoutes,
);
app.use(
'/api/marketing_pages',
passport.authenticate('jwt', { session: false }),
marketing_pagesRoutes,
);
app.use(
'/api/contact_messages',
passport.authenticate('jwt', { session: false }),
contact_messagesRoutes,
);
app.use(
'/api/pricing_plans',
passport.authenticate('jwt', { session: false }),
pricing_plansRoutes,
);
app.use(
'/api/plan_features',
passport.authenticate('jwt', { session: false }),
plan_featuresRoutes,
);
app.use(
'/api/users',
passport.authenticate('jwt', { session: false }),
usersRoutes,
);
app.use(
'/api/openai',
passport.authenticate('jwt', { session: false }),
openaiRoutes,
); );
app.use( app.use(
'/api/search', '/api/search',
passport.authenticate('jwt', { session: false }), passport.authenticate('jwt', { session: false }),
searchRoutes, searchRoutes);
); app.use(
'/api/sql',
passport.authenticate('jwt', { session: false }),
sqlRoutes);
const publicDir = path.join(__dirname, '../public');
const publicDir = path.join(
__dirname,
'../public',
);
if (fs.existsSync(publicDir)) { if (fs.existsSync(publicDir)) {
app.use('/', express.static(publicDir)); app.use('/', express.static(publicDir));
app.get('*', function (request, response) { app.get('*', function(request, response) {
response.sendFile(path.resolve(publicDir, 'index.html')); response.sendFile(
path.resolve(publicDir, 'index.html'),
);
}); });
} }
const PORT = process.env.NODE_ENV === 'dev_stage' ? 3000 : 8080; const PORT = process.env.NODE_ENV === 'dev_stage' ? 3000 : 8080;
db.sequelize.sync().then(function () {
app.listen(PORT, () => { app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`); console.log(`Listening on port ${PORT}`);
}); });
});
module.exports = app; module.exports = app;

View File

@ -9,6 +9,7 @@ const Op = Sequelize.Op;
* @param {object} currentUser * @param {object} currentUser
*/ */
async function checkPermissions(permission, currentUser) { async function checkPermissions(permission, currentUser) {
if (!currentUser) { if (!currentUser) {
throw new ValidationError('auth.unauthorized'); throw new ValidationError('auth.unauthorized');
} }
@ -35,88 +36,401 @@ async function checkPermissions(permission, currentUser) {
} }
module.exports = class SearchService { module.exports = class SearchService {
static async search(searchQuery, currentUser) { static async search(searchQuery, currentUser ) {
try { try {
if (!searchQuery) { if (!searchQuery) {
throw new ValidationError('iam.errors.searchQueryRequired'); throw new ValidationError('iam.errors.searchQueryRequired');
} }
const tableColumns = { const tableColumns = {
courses: [
'title',
'slug',
"users": [
"firstName",
"lastName",
"phoneNumber",
"email",
],
'short_description',
"courses": [
"title",
"slug",
"short_description",
"description",
"currency",
],
'description',
"course_sections": [
"title",
],
'currency',
], "lessons": [
"title",
"slug",
"content",
"video_url",
],
course_sections: ['title'],
"enrollments": [
"notes",
],
lessons: ['title', 'slug', 'content', 'video_url'],
enrollments: ['notes'],
"course_reviews": [
"title",
"comment",
],
course_reviews: ['title', 'comment'],
"course_announcements": [
"title",
"message",
],
course_announcements: ['title', 'message'],
"quizzes": [
"title",
"instructions",
],
quizzes: ['title', 'instructions'],
"quiz_questions": [
"prompt",
"explanation",
],
quiz_questions: ['prompt', 'explanation'],
"quiz_options": [
"label",
],
quiz_options: ['label'],
assignment_submissions: ['submission_text', 'instructor_feedback'],
"assignment_submissions": [
"submission_text",
"instructor_feedback",
],
marketing_pages: [
'title', "marketing_pages": [
"title",
"slug",
"hero_headline",
"hero_subheadline",
"content",
],
'slug',
"contact_messages": [
"name",
"email",
"subject",
"message",
],
'hero_headline',
"pricing_plans": [
"name",
"description",
"currency",
],
'hero_subheadline',
"plan_features": [
'content',
], "name",
contact_messages: ['name', 'email', 'subject', 'message'], "description",
pricing_plans: ['name', 'description', 'currency'], ],
plan_features: ['name', 'description'],
users: ['firstName', 'lastName', 'phoneNumber', 'email'],
}; };
const columnsInt = { const columnsInt = {
courses: ['price'],
course_sections: ['position'],
lessons: ['duration_minutes', 'position'],
enrollments: ['progress_percent'],
lesson_progress: ['progress_percent'],
course_reviews: ['rating'], "courses": [
quizzes: ['time_limit_minutes', 'pass_percent'], "price",
quiz_questions: ['points', 'position'], ],
quiz_options: ['position'],
quiz_attempts: ['score_percent'],
assignment_submissions: ['grade_percent'], "course_sections": [
pricing_plans: ['price_monthly', 'price_yearly', 'sort_order'], "position",
plan_features: ['sort_order'], ],
"lessons": [
"duration_minutes",
"position",
],
"enrollments": [
"progress_percent",
],
"lesson_progress": [
"progress_percent",
],
"course_reviews": [
"rating",
],
"quizzes": [
"time_limit_minutes",
"pass_percent",
],
"quiz_questions": [
"points",
"position",
],
"quiz_options": [
"position",
],
"quiz_attempts": [
"score_percent",
],
"assignment_submissions": [
"grade_percent",
],
"pricing_plans": [
"price_monthly",
"price_yearly",
"sort_order",
],
"plan_features": [
"sort_order",
],
}; };
let allFoundRecords = []; let allFoundRecords = [];
@ -127,63 +441,48 @@ module.exports = class SearchService {
const attributesIntToSearch = columnsInt[tableName] || []; const attributesIntToSearch = columnsInt[tableName] || [];
const whereCondition = { const whereCondition = {
[Op.or]: [ [Op.or]: [
...attributesToSearch.map((attribute) => ({ ...attributesToSearch.map(attribute => ({
[attribute]: { [attribute]: {
[Op.iLike]: `%${searchQuery}%`, [Op.iLike] : `%${searchQuery}%`,
}, },
})), })),
...attributesIntToSearch.map((attribute) => ...attributesIntToSearch.map(attribute => (
Sequelize.where( Sequelize.where(
Sequelize.cast( Sequelize.cast(Sequelize.col(`${tableName}.${attribute}`), 'varchar'),
Sequelize.col(`${tableName}.${attribute}`), { [Op.iLike]: `%${searchQuery}%` }
'varchar', )
), )),
{ [Op.iLike]: `%${searchQuery}%` },
),
),
], ],
}; };
const hasPermission = await checkPermissions(
`READ_${tableName.toUpperCase()}`,
currentUser, const hasPermission = await checkPermissions(`READ_${tableName.toUpperCase()}`, currentUser);
);
if (!hasPermission) { if (!hasPermission) {
continue; continue;
} }
const foundRecords = await db[tableName].findAll({ const foundRecords = await db[tableName].findAll({
where: whereCondition, where: whereCondition,
attributes: [ attributes: [...tableColumns[tableName], 'id', ...attributesIntToSearch],
...tableColumns[tableName],
'id',
...attributesIntToSearch,
],
}); });
const modifiedRecords = foundRecords.map((record) => { const modifiedRecords = foundRecords.map((record) => {
const matchAttribute = []; const matchAttribute = [];
for (const attribute of attributesToSearch) { for (const attribute of attributesToSearch) {
if ( if (record[attribute]?.toLowerCase()?.includes(searchQuery.toLowerCase())) {
record[attribute]
?.toLowerCase()
?.includes(searchQuery.toLowerCase())
) {
matchAttribute.push(attribute); matchAttribute.push(attribute);
} }
} }
for (const attribute of attributesIntToSearch) { for (const attribute of attributesIntToSearch) {
const castedValue = String(record[attribute]); const castedValue = String(record[attribute]);
if ( if (castedValue && castedValue.toLowerCase().includes(searchQuery.toLowerCase())) {
castedValue &&
castedValue.toLowerCase().includes(searchQuery.toLowerCase())
) {
matchAttribute.push(attribute); matchAttribute.push(attribute);
} }
} }
return { return {
...record.get(), ...record.get(),
matchAttribute, matchAttribute,
@ -200,4 +499,4 @@ module.exports = class SearchService {
throw error; throw error;
} }
} }
}; }

View File

@ -2,230 +2,269 @@ import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
export default { export default {
filesFormatter(arr) { filesFormatter(arr) {
if (!arr || !arr.length) return []; if (!arr || !arr.length) return [];
return arr.map((item) => item); return arr.map((item) => item);
}, },
imageFormatter(arr) { imageFormatter(arr) {
if (!arr || !arr.length) return []; if (!arr || !arr.length) return []
return arr.map((item) => ({ return arr.map(item => ({
publicUrl: item.publicUrl || '', publicUrl: item.publicUrl || ''
})); }))
}, },
oneImageFormatter(arr) { oneImageFormatter(arr) {
if (!arr || !arr.length) return ''; if (!arr || !arr.length) return ''
return arr[0].publicUrl || ''; return arr[0].publicUrl || ''
}, },
dateFormatter(date) { dateFormatter(date) {
if (!date) return ''; if (!date) return ''
return dayjs(date).format('YYYY-MM-DD'); return dayjs(date).format('YYYY-MM-DD')
}, },
dateTimeFormatter(date) { dateTimeFormatter(date) {
if (!date) return ''; if (!date) return ''
return dayjs(date).format('YYYY-MM-DD HH:mm'); return dayjs(date).format('YYYY-MM-DD HH:mm')
}, },
booleanFormatter(val) { booleanFormatter(val) {
return val ? 'Yes' : 'No'; return val ? 'Yes' : 'No'
}, },
dataGridEditFormatter(obj) { dataGridEditFormatter(obj) {
return _.transform(obj, (result, value, key) => { return _.transform(obj, (result, value, key) => {
if (_.isArray(value)) { if (_.isArray(value)) {
result[key] = _.map(value, 'id'); result[key] = _.map(value, 'id');
} else if (_.isObject(value)) { } else if (_.isObject(value)) {
result[key] = value.id; result[key] = value.id;
} else { } else {
result[key] = value; result[key] = value;
} }
}); });
}, },
rolesManyListFormatter(val) {
if (!val || !val.length) return []; usersManyListFormatter(val) {
return val.map((item) => item.name); if (!val || !val.length) return []
}, return val.map((item) => item.firstName)
rolesOneListFormatter(val) { },
if (!val) return ''; usersOneListFormatter(val) {
return val.name; if (!val) return ''
}, return val.firstName
rolesManyListFormatterEdit(val) { },
if (!val || !val.length) return []; usersManyListFormatterEdit(val) {
return val.map((item) => { if (!val || !val.length) return []
return { id: item.id, label: item.name }; return val.map((item) => {
}); return {id: item.id, label: item.firstName}
}, });
rolesOneListFormatterEdit(val) { },
if (!val) return ''; usersOneListFormatterEdit(val) {
return { label: val.name, id: val.id }; if (!val) return ''
}, return {label: val.firstName, id: val.id}
},
permissionsManyListFormatter(val) {
if (!val || !val.length) return [];
return val.map((item) => item.name);
}, rolesManyListFormatter(val) {
permissionsOneListFormatter(val) { if (!val || !val.length) return []
if (!val) return ''; return val.map((item) => item.name)
return val.name; },
}, rolesOneListFormatter(val) {
permissionsManyListFormatterEdit(val) { if (!val) return ''
if (!val || !val.length) return []; return val.name
return val.map((item) => { },
return { id: item.id, label: item.name }; rolesManyListFormatterEdit(val) {
}); if (!val || !val.length) return []
}, return val.map((item) => {
permissionsOneListFormatterEdit(val) { return {id: item.id, label: item.name}
if (!val) return ''; });
return { label: val.name, id: val.id }; },
}, rolesOneListFormatterEdit(val) {
if (!val) return ''
coursesManyListFormatter(val) { return {label: val.name, id: val.id}
if (!val || !val.length) return []; },
return val.map((item) => item.title);
},
coursesOneListFormatter(val) {
if (!val) return ''; permissionsManyListFormatter(val) {
return val.title; if (!val || !val.length) return []
}, return val.map((item) => item.name)
coursesManyListFormatterEdit(val) { },
if (!val || !val.length) return []; permissionsOneListFormatter(val) {
return val.map((item) => { if (!val) return ''
return { id: item.id, label: item.title }; return val.name
}); },
}, permissionsManyListFormatterEdit(val) {
coursesOneListFormatterEdit(val) { if (!val || !val.length) return []
if (!val) return ''; return val.map((item) => {
return { label: val.title, id: val.id }; return {id: item.id, label: item.name}
}, });
},
course_sectionsManyListFormatter(val) { permissionsOneListFormatterEdit(val) {
if (!val || !val.length) return []; if (!val) return ''
return val.map((item) => item.title); return {label: val.name, id: val.id}
}, },
course_sectionsOneListFormatter(val) {
if (!val) return '';
return val.title;
}, coursesManyListFormatter(val) {
course_sectionsManyListFormatterEdit(val) { if (!val || !val.length) return []
if (!val || !val.length) return []; return val.map((item) => item.title)
return val.map((item) => { },
return { id: item.id, label: item.title }; coursesOneListFormatter(val) {
}); if (!val) return ''
}, return val.title
course_sectionsOneListFormatterEdit(val) { },
if (!val) return ''; coursesManyListFormatterEdit(val) {
return { label: val.title, id: val.id }; if (!val || !val.length) return []
}, return val.map((item) => {
return {id: item.id, label: item.title}
lessonsManyListFormatter(val) { });
if (!val || !val.length) return []; },
return val.map((item) => item.title); coursesOneListFormatterEdit(val) {
}, if (!val) return ''
lessonsOneListFormatter(val) { return {label: val.title, id: val.id}
if (!val) return ''; },
return val.title;
},
lessonsManyListFormatterEdit(val) {
if (!val || !val.length) return []; course_sectionsManyListFormatter(val) {
return val.map((item) => { if (!val || !val.length) return []
return { id: item.id, label: item.title }; return val.map((item) => item.title)
}); },
}, course_sectionsOneListFormatter(val) {
lessonsOneListFormatterEdit(val) { if (!val) return ''
if (!val) return ''; return val.title
return { label: val.title, id: val.id }; },
}, course_sectionsManyListFormatterEdit(val) {
if (!val || !val.length) return []
enrollmentsManyListFormatter(val) { return val.map((item) => {
if (!val || !val.length) return []; return {id: item.id, label: item.title}
return val.map((item) => item.notes); });
}, },
enrollmentsOneListFormatter(val) { course_sectionsOneListFormatterEdit(val) {
if (!val) return ''; if (!val) return ''
return val.notes; return {label: val.title, id: val.id}
}, },
enrollmentsManyListFormatterEdit(val) {
if (!val || !val.length) return [];
return val.map((item) => {
return { id: item.id, label: item.notes }; lessonsManyListFormatter(val) {
}); if (!val || !val.length) return []
}, return val.map((item) => item.title)
enrollmentsOneListFormatterEdit(val) { },
if (!val) return ''; lessonsOneListFormatter(val) {
return { label: val.notes, id: val.id }; if (!val) return ''
}, return val.title
},
quizzesManyListFormatter(val) { lessonsManyListFormatterEdit(val) {
if (!val || !val.length) return []; if (!val || !val.length) return []
return val.map((item) => item.title); return val.map((item) => {
}, return {id: item.id, label: item.title}
quizzesOneListFormatter(val) { });
if (!val) return ''; },
return val.title; lessonsOneListFormatterEdit(val) {
}, if (!val) return ''
quizzesManyListFormatterEdit(val) { return {label: val.title, id: val.id}
if (!val || !val.length) return []; },
return val.map((item) => {
return { id: item.id, label: item.title };
});
}, enrollmentsManyListFormatter(val) {
quizzesOneListFormatterEdit(val) { if (!val || !val.length) return []
if (!val) return ''; return val.map((item) => item.notes)
return { label: val.title, id: val.id }; },
}, enrollmentsOneListFormatter(val) {
if (!val) return ''
quiz_questionsManyListFormatter(val) { return val.notes
if (!val || !val.length) return []; },
return val.map((item) => item.prompt); enrollmentsManyListFormatterEdit(val) {
}, if (!val || !val.length) return []
quiz_questionsOneListFormatter(val) { return val.map((item) => {
if (!val) return ''; return {id: item.id, label: item.notes}
return val.prompt; });
}, },
quiz_questionsManyListFormatterEdit(val) { enrollmentsOneListFormatterEdit(val) {
if (!val || !val.length) return []; if (!val) return ''
return val.map((item) => { return {label: val.notes, id: val.id}
return { id: item.id, label: item.prompt }; },
});
},
quiz_questionsOneListFormatterEdit(val) {
if (!val) return '';
return { label: val.prompt, id: val.id };
},
plan_featuresManyListFormatter(val) {
if (!val || !val.length) return [];
return val.map((item) => item.name); quizzesManyListFormatter(val) {
}, if (!val || !val.length) return []
plan_featuresOneListFormatter(val) { return val.map((item) => item.title)
if (!val) return ''; },
return val.name; quizzesOneListFormatter(val) {
}, if (!val) return ''
plan_featuresManyListFormatterEdit(val) { return val.title
if (!val || !val.length) return []; },
return val.map((item) => { quizzesManyListFormatterEdit(val) {
return { id: item.id, label: item.name }; if (!val || !val.length) return []
}); return val.map((item) => {
}, return {id: item.id, label: item.title}
plan_featuresOneListFormatterEdit(val) { });
if (!val) return ''; },
return { label: val.name, id: val.id }; quizzesOneListFormatterEdit(val) {
}, if (!val) return ''
return {label: val.title, id: val.id}
usersManyListFormatter(val) { },
if (!val || !val.length) return [];
return val.map((item) => item.firstName);
},
usersOneListFormatter(val) { quiz_questionsManyListFormatter(val) {
if (!val) return ''; if (!val || !val.length) return []
return val.firstName; return val.map((item) => item.prompt)
}, },
usersManyListFormatterEdit(val) { quiz_questionsOneListFormatter(val) {
if (!val || !val.length) return []; if (!val) return ''
return val.map((item) => { return val.prompt
return { id: item.id, label: item.firstName }; },
}); quiz_questionsManyListFormatterEdit(val) {
}, if (!val || !val.length) return []
usersOneListFormatterEdit(val) { return val.map((item) => {
if (!val) return ''; return {id: item.id, label: item.prompt}
return { label: val.firstName, id: val.id }; });
}, },
}; quiz_questionsOneListFormatterEdit(val) {
if (!val) return ''
return {label: val.prompt, id: val.id}
},
plan_featuresManyListFormatter(val) {
if (!val || !val.length) return []
return val.map((item) => item.name)
},
plan_featuresOneListFormatter(val) {
if (!val) return ''
return val.name
},
plan_featuresManyListFormatterEdit(val) {
if (!val || !val.length) return []
return val.map((item) => {
return {id: item.id, label: item.name}
});
},
plan_featuresOneListFormatterEdit(val) {
if (!val) return ''
return {label: val.name, id: val.id}
},
}

View File

@ -1,5 +1,5 @@
import * as icon from '@mdi/js'; import * as icon from '@mdi/js';
import { MenuAsideItem } from './interfaces'; import { MenuAsideItem } from './interfaces'
const menuAside: MenuAsideItem[] = [ const menuAside: MenuAsideItem[] = [
{ {
@ -7,14 +7,22 @@ const menuAside: MenuAsideItem[] = [
icon: icon.mdiViewDashboardOutline, icon: icon.mdiViewDashboardOutline,
label: 'Dashboard', label: 'Dashboard',
}, },
{
href: '/users/users-list',
label: 'Users',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiAccountGroup ?? icon.mdiTable,
permissions: 'READ_USERS'
},
{ {
href: '/roles/roles-list', href: '/roles/roles-list',
label: 'Roles', label: 'Roles',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon.mdiShieldAccountVariantOutline ?? icon.mdiTable, icon: icon.mdiShieldAccountVariantOutline ?? icon.mdiTable,
permissions: 'READ_ROLES', permissions: 'READ_ROLES'
}, },
{ {
href: '/permissions/permissions-list', href: '/permissions/permissions-list',
@ -22,191 +30,135 @@ const menuAside: MenuAsideItem[] = [
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon.mdiShieldAccountOutline ?? icon.mdiTable, icon: icon.mdiShieldAccountOutline ?? icon.mdiTable,
permissions: 'READ_PERMISSIONS', permissions: 'READ_PERMISSIONS'
}, },
{ {
href: '/courses/courses-list', href: '/courses/courses-list',
label: 'Courses', label: 'Courses',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiSchool' in icon ? icon['mdiSchool' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiSchool' in icon permissions: 'READ_COURSES'
? icon['mdiSchool' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_COURSES',
}, },
{ {
href: '/course_sections/course_sections-list', href: '/course_sections/course_sections-list',
label: 'Course sections', label: 'Course sections',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiViewList' in icon ? icon['mdiViewList' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiViewList' in icon permissions: 'READ_COURSE_SECTIONS'
? icon['mdiViewList' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_COURSE_SECTIONS',
}, },
{ {
href: '/lessons/lessons-list', href: '/lessons/lessons-list',
label: 'Lessons', label: 'Lessons',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiPlayCircleOutline' in icon ? icon['mdiPlayCircleOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiPlayCircleOutline' in icon permissions: 'READ_LESSONS'
? icon['mdiPlayCircleOutline' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_LESSONS',
}, },
{ {
href: '/enrollments/enrollments-list', href: '/enrollments/enrollments-list',
label: 'Enrollments', label: 'Enrollments',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiAccountSchool' in icon ? icon['mdiAccountSchool' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiAccountSchool' in icon permissions: 'READ_ENROLLMENTS'
? icon['mdiAccountSchool' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_ENROLLMENTS',
}, },
{ {
href: '/lesson_progress/lesson_progress-list', href: '/lesson_progress/lesson_progress-list',
label: 'Lesson progress', label: 'Lesson progress',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiProgressCheck' in icon ? icon['mdiProgressCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiProgressCheck' in icon permissions: 'READ_LESSON_PROGRESS'
? icon['mdiProgressCheck' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_LESSON_PROGRESS',
}, },
{ {
href: '/course_reviews/course_reviews-list', href: '/course_reviews/course_reviews-list',
label: 'Course reviews', label: 'Course reviews',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiStarCircle' in icon ? icon['mdiStarCircle' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiStarCircle' in icon permissions: 'READ_COURSE_REVIEWS'
? icon['mdiStarCircle' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_COURSE_REVIEWS',
}, },
{ {
href: '/course_announcements/course_announcements-list', href: '/course_announcements/course_announcements-list',
label: 'Course announcements', label: 'Course announcements',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiBullhorn' in icon ? icon['mdiBullhorn' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiBullhorn' in icon permissions: 'READ_COURSE_ANNOUNCEMENTS'
? icon['mdiBullhorn' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_COURSE_ANNOUNCEMENTS',
}, },
{ {
href: '/quizzes/quizzes-list', href: '/quizzes/quizzes-list',
label: 'Quizzes', label: 'Quizzes',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiHelpCircleOutline' in icon ? icon['mdiHelpCircleOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiHelpCircleOutline' in icon permissions: 'READ_QUIZZES'
? icon['mdiHelpCircleOutline' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_QUIZZES',
}, },
{ {
href: '/quiz_questions/quiz_questions-list', href: '/quiz_questions/quiz_questions-list',
label: 'Quiz questions', label: 'Quiz questions',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiOrderBoolAscendingVariant' in icon ? icon['mdiOrderBoolAscendingVariant' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiOrderBoolAscendingVariant' in icon permissions: 'READ_QUIZ_QUESTIONS'
? icon['mdiOrderBoolAscendingVariant' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_QUIZ_QUESTIONS',
}, },
{ {
href: '/quiz_options/quiz_options-list', href: '/quiz_options/quiz_options-list',
label: 'Quiz options', label: 'Quiz options',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiCheckboxMarkedCircleOutline' in icon ? icon['mdiCheckboxMarkedCircleOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiCheckboxMarkedCircleOutline' in icon permissions: 'READ_QUIZ_OPTIONS'
? icon['mdiCheckboxMarkedCircleOutline' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_QUIZ_OPTIONS',
}, },
{ {
href: '/quiz_attempts/quiz_attempts-list', href: '/quiz_attempts/quiz_attempts-list',
label: 'Quiz attempts', label: 'Quiz attempts',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiClipboardTextOutline' in icon ? icon['mdiClipboardTextOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiClipboardTextOutline' in icon permissions: 'READ_QUIZ_ATTEMPTS'
? icon['mdiClipboardTextOutline' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_QUIZ_ATTEMPTS',
}, },
{ {
href: '/assignment_submissions/assignment_submissions-list', href: '/assignment_submissions/assignment_submissions-list',
label: 'Assignment submissions', label: 'Assignment submissions',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiFileUploadOutline' in icon ? icon['mdiFileUploadOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiFileUploadOutline' in icon permissions: 'READ_ASSIGNMENT_SUBMISSIONS'
? icon['mdiFileUploadOutline' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_ASSIGNMENT_SUBMISSIONS',
}, },
{ {
href: '/marketing_pages/marketing_pages-list', href: '/marketing_pages/marketing_pages-list',
label: 'Marketing pages', label: 'Marketing pages',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiWeb' in icon ? icon['mdiWeb' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiWeb' in icon permissions: 'READ_MARKETING_PAGES'
? icon['mdiWeb' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_MARKETING_PAGES',
}, },
{ {
href: '/contact_messages/contact_messages-list', href: '/contact_messages/contact_messages-list',
label: 'Contact messages', label: 'Contact messages',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiEmailOutline' in icon ? icon['mdiEmailOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiEmailOutline' in icon permissions: 'READ_CONTACT_MESSAGES'
? icon['mdiEmailOutline' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_CONTACT_MESSAGES',
}, },
{ {
href: '/pricing_plans/pricing_plans-list', href: '/pricing_plans/pricing_plans-list',
label: 'Pricing plans', label: 'Pricing plans',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiCashMultiple' in icon ? icon['mdiCashMultiple' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiCashMultiple' in icon permissions: 'READ_PRICING_PLANS'
? icon['mdiCashMultiple' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_PRICING_PLANS',
}, },
{ {
href: '/plan_features/plan_features-list', href: '/plan_features/plan_features-list',
label: 'Plan features', label: 'Plan features',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
icon: icon: 'mdiChecklist' in icon ? icon['mdiChecklist' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
'mdiChecklist' in icon permissions: 'READ_PLAN_FEATURES'
? icon['mdiChecklist' as keyof typeof icon]
: icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_PLAN_FEATURES',
},
{
href: '/users/users-list',
label: 'Users',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiAccountGroup ?? icon.mdiTable,
permissions: 'READ_USERS',
}, },
{ {
href: '/profile', href: '/profile',
@ -214,13 +166,14 @@ const menuAside: MenuAsideItem[] = [
icon: icon.mdiAccountCircle, icon: icon.mdiAccountCircle,
}, },
{ {
href: '/api-docs', href: '/api-docs',
target: '_blank', target: '_blank',
label: 'Swagger API', label: 'Swagger API',
icon: icon.mdiFileCode, icon: icon.mdiFileCode,
permissions: 'READ_API_DOCS', permissions: 'READ_API_DOCS'
}, },
]; ]
export default menuAside; export default menuAside

File diff suppressed because it is too large Load Diff

View File

@ -4,25 +4,25 @@ import mainReducer from './mainSlice';
import authSlice from './authSlice'; import authSlice from './authSlice';
import openAiSlice from './openAiSlice'; import openAiSlice from './openAiSlice';
import rolesSlice from './roles/rolesSlice'; import usersSlice from "./users/usersSlice";
import permissionsSlice from './permissions/permissionsSlice'; import rolesSlice from "./roles/rolesSlice";
import coursesSlice from './courses/coursesSlice'; import permissionsSlice from "./permissions/permissionsSlice";
import course_sectionsSlice from './course_sections/course_sectionsSlice'; import coursesSlice from "./courses/coursesSlice";
import lessonsSlice from './lessons/lessonsSlice'; import course_sectionsSlice from "./course_sections/course_sectionsSlice";
import enrollmentsSlice from './enrollments/enrollmentsSlice'; import lessonsSlice from "./lessons/lessonsSlice";
import lesson_progressSlice from './lesson_progress/lesson_progressSlice'; import enrollmentsSlice from "./enrollments/enrollmentsSlice";
import course_reviewsSlice from './course_reviews/course_reviewsSlice'; import lesson_progressSlice from "./lesson_progress/lesson_progressSlice";
import course_announcementsSlice from './course_announcements/course_announcementsSlice'; import course_reviewsSlice from "./course_reviews/course_reviewsSlice";
import quizzesSlice from './quizzes/quizzesSlice'; import course_announcementsSlice from "./course_announcements/course_announcementsSlice";
import quiz_questionsSlice from './quiz_questions/quiz_questionsSlice'; import quizzesSlice from "./quizzes/quizzesSlice";
import quiz_optionsSlice from './quiz_options/quiz_optionsSlice'; import quiz_questionsSlice from "./quiz_questions/quiz_questionsSlice";
import quiz_attemptsSlice from './quiz_attempts/quiz_attemptsSlice'; import quiz_optionsSlice from "./quiz_options/quiz_optionsSlice";
import assignment_submissionsSlice from './assignment_submissions/assignment_submissionsSlice'; import quiz_attemptsSlice from "./quiz_attempts/quiz_attemptsSlice";
import marketing_pagesSlice from './marketing_pages/marketing_pagesSlice'; import assignment_submissionsSlice from "./assignment_submissions/assignment_submissionsSlice";
import contact_messagesSlice from './contact_messages/contact_messagesSlice'; import marketing_pagesSlice from "./marketing_pages/marketing_pagesSlice";
import pricing_plansSlice from './pricing_plans/pricing_plansSlice'; import contact_messagesSlice from "./contact_messages/contact_messagesSlice";
import plan_featuresSlice from './plan_features/plan_featuresSlice'; import pricing_plansSlice from "./pricing_plans/pricing_plansSlice";
import usersSlice from './users/usersSlice'; import plan_featuresSlice from "./plan_features/plan_featuresSlice";
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
@ -31,29 +31,29 @@ export const store = configureStore({
auth: authSlice, auth: authSlice,
openAi: openAiSlice, openAi: openAiSlice,
roles: rolesSlice, users: usersSlice,
permissions: permissionsSlice, roles: rolesSlice,
courses: coursesSlice, permissions: permissionsSlice,
course_sections: course_sectionsSlice, courses: coursesSlice,
lessons: lessonsSlice, course_sections: course_sectionsSlice,
enrollments: enrollmentsSlice, lessons: lessonsSlice,
lesson_progress: lesson_progressSlice, enrollments: enrollmentsSlice,
course_reviews: course_reviewsSlice, lesson_progress: lesson_progressSlice,
course_announcements: course_announcementsSlice, course_reviews: course_reviewsSlice,
quizzes: quizzesSlice, course_announcements: course_announcementsSlice,
quiz_questions: quiz_questionsSlice, quizzes: quizzesSlice,
quiz_options: quiz_optionsSlice, quiz_questions: quiz_questionsSlice,
quiz_attempts: quiz_attemptsSlice, quiz_options: quiz_optionsSlice,
assignment_submissions: assignment_submissionsSlice, quiz_attempts: quiz_attemptsSlice,
marketing_pages: marketing_pagesSlice, assignment_submissions: assignment_submissionsSlice,
contact_messages: contact_messagesSlice, marketing_pages: marketing_pagesSlice,
pricing_plans: pricing_plansSlice, contact_messages: contact_messagesSlice,
plan_features: plan_featuresSlice, pricing_plans: pricing_plansSlice,
users: usersSlice, plan_features: plan_featuresSlice,
}, },
}); })
// Infer the `RootState` and `AppDispatch` types from the store itself // Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>; export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch; export type AppDispatch = typeof store.dispatch