Compare commits
No commits in common. "bb2fbfd4adfde6596973340766ec781a86b49ce3" and "71822595cb64a62f5a0d346dd542c74c2febe3bc" have entirely different histories.
bb2fbfd4ad
...
71822595cb
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,6 +1,3 @@
|
|||||||
/backend/node_modules
|
|
||||||
/frontend/node_modules
|
|
||||||
node_modules/
|
node_modules/
|
||||||
*/node_modules/
|
*/node_modules/
|
||||||
**/node_modules/
|
*/build/
|
||||||
*/build/
|
|
||||||
|
|||||||
4
502.html
4
502.html
@ -129,8 +129,8 @@
|
|||||||
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
||||||
available.</p>
|
available.</p>
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<h2>CourseFlow LMS</h2>
|
<h2>Store Operations Manager</h2>
|
||||||
<p>CourseFlow LMS: instructor and student workflows for courses, lessons, enrollments, and progress tracking.</p>
|
<p>Manage products, customers, orders, payments, and fulfillment workflows for retail operations.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="loader-container">
|
<div class="loader-container">
|
||||||
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
# CourseFlow LMS
|
# Store Operations Manager
|
||||||
|
|
||||||
|
|
||||||
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
|
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
DB_NAME=app_37465
|
DB_NAME=app_37462
|
||||||
DB_USER=app_37465
|
DB_USER=app_37462
|
||||||
DB_PASS=578d9f61-c443-4a62-87c0-ac39c47254f1
|
DB_PASS=b5fc3cee-cc82-4cf6-bbcd-51bd3a434c2c
|
||||||
DB_HOST=127.0.0.1
|
DB_HOST=127.0.0.1
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
#CourseFlow LMS - template backend,
|
#Store Operations Manager - template backend,
|
||||||
|
|
||||||
#### Run App on local machine:
|
#### Run App on local machine:
|
||||||
|
|
||||||
@ -30,10 +30,10 @@
|
|||||||
- `psql postgres -U admin`
|
- `psql postgres -U admin`
|
||||||
|
|
||||||
- Type this command to creating a new database.
|
- Type this command to creating a new database.
|
||||||
- `postgres=> CREATE DATABASE db_courseflow_lms;`
|
- `postgres=> CREATE DATABASE db_store_operations_manager;`
|
||||||
|
|
||||||
- Then give that new user privileges to the new database then quit the `psql`.
|
- Then give that new user privileges to the new database then quit the `psql`.
|
||||||
- `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_courseflow_lms TO admin;`
|
- `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_store_operations_manager TO admin;`
|
||||||
- `postgres=> \q`
|
- `postgres=> \q`
|
||||||
|
|
||||||
------------
|
------------
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "courseflowlms",
|
"name": "storeoperationsmanager",
|
||||||
"description": "CourseFlow LMS - template backend",
|
"description": "Store Operations Manager - template backend",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
||||||
"db:migrate": "sequelize-cli db:migrate",
|
"db:migrate": "sequelize-cli db:migrate",
|
||||||
|
|||||||
@ -11,15 +11,15 @@ const config = {
|
|||||||
bcrypt: {
|
bcrypt: {
|
||||||
saltRounds: 12
|
saltRounds: 12
|
||||||
},
|
},
|
||||||
admin_pass: "578d9f61",
|
admin_pass: "b5fc3cee",
|
||||||
user_pass: "ac39c47254f1",
|
user_pass: "51bd3a434c2c",
|
||||||
admin_email: "admin@flatlogic.com",
|
admin_email: "admin@flatlogic.com",
|
||||||
providers: {
|
providers: {
|
||||||
LOCAL: 'local',
|
LOCAL: 'local',
|
||||||
GOOGLE: 'google',
|
GOOGLE: 'google',
|
||||||
MICROSOFT: 'microsoft'
|
MICROSOFT: 'microsoft'
|
||||||
},
|
},
|
||||||
secret_key: process.env.SECRET_KEY || '578d9f61-c443-4a62-87c0-ac39c47254f1',
|
secret_key: process.env.SECRET_KEY || 'b5fc3cee-cc82-4cf6-bbcd-51bd3a434c2c',
|
||||||
remote: '',
|
remote: '',
|
||||||
port: process.env.NODE_ENV === "production" ? "" : "8080",
|
port: process.env.NODE_ENV === "production" ? "" : "8080",
|
||||||
hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost",
|
hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost",
|
||||||
@ -39,7 +39,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
uploadDir: os.tmpdir(),
|
uploadDir: os.tmpdir(),
|
||||||
email: {
|
email: {
|
||||||
from: 'CourseFlow LMS <app@flatlogic.app>',
|
from: 'Store Operations Manager <app@flatlogic.app>',
|
||||||
host: 'email-smtp.us-east-1.amazonaws.com',
|
host: 'email-smtp.us-east-1.amazonaws.com',
|
||||||
port: 587,
|
port: 587,
|
||||||
auth: {
|
auth: {
|
||||||
@ -56,11 +56,11 @@ const config = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
user: 'Student',
|
user: 'Support Agent',
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
project_uuid: '578d9f61-c443-4a62-87c0-ac39c47254f1',
|
project_uuid: 'b5fc3cee-cc82-4cf6-bbcd-51bd3a434c2c',
|
||||||
flHost: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
|
flHost: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ const config = {
|
|||||||
|
|
||||||
config.pexelsKey = process.env.PEXELS_KEY || '';
|
config.pexelsKey = process.env.PEXELS_KEY || '';
|
||||||
|
|
||||||
config.pexelsQuery = 'open book with flowing pages';
|
config.pexelsQuery = 'city skyline at golden hour';
|
||||||
config.host = process.env.NODE_ENV === "production" ? config.remote : "http://localhost";
|
config.host = process.env.NODE_ENV === "production" ? config.remote : "http://localhost";
|
||||||
config.apiUrl = `${config.host}${config.port ? `:${config.port}` : ``}/api`;
|
config.apiUrl = `${config.host}${config.port ? `:${config.port}` : ``}/api`;
|
||||||
config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`;
|
config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = class Course_categoriesDBApi {
|
module.exports = class CategoriesDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const course_categories = await db.course_categories.create(
|
const categories = await db.categories.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
@ -26,11 +26,6 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
slug: data.slug
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
description: data.description
|
description: data.description
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
@ -44,12 +39,16 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await categories.setParent( data.parent || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return course_categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -58,17 +57,12 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// Prepare data - wrapping individual data transformations in a map() method
|
||||||
const course_categoriesData = data.map((item, index) => ({
|
const categoriesData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
name: item.name
|
name: item.name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
|
||||||
|
|
||||||
slug: item.slug
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
,
|
||||||
|
|
||||||
description: item.description
|
description: item.description
|
||||||
@ -83,12 +77,12 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// Bulk create items
|
||||||
const course_categories = await db.course_categories.bulkCreate(course_categoriesData, { transaction });
|
const categories = await db.categories.bulkCreate(categoriesData, { transaction });
|
||||||
|
|
||||||
// For each item created, replace relation files
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
return course_categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -96,7 +90,7 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
const course_categories = await db.course_categories.findByPk(id, {}, {transaction});
|
const categories = await db.categories.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -106,32 +100,38 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
if (data.name !== undefined) updatePayload.name = data.name;
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
|
||||||
|
|
||||||
if (data.slug !== undefined) updatePayload.slug = data.slug;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.description !== undefined) updatePayload.description = data.description;
|
if (data.description !== undefined) updatePayload.description = data.description;
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await course_categories.update(updatePayload, {transaction});
|
await categories.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (data.parent !== undefined) {
|
||||||
|
await categories.setParent(
|
||||||
|
|
||||||
|
data.parent,
|
||||||
|
|
||||||
|
{ transaction }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return course_categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const course_categories = await db.course_categories.findAll({
|
const categories = await db.categories.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -141,60 +141,59 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of course_categories) {
|
for (const record of categories) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of course_categories) {
|
for (const record of categories) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return course_categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const course_categories = await db.course_categories.findByPk(id, options);
|
const categories = await db.categories.findByPk(id, options);
|
||||||
|
|
||||||
await course_categories.update({
|
await categories.update({
|
||||||
deletedBy: currentUser.id
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await course_categories.destroy({
|
await categories.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return course_categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const course_categories = await db.course_categories.findOne(
|
const categories = await db.categories.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!course_categories) {
|
if (!categories) {
|
||||||
return course_categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = course_categories.get({plain: true});
|
const output = categories.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.products_category = await categories.getProducts_category({
|
||||||
output.courses_category = await course_categories.getCourses_category({
|
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -206,6 +205,10 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.parent = await categories.getParent({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@ -232,6 +235,23 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
|
|
||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.categories,
|
||||||
|
as: 'parent',
|
||||||
|
|
||||||
|
where: filter.parent ? {
|
||||||
|
[Op.or]: [
|
||||||
|
{ id: { [Op.in]: filter.parent.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
[Op.or]: filter.parent.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} : {},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
@ -249,29 +269,18 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'course_categories',
|
'categories',
|
||||||
'name',
|
'name',
|
||||||
filter.name,
|
filter.name,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.slug) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'course_categories',
|
|
||||||
'slug',
|
|
||||||
filter.slug,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.description) {
|
if (filter.description) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'course_categories',
|
'categories',
|
||||||
'description',
|
'description',
|
||||||
filter.description,
|
filter.description,
|
||||||
),
|
),
|
||||||
@ -294,6 +303,8 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.createdAtRange) {
|
if (filter.createdAtRange) {
|
||||||
@ -341,7 +352,7 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.course_categories.findAndCountAll(queryOptions);
|
const { rows, count } = await db.categories.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -363,7 +374,7 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'course_categories',
|
'categories',
|
||||||
'name',
|
'name',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
@ -371,7 +382,7 @@ module.exports = class Course_categoriesDBApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.course_categories.findAll({
|
const records = await db.categories.findAll({
|
||||||
attributes: [ 'id', 'name' ],
|
attributes: [ 'id', 'name' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
@ -1,491 +0,0 @@
|
|||||||
|
|
||||||
const db = require('../models');
|
|
||||||
const FileDBApi = require('./file');
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const Utils = require('../utils');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Sequelize = db.Sequelize;
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
|
|
||||||
module.exports = class CertificatesDBApi {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static async create(data, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
const certificates = await db.certificates.create(
|
|
||||||
{
|
|
||||||
id: data.id || undefined,
|
|
||||||
|
|
||||||
serial: data.serial
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
issued_at: data.issued_at
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
importHash: data.importHash || null,
|
|
||||||
createdById: currentUser.id,
|
|
||||||
updatedById: currentUser.id,
|
|
||||||
},
|
|
||||||
{ transaction },
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
await certificates.setStudent( data.student || null, {
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await certificates.setCourse( data.course || null, {
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.certificates.getTableName(),
|
|
||||||
belongsToColumn: 'file',
|
|
||||||
belongsToId: certificates.id,
|
|
||||||
},
|
|
||||||
data.file,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static async bulkImport(data, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
|
||||||
const certificatesData = data.map((item, index) => ({
|
|
||||||
id: item.id || undefined,
|
|
||||||
|
|
||||||
serial: item.serial
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
issued_at: item.issued_at
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
|
||||||
createdById: currentUser.id,
|
|
||||||
updatedById: currentUser.id,
|
|
||||||
createdAt: new Date(Date.now() + index * 1000),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Bulk create items
|
|
||||||
const certificates = await db.certificates.bulkCreate(certificatesData, { transaction });
|
|
||||||
|
|
||||||
// For each item created, replace relation files
|
|
||||||
|
|
||||||
for (let i = 0; i < certificates.length; i++) {
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.certificates.getTableName(),
|
|
||||||
belongsToColumn: 'file',
|
|
||||||
belongsToId: certificates[i].id,
|
|
||||||
},
|
|
||||||
data[i].file,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(id, data, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
|
|
||||||
const certificates = await db.certificates.findByPk(id, {}, {transaction});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updatePayload = {};
|
|
||||||
|
|
||||||
if (data.serial !== undefined) updatePayload.serial = data.serial;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.issued_at !== undefined) updatePayload.issued_at = data.issued_at;
|
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
|
||||||
|
|
||||||
await certificates.update(updatePayload, {transaction});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.student !== undefined) {
|
|
||||||
await certificates.setStudent(
|
|
||||||
|
|
||||||
data.student,
|
|
||||||
|
|
||||||
{ transaction }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.course !== undefined) {
|
|
||||||
await certificates.setCourse(
|
|
||||||
|
|
||||||
data.course,
|
|
||||||
|
|
||||||
{ transaction }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.certificates.getTableName(),
|
|
||||||
belongsToColumn: 'file',
|
|
||||||
belongsToId: certificates.id,
|
|
||||||
},
|
|
||||||
data.file,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
const certificates = await db.certificates.findAll({
|
|
||||||
where: {
|
|
||||||
id: {
|
|
||||||
[Op.in]: ids,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
|
||||||
for (const record of certificates) {
|
|
||||||
await record.update(
|
|
||||||
{deletedBy: currentUser.id},
|
|
||||||
{transaction}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for (const record of certificates) {
|
|
||||||
await record.destroy({transaction});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async remove(id, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
const certificates = await db.certificates.findByPk(id, options);
|
|
||||||
|
|
||||||
await certificates.update({
|
|
||||||
deletedBy: currentUser.id
|
|
||||||
}, {
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await certificates.destroy({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async findBy(where, options) {
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
const certificates = await db.certificates.findOne(
|
|
||||||
{ where },
|
|
||||||
{ transaction },
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!certificates) {
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = certificates.get({plain: true});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.student = await certificates.getStudent({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.course = await certificates.getCourse({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.file = await certificates.getFile({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async findAll(
|
|
||||||
filter,
|
|
||||||
options
|
|
||||||
) {
|
|
||||||
const limit = filter.limit || 0;
|
|
||||||
let offset = 0;
|
|
||||||
let where = {};
|
|
||||||
const currentPage = +filter.page;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
offset = currentPage * limit;
|
|
||||||
|
|
||||||
const orderBy = null;
|
|
||||||
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
let include = [
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.users,
|
|
||||||
as: 'student',
|
|
||||||
|
|
||||||
where: filter.student ? {
|
|
||||||
[Op.or]: [
|
|
||||||
{ id: { [Op.in]: filter.student.split('|').map(term => Utils.uuid(term)) } },
|
|
||||||
{
|
|
||||||
firstName: {
|
|
||||||
[Op.or]: filter.student.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} : {},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.courses,
|
|
||||||
as: 'course',
|
|
||||||
|
|
||||||
where: filter.course ? {
|
|
||||||
[Op.or]: [
|
|
||||||
{ id: { [Op.in]: filter.course.split('|').map(term => Utils.uuid(term)) } },
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
[Op.or]: filter.course.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} : {},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.file,
|
|
||||||
as: 'file',
|
|
||||||
},
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
if (filter) {
|
|
||||||
if (filter.id) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
['id']: Utils.uuid(filter.id),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.serial) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'certificates',
|
|
||||||
'serial',
|
|
||||||
filter.serial,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.issued_atRange) {
|
|
||||||
const [start, end] = filter.issued_atRange;
|
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
issued_at: {
|
|
||||||
...where.issued_at,
|
|
||||||
[Op.gte]: start,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end !== undefined && end !== null && end !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
issued_at: {
|
|
||||||
...where.issued_at,
|
|
||||||
[Op.lte]: end,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.active !== undefined) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
active: filter.active === true || filter.active === 'true'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.createdAtRange) {
|
|
||||||
const [start, end] = filter.createdAtRange;
|
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
['createdAt']: {
|
|
||||||
...where.createdAt,
|
|
||||||
[Op.gte]: start,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end !== undefined && end !== null && end !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
['createdAt']: {
|
|
||||||
...where.createdAt,
|
|
||||||
[Op.lte]: end,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const queryOptions = {
|
|
||||||
where,
|
|
||||||
include,
|
|
||||||
distinct: true,
|
|
||||||
order: filter.field && filter.sort
|
|
||||||
? [[filter.field, filter.sort]]
|
|
||||||
: [['createdAt', 'desc']],
|
|
||||||
transaction: options?.transaction,
|
|
||||||
logging: console.log
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!options?.countOnly) {
|
|
||||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
|
||||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { rows, count } = await db.certificates.findAndCountAll(queryOptions);
|
|
||||||
|
|
||||||
return {
|
|
||||||
rows: options?.countOnly ? [] : rows,
|
|
||||||
count: count
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error executing query:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async findAllAutocomplete(query, limit, offset, ) {
|
|
||||||
let where = {};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (query) {
|
|
||||||
where = {
|
|
||||||
[Op.or]: [
|
|
||||||
{ ['id']: Utils.uuid(query) },
|
|
||||||
Utils.ilike(
|
|
||||||
'certificates',
|
|
||||||
'serial',
|
|
||||||
query,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const records = await db.certificates.findAll({
|
|
||||||
attributes: [ 'id', 'serial' ],
|
|
||||||
where,
|
|
||||||
limit: limit ? Number(limit) : undefined,
|
|
||||||
offset: offset ? Number(offset) : undefined,
|
|
||||||
orderBy: [['serial', 'ASC']],
|
|
||||||
});
|
|
||||||
|
|
||||||
return records.map((record) => ({
|
|
||||||
id: record.id,
|
|
||||||
label: record.serial,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,695 +0,0 @@
|
|||||||
|
|
||||||
const db = require('../models');
|
|
||||||
const FileDBApi = require('./file');
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const Utils = require('../utils');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Sequelize = db.Sequelize;
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
|
|
||||||
module.exports = class CoursesDBApi {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static async create(data, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
const courses = await db.courses.create(
|
|
||||||
{
|
|
||||||
id: data.id || undefined,
|
|
||||||
|
|
||||||
title: data.title
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
short_description: data.short_description
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
description: data.description
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
level: data.level
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
language: data.language
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
price: data.price
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
published: data.published
|
|
||||||
||
|
|
||||||
false
|
|
||||||
|
|
||||||
,
|
|
||||||
|
|
||||||
published_at: data.published_at
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
duration: data.duration
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
importHash: data.importHash || null,
|
|
||||||
createdById: currentUser.id,
|
|
||||||
updatedById: currentUser.id,
|
|
||||||
},
|
|
||||||
{ transaction },
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
await courses.setInstructor( data.instructor || null, {
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await courses.setCategory( data.category || null, {
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.courses.getTableName(),
|
|
||||||
belongsToColumn: 'thumbnail',
|
|
||||||
belongsToId: courses.id,
|
|
||||||
},
|
|
||||||
data.thumbnail,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return courses;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static async bulkImport(data, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
|
||||||
const coursesData = data.map((item, index) => ({
|
|
||||||
id: item.id || undefined,
|
|
||||||
|
|
||||||
title: item.title
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
short_description: item.short_description
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
description: item.description
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
level: item.level
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
language: item.language
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
price: item.price
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
published: item.published
|
|
||||||
||
|
|
||||||
false
|
|
||||||
|
|
||||||
,
|
|
||||||
|
|
||||||
published_at: item.published_at
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
duration: item.duration
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
|
||||||
createdById: currentUser.id,
|
|
||||||
updatedById: currentUser.id,
|
|
||||||
createdAt: new Date(Date.now() + index * 1000),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Bulk create items
|
|
||||||
const courses = await db.courses.bulkCreate(coursesData, { transaction });
|
|
||||||
|
|
||||||
// For each item created, replace relation files
|
|
||||||
|
|
||||||
for (let i = 0; i < courses.length; i++) {
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.courses.getTableName(),
|
|
||||||
belongsToColumn: 'thumbnail',
|
|
||||||
belongsToId: courses[i].id,
|
|
||||||
},
|
|
||||||
data[i].thumbnail,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return courses;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(id, data, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
|
|
||||||
const courses = await db.courses.findByPk(id, {}, {transaction});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updatePayload = {};
|
|
||||||
|
|
||||||
if (data.title !== undefined) updatePayload.title = data.title;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.short_description !== undefined) updatePayload.short_description = data.short_description;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.description !== undefined) updatePayload.description = data.description;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.level !== undefined) updatePayload.level = data.level;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.language !== undefined) updatePayload.language = data.language;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.price !== undefined) updatePayload.price = data.price;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.published !== undefined) updatePayload.published = data.published;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.published_at !== undefined) updatePayload.published_at = data.published_at;
|
|
||||||
|
|
||||||
|
|
||||||
if (data.duration !== undefined) updatePayload.duration = data.duration;
|
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
|
||||||
|
|
||||||
await courses.update(updatePayload, {transaction});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.instructor !== undefined) {
|
|
||||||
await courses.setInstructor(
|
|
||||||
|
|
||||||
data.instructor,
|
|
||||||
|
|
||||||
{ transaction }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.category !== undefined) {
|
|
||||||
await courses.setCategory(
|
|
||||||
|
|
||||||
data.category,
|
|
||||||
|
|
||||||
{ transaction }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.courses.getTableName(),
|
|
||||||
belongsToColumn: 'thumbnail',
|
|
||||||
belongsToId: courses.id,
|
|
||||||
},
|
|
||||||
data.thumbnail,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return courses;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
const courses = await db.courses.findAll({
|
|
||||||
where: {
|
|
||||||
id: {
|
|
||||||
[Op.in]: ids,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
|
||||||
for (const record of courses) {
|
|
||||||
await record.update(
|
|
||||||
{deletedBy: currentUser.id},
|
|
||||||
{transaction}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for (const record of courses) {
|
|
||||||
await record.destroy({transaction});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
return courses;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async remove(id, options) {
|
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
const courses = await db.courses.findByPk(id, options);
|
|
||||||
|
|
||||||
await courses.update({
|
|
||||||
deletedBy: currentUser.id
|
|
||||||
}, {
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await courses.destroy({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
return courses;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async findBy(where, options) {
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
const courses = await db.courses.findOne(
|
|
||||||
{ where },
|
|
||||||
{ transaction },
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!courses) {
|
|
||||||
return courses;
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = courses.get({plain: true});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.lessons_course = await courses.getLessons_course({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.enrollments_course = await courses.getEnrollments_course({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.announcements_course = await courses.getAnnouncements_course({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.certificates_course = await courses.getCertificates_course({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.instructor = await courses.getInstructor({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.category = await courses.getCategory({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.thumbnail = await courses.getThumbnail({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async findAll(
|
|
||||||
filter,
|
|
||||||
options
|
|
||||||
) {
|
|
||||||
const limit = filter.limit || 0;
|
|
||||||
let offset = 0;
|
|
||||||
let where = {};
|
|
||||||
const currentPage = +filter.page;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
offset = currentPage * limit;
|
|
||||||
|
|
||||||
const orderBy = null;
|
|
||||||
|
|
||||||
const transaction = (options && options.transaction) || undefined;
|
|
||||||
|
|
||||||
let include = [
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.users,
|
|
||||||
as: 'instructor',
|
|
||||||
|
|
||||||
where: filter.instructor ? {
|
|
||||||
[Op.or]: [
|
|
||||||
{ id: { [Op.in]: filter.instructor.split('|').map(term => Utils.uuid(term)) } },
|
|
||||||
{
|
|
||||||
firstName: {
|
|
||||||
[Op.or]: filter.instructor.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} : {},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.course_categories,
|
|
||||||
as: 'category',
|
|
||||||
|
|
||||||
where: filter.category ? {
|
|
||||||
[Op.or]: [
|
|
||||||
{ id: { [Op.in]: filter.category.split('|').map(term => Utils.uuid(term)) } },
|
|
||||||
{
|
|
||||||
name: {
|
|
||||||
[Op.or]: filter.category.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} : {},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.file,
|
|
||||||
as: 'thumbnail',
|
|
||||||
},
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
if (filter) {
|
|
||||||
if (filter.id) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
['id']: Utils.uuid(filter.id),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.title) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'courses',
|
|
||||||
'title',
|
|
||||||
filter.title,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.short_description) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'courses',
|
|
||||||
'short_description',
|
|
||||||
filter.short_description,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.description) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'courses',
|
|
||||||
'description',
|
|
||||||
filter.description,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.language) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'courses',
|
|
||||||
'language',
|
|
||||||
filter.language,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.priceRange) {
|
|
||||||
const [start, end] = filter.priceRange;
|
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
price: {
|
|
||||||
...where.price,
|
|
||||||
[Op.gte]: start,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end !== undefined && end !== null && end !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
price: {
|
|
||||||
...where.price,
|
|
||||||
[Op.lte]: end,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.published_atRange) {
|
|
||||||
const [start, end] = filter.published_atRange;
|
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
published_at: {
|
|
||||||
...where.published_at,
|
|
||||||
[Op.gte]: start,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end !== undefined && end !== null && end !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
published_at: {
|
|
||||||
...where.published_at,
|
|
||||||
[Op.lte]: end,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.durationRange) {
|
|
||||||
const [start, end] = filter.durationRange;
|
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
duration: {
|
|
||||||
...where.duration,
|
|
||||||
[Op.gte]: start,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end !== undefined && end !== null && end !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
duration: {
|
|
||||||
...where.duration,
|
|
||||||
[Op.lte]: end,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.active !== undefined) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
active: filter.active === true || filter.active === 'true'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.level) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
level: filter.level,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.published) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
published: filter.published,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.createdAtRange) {
|
|
||||||
const [start, end] = filter.createdAtRange;
|
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
['createdAt']: {
|
|
||||||
...where.createdAt,
|
|
||||||
[Op.gte]: start,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end !== undefined && end !== null && end !== '') {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
['createdAt']: {
|
|
||||||
...where.createdAt,
|
|
||||||
[Op.lte]: end,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const queryOptions = {
|
|
||||||
where,
|
|
||||||
include,
|
|
||||||
distinct: true,
|
|
||||||
order: filter.field && filter.sort
|
|
||||||
? [[filter.field, filter.sort]]
|
|
||||||
: [['createdAt', 'desc']],
|
|
||||||
transaction: options?.transaction,
|
|
||||||
logging: console.log
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!options?.countOnly) {
|
|
||||||
queryOptions.limit = limit ? Number(limit) : undefined;
|
|
||||||
queryOptions.offset = offset ? Number(offset) : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { rows, count } = await db.courses.findAndCountAll(queryOptions);
|
|
||||||
|
|
||||||
return {
|
|
||||||
rows: options?.countOnly ? [] : rows,
|
|
||||||
count: count
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error executing query:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async findAllAutocomplete(query, limit, offset, ) {
|
|
||||||
let where = {};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (query) {
|
|
||||||
where = {
|
|
||||||
[Op.or]: [
|
|
||||||
{ ['id']: Utils.uuid(query) },
|
|
||||||
Utils.ilike(
|
|
||||||
'courses',
|
|
||||||
'title',
|
|
||||||
query,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const records = await db.courses.findAll({
|
|
||||||
attributes: [ 'id', 'title' ],
|
|
||||||
where,
|
|
||||||
limit: limit ? Number(limit) : undefined,
|
|
||||||
offset: offset ? Number(offset) : undefined,
|
|
||||||
orderBy: [['title', 'ASC']],
|
|
||||||
});
|
|
||||||
|
|
||||||
return records.map((record) => ({
|
|
||||||
id: record.id,
|
|
||||||
label: record.title,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = class Quiz_questionsDBApi {
|
module.exports = class CustomersDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -17,26 +17,42 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const quiz_questions = await db.quiz_questions.create(
|
const customers = await db.customers.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
question: data.question
|
name: data.name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
question_type: data.question_type
|
email: data.email
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
choices: data.choices
|
phone: data.phone
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
answer: data.answer
|
address: data.address
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
notes: data.notes
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
vip: data.vip
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
tax_number: data.tax_number
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -49,16 +65,12 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await quiz_questions.setQuiz( data.quiz || null, {
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return quiz_questions;
|
return customers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -67,25 +79,41 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// Prepare data - wrapping individual data transformations in a map() method
|
||||||
const quiz_questionsData = data.map((item, index) => ({
|
const customersData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
question: item.question
|
name: item.name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
question_type: item.question_type
|
email: item.email
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
choices: item.choices
|
phone: item.phone
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
answer: item.answer
|
address: item.address
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
notes: item.notes
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
vip: item.vip
|
||||||
|
||
|
||||||
|
false
|
||||||
|
|
||||||
|
,
|
||||||
|
|
||||||
|
tax_number: item.tax_number
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -97,12 +125,12 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// Bulk create items
|
||||||
const quiz_questions = await db.quiz_questions.bulkCreate(quiz_questionsData, { transaction });
|
const customers = await db.customers.bulkCreate(customersData, { transaction });
|
||||||
|
|
||||||
// For each item created, replace relation files
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
return quiz_questions;
|
return customers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -110,54 +138,54 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
const quiz_questions = await db.quiz_questions.findByPk(id, {}, {transaction});
|
const customers = await db.customers.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updatePayload = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
if (data.question !== undefined) updatePayload.question = data.question;
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
|
||||||
|
|
||||||
if (data.question_type !== undefined) updatePayload.question_type = data.question_type;
|
if (data.email !== undefined) updatePayload.email = data.email;
|
||||||
|
|
||||||
|
|
||||||
if (data.choices !== undefined) updatePayload.choices = data.choices;
|
if (data.phone !== undefined) updatePayload.phone = data.phone;
|
||||||
|
|
||||||
|
|
||||||
if (data.answer !== undefined) updatePayload.answer = data.answer;
|
if (data.address !== undefined) updatePayload.address = data.address;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.notes !== undefined) updatePayload.notes = data.notes;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.vip !== undefined) updatePayload.vip = data.vip;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.tax_number !== undefined) updatePayload.tax_number = data.tax_number;
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await quiz_questions.update(updatePayload, {transaction});
|
await customers.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.quiz !== undefined) {
|
|
||||||
await quiz_questions.setQuiz(
|
|
||||||
|
|
||||||
data.quiz,
|
|
||||||
|
|
||||||
{ transaction }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return quiz_questions;
|
return customers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const quiz_questions = await db.quiz_questions.findAll({
|
const customers = await db.customers.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -167,53 +195,53 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of quiz_questions) {
|
for (const record of customers) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of quiz_questions) {
|
for (const record of customers) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return quiz_questions;
|
return customers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const quiz_questions = await db.quiz_questions.findByPk(id, options);
|
const customers = await db.customers.findByPk(id, options);
|
||||||
|
|
||||||
await quiz_questions.update({
|
await customers.update({
|
||||||
deletedBy: currentUser.id
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await quiz_questions.destroy({
|
await customers.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return quiz_questions;
|
return customers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const quiz_questions = await db.quiz_questions.findOne(
|
const customers = await db.customers.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!quiz_questions) {
|
if (!customers) {
|
||||||
return quiz_questions;
|
return customers;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = quiz_questions.get({plain: true});
|
const output = customers.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -222,18 +250,15 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.orders_customer = await customers.getOrders_customer({
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.quiz = await quiz_questions.getQuiz({
|
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -259,23 +284,6 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
|
|
||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
{
|
|
||||||
model: db.quizzes,
|
|
||||||
as: 'quiz',
|
|
||||||
|
|
||||||
where: filter.quiz ? {
|
|
||||||
[Op.or]: [
|
|
||||||
{ id: { [Op.in]: filter.quiz.split('|').map(term => Utils.uuid(term)) } },
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
[Op.or]: filter.quiz.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} : {},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
@ -289,35 +297,68 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.question) {
|
if (filter.name) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'quiz_questions',
|
'customers',
|
||||||
'question',
|
'name',
|
||||||
filter.question,
|
filter.name,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.choices) {
|
if (filter.email) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'quiz_questions',
|
'customers',
|
||||||
'choices',
|
'email',
|
||||||
filter.choices,
|
filter.email,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.answer) {
|
if (filter.phone) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'quiz_questions',
|
'customers',
|
||||||
'answer',
|
'phone',
|
||||||
filter.answer,
|
filter.phone,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.address) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'customers',
|
||||||
|
'address',
|
||||||
|
filter.address,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.notes) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'customers',
|
||||||
|
'notes',
|
||||||
|
filter.notes,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.tax_number) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'customers',
|
||||||
|
'tax_number',
|
||||||
|
filter.tax_number,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -336,17 +377,15 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.question_type) {
|
if (filter.vip) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
question_type: filter.question_type,
|
vip: filter.vip,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.createdAtRange) {
|
if (filter.createdAtRange) {
|
||||||
@ -394,7 +433,7 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.quiz_questions.findAndCountAll(queryOptions);
|
const { rows, count } = await db.customers.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -416,25 +455,25 @@ module.exports = class Quiz_questionsDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'quiz_questions',
|
'customers',
|
||||||
'question',
|
'name',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.quiz_questions.findAll({
|
const records = await db.customers.findAll({
|
||||||
attributes: [ 'id', 'question' ],
|
attributes: [ 'id', 'name' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['question', 'ASC']],
|
orderBy: [['name', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.question,
|
label: record.name,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = class ProgressDBApi {
|
module.exports = class Order_itemsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -17,32 +17,26 @@ module.exports = class ProgressDBApi {
|
|||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const progress = await db.progress.create(
|
const order_items = await db.order_items.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
summary: data.summary
|
name: data.name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
completed: data.completed
|
quantity: data.quantity
|
||||||
||
|
|
||||||
false
|
|
||||||
|
|
||||||
,
|
|
||||||
|
|
||||||
completed_at: data.completed_at
|
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
percent: data.percent
|
unit_price: data.unit_price
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
notes: data.notes
|
total_price: data.total_price
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -55,11 +49,11 @@ module.exports = class ProgressDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await progress.setStudent( data.student || null, {
|
await order_items.setOrder( data.order || null, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await progress.setLesson( data.lesson || null, {
|
await order_items.setProduct( data.product || null, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -68,7 +62,7 @@ module.exports = class ProgressDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return progress;
|
return order_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -77,31 +71,25 @@ module.exports = class ProgressDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// Prepare data - wrapping individual data transformations in a map() method
|
||||||
const progressData = data.map((item, index) => ({
|
const order_itemsData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
summary: item.summary
|
name: item.name
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
completed: item.completed
|
quantity: item.quantity
|
||||||
||
|
|
||||||
false
|
|
||||||
|
|
||||||
,
|
|
||||||
|
|
||||||
completed_at: item.completed_at
|
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
percent: item.percent
|
unit_price: item.unit_price
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
notes: item.notes
|
total_price: item.total_price
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -113,12 +101,12 @@ module.exports = class ProgressDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// Bulk create items
|
||||||
const progress = await db.progress.bulkCreate(progressData, { transaction });
|
const order_items = await db.order_items.bulkCreate(order_itemsData, { transaction });
|
||||||
|
|
||||||
// For each item created, replace relation files
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
return progress;
|
return order_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -126,47 +114,44 @@ module.exports = class ProgressDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
const progress = await db.progress.findByPk(id, {}, {transaction});
|
const order_items = await db.order_items.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updatePayload = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
if (data.summary !== undefined) updatePayload.summary = data.summary;
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
|
||||||
|
|
||||||
if (data.completed !== undefined) updatePayload.completed = data.completed;
|
if (data.quantity !== undefined) updatePayload.quantity = data.quantity;
|
||||||
|
|
||||||
|
|
||||||
if (data.completed_at !== undefined) updatePayload.completed_at = data.completed_at;
|
if (data.unit_price !== undefined) updatePayload.unit_price = data.unit_price;
|
||||||
|
|
||||||
|
|
||||||
if (data.percent !== undefined) updatePayload.percent = data.percent;
|
if (data.total_price !== undefined) updatePayload.total_price = data.total_price;
|
||||||
|
|
||||||
|
|
||||||
if (data.notes !== undefined) updatePayload.notes = data.notes;
|
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await progress.update(updatePayload, {transaction});
|
await order_items.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.student !== undefined) {
|
if (data.order !== undefined) {
|
||||||
await progress.setStudent(
|
await order_items.setOrder(
|
||||||
|
|
||||||
data.student,
|
data.order,
|
||||||
|
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.lesson !== undefined) {
|
if (data.product !== undefined) {
|
||||||
await progress.setLesson(
|
await order_items.setProduct(
|
||||||
|
|
||||||
data.lesson,
|
data.product,
|
||||||
|
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
@ -178,14 +163,14 @@ module.exports = class ProgressDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return progress;
|
return order_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const progress = await db.progress.findAll({
|
const order_items = await db.order_items.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -195,53 +180,53 @@ module.exports = class ProgressDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of progress) {
|
for (const record of order_items) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of progress) {
|
for (const record of order_items) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return progress;
|
return order_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const progress = await db.progress.findByPk(id, options);
|
const order_items = await db.order_items.findByPk(id, options);
|
||||||
|
|
||||||
await progress.update({
|
await order_items.update({
|
||||||
deletedBy: currentUser.id
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await progress.destroy({
|
await order_items.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return progress;
|
return order_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const progress = await db.progress.findOne(
|
const order_items = await db.order_items.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!progress) {
|
if (!order_items) {
|
||||||
return progress;
|
return order_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = progress.get({plain: true});
|
const output = order_items.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -255,14 +240,12 @@ module.exports = class ProgressDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.order = await order_items.getOrder({
|
||||||
|
|
||||||
output.student = await progress.getStudent({
|
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
output.lesson = await progress.getLesson({
|
output.product = await order_items.getProduct({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -293,15 +276,15 @@ module.exports = class ProgressDBApi {
|
|||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.users,
|
model: db.orders,
|
||||||
as: 'student',
|
as: 'order',
|
||||||
|
|
||||||
where: filter.student ? {
|
where: filter.order ? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ id: { [Op.in]: filter.student.split('|').map(term => Utils.uuid(term)) } },
|
{ id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } },
|
||||||
{
|
{
|
||||||
firstName: {
|
order_number: {
|
||||||
[Op.or]: filter.student.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -310,15 +293,15 @@ module.exports = class ProgressDBApi {
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.lessons,
|
model: db.products,
|
||||||
as: 'lesson',
|
as: 'product',
|
||||||
|
|
||||||
where: filter.lesson ? {
|
where: filter.product ? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ id: { [Op.in]: filter.lesson.split('|').map(term => Utils.uuid(term)) } },
|
{ id: { [Op.in]: filter.product.split('|').map(term => Utils.uuid(term)) } },
|
||||||
{
|
{
|
||||||
title: {
|
name: {
|
||||||
[Op.or]: filter.lesson.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[Op.or]: filter.product.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -339,24 +322,13 @@ module.exports = class ProgressDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.summary) {
|
if (filter.name) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'progress',
|
'order_items',
|
||||||
'summary',
|
'name',
|
||||||
filter.summary,
|
filter.name,
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.notes) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'progress',
|
|
||||||
'notes',
|
|
||||||
filter.notes,
|
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -366,14 +338,14 @@ module.exports = class ProgressDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.completed_atRange) {
|
if (filter.quantityRange) {
|
||||||
const [start, end] = filter.completed_atRange;
|
const [start, end] = filter.quantityRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
completed_at: {
|
quantity: {
|
||||||
...where.completed_at,
|
...where.quantity,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -382,22 +354,22 @@ module.exports = class ProgressDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
completed_at: {
|
quantity: {
|
||||||
...where.completed_at,
|
...where.quantity,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.percentRange) {
|
if (filter.unit_priceRange) {
|
||||||
const [start, end] = filter.percentRange;
|
const [start, end] = filter.unit_priceRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
percent: {
|
unit_price: {
|
||||||
...where.percent,
|
...where.unit_price,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -406,8 +378,32 @@ module.exports = class ProgressDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
percent: {
|
unit_price: {
|
||||||
...where.percent,
|
...where.unit_price,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.total_priceRange) {
|
||||||
|
const [start, end] = filter.total_priceRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
total_price: {
|
||||||
|
...where.total_price,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
total_price: {
|
||||||
|
...where.total_price,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -423,13 +419,6 @@ module.exports = class ProgressDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.completed) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
completed: filter.completed,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -483,7 +472,7 @@ module.exports = class ProgressDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.progress.findAndCountAll(queryOptions);
|
const { rows, count } = await db.order_items.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -505,25 +494,25 @@ module.exports = class ProgressDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'progress',
|
'order_items',
|
||||||
'summary',
|
'name',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.progress.findAll({
|
const records = await db.order_items.findAll({
|
||||||
attributes: [ 'id', 'summary' ],
|
attributes: [ 'id', 'name' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['summary', 'ASC']],
|
orderBy: [['name', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.summary,
|
label: record.name,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = class LessonsDBApi {
|
module.exports = class OrdersDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -17,44 +17,48 @@ module.exports = class LessonsDBApi {
|
|||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const lessons = await db.lessons.create(
|
const orders = await db.orders.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
title: data.title
|
order_number: data.order_number
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
content: data.content
|
status: data.status
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
order: data.order
|
total: data.total
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
duration: data.duration
|
placed_at: data.placed_at
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
start_at: data.start_at
|
shipped_at: data.shipped_at
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
end_at: data.end_at
|
delivery_date: data.delivery_date
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
is_published: data.is_published
|
payment_status: data.payment_status
|
||||||
||
|
||
|
||||||
false
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
shipping_address: data.shipping_address
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: data.importHash || null,
|
importHash: data.importHash || null,
|
||||||
@ -65,7 +69,7 @@ module.exports = class LessonsDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await lessons.setCourse( data.course || null, {
|
await orders.setCustomer( data.customer || null, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,18 +77,8 @@ module.exports = class LessonsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.lessons.getTableName(),
|
|
||||||
belongsToColumn: 'video_files',
|
|
||||||
belongsToId: lessons.id,
|
|
||||||
},
|
|
||||||
data.video_files,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return lessons;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -93,43 +87,47 @@ module.exports = class LessonsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// Prepare data - wrapping individual data transformations in a map() method
|
||||||
const lessonsData = data.map((item, index) => ({
|
const ordersData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
title: item.title
|
order_number: item.order_number
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
content: item.content
|
status: item.status
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
order: item.order
|
total: item.total
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
duration: item.duration
|
placed_at: item.placed_at
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
start_at: item.start_at
|
shipped_at: item.shipped_at
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
end_at: item.end_at
|
delivery_date: item.delivery_date
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
is_published: item.is_published
|
payment_status: item.payment_status
|
||||||
||
|
||
|
||||||
false
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
shipping_address: item.shipping_address
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -139,24 +137,12 @@ module.exports = class LessonsDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// Bulk create items
|
||||||
const lessons = await db.lessons.bulkCreate(lessonsData, { transaction });
|
const orders = await db.orders.bulkCreate(ordersData, { transaction });
|
||||||
|
|
||||||
// For each item created, replace relation files
|
// For each item created, replace relation files
|
||||||
|
|
||||||
for (let i = 0; i < lessons.length; i++) {
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.lessons.getTableName(),
|
|
||||||
belongsToColumn: 'video_files',
|
|
||||||
belongsToId: lessons[i].id,
|
|
||||||
},
|
|
||||||
data[i].video_files,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return lessons;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -164,44 +150,47 @@ module.exports = class LessonsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
const lessons = await db.lessons.findByPk(id, {}, {transaction});
|
const orders = await db.orders.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updatePayload = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
if (data.title !== undefined) updatePayload.title = data.title;
|
if (data.order_number !== undefined) updatePayload.order_number = data.order_number;
|
||||||
|
|
||||||
|
|
||||||
if (data.content !== undefined) updatePayload.content = data.content;
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
if (data.order !== undefined) updatePayload.order = data.order;
|
if (data.total !== undefined) updatePayload.total = data.total;
|
||||||
|
|
||||||
|
|
||||||
if (data.duration !== undefined) updatePayload.duration = data.duration;
|
if (data.placed_at !== undefined) updatePayload.placed_at = data.placed_at;
|
||||||
|
|
||||||
|
|
||||||
if (data.start_at !== undefined) updatePayload.start_at = data.start_at;
|
if (data.shipped_at !== undefined) updatePayload.shipped_at = data.shipped_at;
|
||||||
|
|
||||||
|
|
||||||
if (data.end_at !== undefined) updatePayload.end_at = data.end_at;
|
if (data.delivery_date !== undefined) updatePayload.delivery_date = data.delivery_date;
|
||||||
|
|
||||||
|
|
||||||
if (data.is_published !== undefined) updatePayload.is_published = data.is_published;
|
if (data.payment_status !== undefined) updatePayload.payment_status = data.payment_status;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.shipping_address !== undefined) updatePayload.shipping_address = data.shipping_address;
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await lessons.update(updatePayload, {transaction});
|
await orders.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.course !== undefined) {
|
if (data.customer !== undefined) {
|
||||||
await lessons.setCourse(
|
await orders.setCustomer(
|
||||||
|
|
||||||
data.course,
|
data.customer,
|
||||||
|
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
@ -212,25 +201,15 @@ module.exports = class LessonsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
|
||||||
{
|
|
||||||
belongsTo: db.lessons.getTableName(),
|
|
||||||
belongsToColumn: 'video_files',
|
|
||||||
belongsToId: lessons.id,
|
|
||||||
},
|
|
||||||
data.video_files,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return lessons;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const lessons = await db.lessons.findAll({
|
const orders = await db.orders.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -240,53 +219,53 @@ module.exports = class LessonsDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of lessons) {
|
for (const record of orders) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of lessons) {
|
for (const record of orders) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return lessons;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const lessons = await db.lessons.findByPk(id, options);
|
const orders = await db.orders.findByPk(id, options);
|
||||||
|
|
||||||
await lessons.update({
|
await orders.update({
|
||||||
deletedBy: currentUser.id
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await lessons.destroy({
|
await orders.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return lessons;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const lessons = await db.lessons.findOne(
|
const orders = await db.orders.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!lessons) {
|
if (!orders) {
|
||||||
return lessons;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = lessons.get({plain: true});
|
const output = orders.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -296,26 +275,23 @@ module.exports = class LessonsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.progress_lesson = await lessons.getProgress_lesson({
|
output.order_items_order = await orders.getOrder_items_order({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
output.quizzes_lesson = await lessons.getQuizzes_lesson({
|
output.payments_order = await orders.getPayments_order({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.shipments_order = await orders.getShipments_order({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.customer = await orders.getCustomer({
|
||||||
|
|
||||||
|
|
||||||
output.course = await lessons.getCourse({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.video_files = await lessons.getVideo_files({
|
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -346,15 +322,15 @@ module.exports = class LessonsDBApi {
|
|||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.courses,
|
model: db.customers,
|
||||||
as: 'course',
|
as: 'customer',
|
||||||
|
|
||||||
where: filter.course ? {
|
where: filter.customer ? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ id: { [Op.in]: filter.course.split('|').map(term => Utils.uuid(term)) } },
|
{ id: { [Op.in]: filter.customer.split('|').map(term => Utils.uuid(term)) } },
|
||||||
{
|
{
|
||||||
title: {
|
name: {
|
||||||
[Op.or]: filter.course.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[Op.or]: filter.customer.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -364,11 +340,6 @@ module.exports = class LessonsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.file,
|
|
||||||
as: 'video_files',
|
|
||||||
},
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
@ -380,24 +351,24 @@ module.exports = class LessonsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.title) {
|
if (filter.order_number) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'lessons',
|
'orders',
|
||||||
'title',
|
'order_number',
|
||||||
filter.title,
|
filter.order_number,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.content) {
|
if (filter.shipping_address) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'lessons',
|
'orders',
|
||||||
'content',
|
'shipping_address',
|
||||||
filter.content,
|
filter.shipping_address,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -405,34 +376,16 @@ module.exports = class LessonsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.calendarStart && filter.calendarEnd) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.or]: [
|
|
||||||
{
|
|
||||||
start_at: {
|
|
||||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
end_at: {
|
|
||||||
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.orderRange) {
|
if (filter.totalRange) {
|
||||||
const [start, end] = filter.orderRange;
|
const [start, end] = filter.totalRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
order: {
|
total: {
|
||||||
...where.order,
|
...where.total,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -441,22 +394,22 @@ module.exports = class LessonsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
order: {
|
total: {
|
||||||
...where.order,
|
...where.total,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.durationRange) {
|
if (filter.placed_atRange) {
|
||||||
const [start, end] = filter.durationRange;
|
const [start, end] = filter.placed_atRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
duration: {
|
placed_at: {
|
||||||
...where.duration,
|
...where.placed_at,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -465,22 +418,22 @@ module.exports = class LessonsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
duration: {
|
placed_at: {
|
||||||
...where.duration,
|
...where.placed_at,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.start_atRange) {
|
if (filter.shipped_atRange) {
|
||||||
const [start, end] = filter.start_atRange;
|
const [start, end] = filter.shipped_atRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
start_at: {
|
shipped_at: {
|
||||||
...where.start_at,
|
...where.shipped_at,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -489,22 +442,22 @@ module.exports = class LessonsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
start_at: {
|
shipped_at: {
|
||||||
...where.start_at,
|
...where.shipped_at,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.end_atRange) {
|
if (filter.delivery_dateRange) {
|
||||||
const [start, end] = filter.end_atRange;
|
const [start, end] = filter.delivery_dateRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
end_at: {
|
delivery_date: {
|
||||||
...where.end_at,
|
...where.delivery_date,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -513,8 +466,8 @@ module.exports = class LessonsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
end_at: {
|
delivery_date: {
|
||||||
...where.end_at,
|
...where.delivery_date,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -530,10 +483,17 @@ module.exports = class LessonsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.is_published) {
|
if (filter.status) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
is_published: filter.is_published,
|
status: filter.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.payment_status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
payment_status: filter.payment_status,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,7 +548,7 @@ module.exports = class LessonsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.lessons.findAndCountAll(queryOptions);
|
const { rows, count } = await db.orders.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -610,25 +570,25 @@ module.exports = class LessonsDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'lessons',
|
'orders',
|
||||||
'title',
|
'order_number',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.lessons.findAll({
|
const records = await db.orders.findAll({
|
||||||
attributes: [ 'id', 'title' ],
|
attributes: [ 'id', 'order_number' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['title', 'ASC']],
|
orderBy: [['order_number', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.title,
|
label: record.order_number,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = class QuizzesDBApi {
|
module.exports = class PaymentsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -17,34 +17,33 @@ module.exports = class QuizzesDBApi {
|
|||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const quizzes = await db.quizzes.create(
|
const payments = await db.payments.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
title: data.title
|
reference: data.reference
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
description: data.description
|
amount: data.amount
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
passing_score: data.passing_score
|
method: data.method
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
time_limit: data.time_limit
|
status: data.status
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
is_active: data.is_active
|
paid_at: data.paid_at
|
||||||
||
|
||
|
||||||
false
|
null
|
||||||
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: data.importHash || null,
|
importHash: data.importHash || null,
|
||||||
@ -55,7 +54,7 @@ module.exports = class QuizzesDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await quizzes.setLesson( data.lesson || null, {
|
await payments.setOrder( data.order || null, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -64,7 +63,7 @@ module.exports = class QuizzesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return quizzes;
|
return payments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -73,33 +72,32 @@ module.exports = class QuizzesDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// Prepare data - wrapping individual data transformations in a map() method
|
||||||
const quizzesData = data.map((item, index) => ({
|
const paymentsData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
title: item.title
|
reference: item.reference
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
description: item.description
|
amount: item.amount
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
passing_score: item.passing_score
|
method: item.method
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
time_limit: item.time_limit
|
status: item.status
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
is_active: item.is_active
|
paid_at: item.paid_at
|
||||||
||
|
||
|
||||||
false
|
null
|
||||||
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -109,12 +107,12 @@ module.exports = class QuizzesDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// Bulk create items
|
||||||
const quizzes = await db.quizzes.bulkCreate(quizzesData, { transaction });
|
const payments = await db.payments.bulkCreate(paymentsData, { transaction });
|
||||||
|
|
||||||
// For each item created, replace relation files
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
return quizzes;
|
return payments;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -122,38 +120,38 @@ module.exports = class QuizzesDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
const quizzes = await db.quizzes.findByPk(id, {}, {transaction});
|
const payments = await db.payments.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updatePayload = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
if (data.title !== undefined) updatePayload.title = data.title;
|
if (data.reference !== undefined) updatePayload.reference = data.reference;
|
||||||
|
|
||||||
|
|
||||||
if (data.description !== undefined) updatePayload.description = data.description;
|
if (data.amount !== undefined) updatePayload.amount = data.amount;
|
||||||
|
|
||||||
|
|
||||||
if (data.passing_score !== undefined) updatePayload.passing_score = data.passing_score;
|
if (data.method !== undefined) updatePayload.method = data.method;
|
||||||
|
|
||||||
|
|
||||||
if (data.time_limit !== undefined) updatePayload.time_limit = data.time_limit;
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
if (data.is_active !== undefined) updatePayload.is_active = data.is_active;
|
if (data.paid_at !== undefined) updatePayload.paid_at = data.paid_at;
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await quizzes.update(updatePayload, {transaction});
|
await payments.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.lesson !== undefined) {
|
if (data.order !== undefined) {
|
||||||
await quizzes.setLesson(
|
await payments.setOrder(
|
||||||
|
|
||||||
data.lesson,
|
data.order,
|
||||||
|
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
@ -165,14 +163,14 @@ module.exports = class QuizzesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return quizzes;
|
return payments;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const quizzes = await db.quizzes.findAll({
|
const payments = await db.payments.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -182,53 +180,53 @@ module.exports = class QuizzesDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of quizzes) {
|
for (const record of payments) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of quizzes) {
|
for (const record of payments) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return quizzes;
|
return payments;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const quizzes = await db.quizzes.findByPk(id, options);
|
const payments = await db.payments.findByPk(id, options);
|
||||||
|
|
||||||
await quizzes.update({
|
await payments.update({
|
||||||
deletedBy: currentUser.id
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await quizzes.destroy({
|
await payments.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return quizzes;
|
return payments;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const quizzes = await db.quizzes.findOne(
|
const payments = await db.payments.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!quizzes) {
|
if (!payments) {
|
||||||
return quizzes;
|
return payments;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = quizzes.get({plain: true});
|
const output = payments.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -240,15 +238,9 @@ module.exports = class QuizzesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.quiz_questions_quiz = await quizzes.getQuiz_questions_quiz({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
output.order = await payments.getOrder({
|
||||||
|
|
||||||
|
|
||||||
output.lesson = await quizzes.getLesson({
|
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -279,15 +271,15 @@ module.exports = class QuizzesDBApi {
|
|||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.lessons,
|
model: db.orders,
|
||||||
as: 'lesson',
|
as: 'order',
|
||||||
|
|
||||||
where: filter.lesson ? {
|
where: filter.order ? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ id: { [Op.in]: filter.lesson.split('|').map(term => Utils.uuid(term)) } },
|
{ id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } },
|
||||||
{
|
{
|
||||||
title: {
|
order_number: {
|
||||||
[Op.or]: filter.lesson.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -308,24 +300,13 @@ module.exports = class QuizzesDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.title) {
|
if (filter.reference) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'quizzes',
|
'payments',
|
||||||
'title',
|
'reference',
|
||||||
filter.title,
|
filter.reference,
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.description) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
[Op.and]: Utils.ilike(
|
|
||||||
'quizzes',
|
|
||||||
'description',
|
|
||||||
filter.description,
|
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -335,14 +316,14 @@ module.exports = class QuizzesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.passing_scoreRange) {
|
if (filter.amountRange) {
|
||||||
const [start, end] = filter.passing_scoreRange;
|
const [start, end] = filter.amountRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
passing_score: {
|
amount: {
|
||||||
...where.passing_score,
|
...where.amount,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -351,22 +332,22 @@ module.exports = class QuizzesDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
passing_score: {
|
amount: {
|
||||||
...where.passing_score,
|
...where.amount,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.time_limitRange) {
|
if (filter.paid_atRange) {
|
||||||
const [start, end] = filter.time_limitRange;
|
const [start, end] = filter.paid_atRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
time_limit: {
|
paid_at: {
|
||||||
...where.time_limit,
|
...where.paid_at,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -375,8 +356,8 @@ module.exports = class QuizzesDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
time_limit: {
|
paid_at: {
|
||||||
...where.time_limit,
|
...where.paid_at,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -392,10 +373,17 @@ module.exports = class QuizzesDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.is_active) {
|
if (filter.method) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
is_active: filter.is_active,
|
method: filter.method,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.status) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
status: filter.status,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,7 +438,7 @@ module.exports = class QuizzesDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.quizzes.findAndCountAll(queryOptions);
|
const { rows, count } = await db.payments.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -472,25 +460,25 @@ module.exports = class QuizzesDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'quizzes',
|
'payments',
|
||||||
'title',
|
'reference',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.quizzes.findAll({
|
const records = await db.payments.findAll({
|
||||||
attributes: [ 'id', 'title' ],
|
attributes: [ 'id', 'reference' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['title', 'ASC']],
|
orderBy: [['reference', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.title,
|
label: record.reference,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,8 +175,6 @@ module.exports = class PermissionsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = class EnrollmentsDBApi {
|
module.exports = class ProductsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -17,27 +17,37 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const enrollments = await db.enrollments.create(
|
const products = await db.products.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
enrollment_code: data.enrollment_code
|
sku: data.sku
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
enrolled_at: data.enrolled_at
|
name: data.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
description: data.description
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
price: data.price
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
stock: data.stock
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
status: data.status
|
status: data.status
|
||||||
||
|
||
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
progress_percent: data.progress_percent
|
|
||||||
||
|
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
@ -49,11 +59,7 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await enrollments.setStudent( data.student || null, {
|
await products.setCategory( data.category || null, {
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await enrollments.setCourse( data.course || null, {
|
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -63,16 +69,16 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
await FileDBApi.replaceRelationFiles(
|
||||||
{
|
{
|
||||||
belongsTo: db.enrollments.getTableName(),
|
belongsTo: db.products.getTableName(),
|
||||||
belongsToColumn: 'certificate',
|
belongsToColumn: 'images',
|
||||||
belongsToId: enrollments.id,
|
belongsToId: products.id,
|
||||||
},
|
},
|
||||||
data.certificate,
|
data.images,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
return enrollments;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,15 +87,30 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// Prepare data - wrapping individual data transformations in a map() method
|
||||||
const enrollmentsData = data.map((item, index) => ({
|
const productsData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
enrollment_code: item.enrollment_code
|
sku: item.sku
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
enrolled_at: item.enrolled_at
|
name: item.name
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
description: item.description
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
price: item.price
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
stock: item.stock
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
@ -97,11 +118,6 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
status: item.status
|
status: item.status
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
|
||||||
|
|
||||||
progress_percent: item.progress_percent
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -111,24 +127,24 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// Bulk create items
|
||||||
const enrollments = await db.enrollments.bulkCreate(enrollmentsData, { transaction });
|
const products = await db.products.bulkCreate(productsData, { transaction });
|
||||||
|
|
||||||
// For each item created, replace relation files
|
// For each item created, replace relation files
|
||||||
|
|
||||||
for (let i = 0; i < enrollments.length; i++) {
|
for (let i = 0; i < products.length; i++) {
|
||||||
await FileDBApi.replaceRelationFiles(
|
await FileDBApi.replaceRelationFiles(
|
||||||
{
|
{
|
||||||
belongsTo: db.enrollments.getTableName(),
|
belongsTo: db.products.getTableName(),
|
||||||
belongsToColumn: 'certificate',
|
belongsToColumn: 'images',
|
||||||
belongsToId: enrollments[i].id,
|
belongsToId: products[i].id,
|
||||||
},
|
},
|
||||||
data[i].certificate,
|
data[i].images,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return enrollments;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -136,44 +152,41 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
const enrollments = await db.enrollments.findByPk(id, {}, {transaction});
|
const products = await db.products.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updatePayload = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
if (data.enrollment_code !== undefined) updatePayload.enrollment_code = data.enrollment_code;
|
if (data.sku !== undefined) updatePayload.sku = data.sku;
|
||||||
|
|
||||||
|
|
||||||
if (data.enrolled_at !== undefined) updatePayload.enrolled_at = data.enrolled_at;
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.description !== undefined) updatePayload.description = data.description;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.price !== undefined) updatePayload.price = data.price;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.stock !== undefined) updatePayload.stock = data.stock;
|
||||||
|
|
||||||
|
|
||||||
if (data.status !== undefined) updatePayload.status = data.status;
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
if (data.progress_percent !== undefined) updatePayload.progress_percent = data.progress_percent;
|
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await enrollments.update(updatePayload, {transaction});
|
await products.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.student !== undefined) {
|
if (data.category !== undefined) {
|
||||||
await enrollments.setStudent(
|
await products.setCategory(
|
||||||
|
|
||||||
data.student,
|
data.category,
|
||||||
|
|
||||||
{ transaction }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.course !== undefined) {
|
|
||||||
await enrollments.setCourse(
|
|
||||||
|
|
||||||
data.course,
|
|
||||||
|
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
@ -186,23 +199,23 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
|
|
||||||
await FileDBApi.replaceRelationFiles(
|
await FileDBApi.replaceRelationFiles(
|
||||||
{
|
{
|
||||||
belongsTo: db.enrollments.getTableName(),
|
belongsTo: db.products.getTableName(),
|
||||||
belongsToColumn: 'certificate',
|
belongsToColumn: 'images',
|
||||||
belongsToId: enrollments.id,
|
belongsToId: products.id,
|
||||||
},
|
},
|
||||||
data.certificate,
|
data.images,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
return enrollments;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const enrollments = await db.enrollments.findAll({
|
const products = await db.products.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -212,53 +225,53 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of enrollments) {
|
for (const record of products) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of enrollments) {
|
for (const record of products) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return enrollments;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const enrollments = await db.enrollments.findByPk(id, options);
|
const products = await db.products.findByPk(id, options);
|
||||||
|
|
||||||
await enrollments.update({
|
await products.update({
|
||||||
deletedBy: currentUser.id
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await enrollments.destroy({
|
await products.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return enrollments;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const enrollments = await db.enrollments.findOne(
|
const products = await db.products.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!enrollments) {
|
if (!products) {
|
||||||
return enrollments;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = enrollments.get({plain: true});
|
const output = products.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -268,23 +281,20 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.order_items_product = await products.getOrder_items_product({
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.student = await enrollments.getStudent({
|
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
output.course = await enrollments.getCourse({
|
|
||||||
|
|
||||||
|
|
||||||
|
output.images = await products.getImages({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
output.certificate = await enrollments.getCertificate({
|
output.category = await products.getCategory({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -315,32 +325,15 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.users,
|
model: db.categories,
|
||||||
as: 'student',
|
as: 'category',
|
||||||
|
|
||||||
where: filter.student ? {
|
where: filter.category ? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ id: { [Op.in]: filter.student.split('|').map(term => Utils.uuid(term)) } },
|
{ id: { [Op.in]: filter.category.split('|').map(term => Utils.uuid(term)) } },
|
||||||
{
|
{
|
||||||
firstName: {
|
name: {
|
||||||
[Op.or]: filter.student.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[Op.or]: filter.category.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} : {},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
model: db.courses,
|
|
||||||
as: 'course',
|
|
||||||
|
|
||||||
where: filter.course ? {
|
|
||||||
[Op.or]: [
|
|
||||||
{ id: { [Op.in]: filter.course.split('|').map(term => Utils.uuid(term)) } },
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
[Op.or]: filter.course.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -352,7 +345,7 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
|
|
||||||
{
|
{
|
||||||
model: db.file,
|
model: db.file,
|
||||||
as: 'certificate',
|
as: 'images',
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
@ -366,13 +359,35 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.enrollment_code) {
|
if (filter.sku) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'enrollments',
|
'products',
|
||||||
'enrollment_code',
|
'sku',
|
||||||
filter.enrollment_code,
|
filter.sku,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.name) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'products',
|
||||||
|
'name',
|
||||||
|
filter.name,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.description) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.and]: Utils.ilike(
|
||||||
|
'products',
|
||||||
|
'description',
|
||||||
|
filter.description,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -382,14 +397,14 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.enrolled_atRange) {
|
if (filter.priceRange) {
|
||||||
const [start, end] = filter.enrolled_atRange;
|
const [start, end] = filter.priceRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
enrolled_at: {
|
price: {
|
||||||
...where.enrolled_at,
|
...where.price,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -398,22 +413,22 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
enrolled_at: {
|
price: {
|
||||||
...where.enrolled_at,
|
...where.price,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.progress_percentRange) {
|
if (filter.stockRange) {
|
||||||
const [start, end] = filter.progress_percentRange;
|
const [start, end] = filter.stockRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
progress_percent: {
|
stock: {
|
||||||
...where.progress_percent,
|
...where.stock,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -422,8 +437,8 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
progress_percent: {
|
stock: {
|
||||||
...where.progress_percent,
|
...where.stock,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -450,8 +465,6 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.createdAtRange) {
|
if (filter.createdAtRange) {
|
||||||
@ -499,7 +512,7 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.enrollments.findAndCountAll(queryOptions);
|
const { rows, count } = await db.products.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -521,25 +534,25 @@ module.exports = class EnrollmentsDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'enrollments',
|
'products',
|
||||||
'enrollment_code',
|
'name',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.enrollments.findAll({
|
const records = await db.products.findAll({
|
||||||
attributes: [ 'id', 'enrollment_code' ],
|
attributes: [ 'id', 'name' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['enrollment_code', 'ASC']],
|
orderBy: [['name', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.enrollment_code,
|
label: record.name,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +200,6 @@ module.exports = class RolesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.permissions = await roles.getPermissions({
|
output.permissions = await roles.getPermissions({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = class AnnouncementsDBApi {
|
module.exports = class ShipmentsDBApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -17,29 +17,33 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const announcements = await db.announcements.create(
|
const shipments = await db.shipments.create(
|
||||||
{
|
{
|
||||||
id: data.id || undefined,
|
id: data.id || undefined,
|
||||||
|
|
||||||
title: data.title
|
carrier: data.carrier
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
content: data.content
|
tracking_number: data.tracking_number
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
published_at: data.published_at
|
shipped_at: data.shipped_at
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
is_active: data.is_active
|
delivered_at: data.delivered_at
|
||||||
||
|
||
|
||||||
false
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: data.importHash || null,
|
importHash: data.importHash || null,
|
||||||
@ -50,7 +54,7 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
await announcements.setCourse( data.course || null, {
|
await shipments.setOrder( data.order || null, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -59,7 +63,7 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return announcements;
|
return shipments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -68,28 +72,32 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// Prepare data - wrapping individual data transformations in a map() method
|
||||||
const announcementsData = data.map((item, index) => ({
|
const shipmentsData = data.map((item, index) => ({
|
||||||
id: item.id || undefined,
|
id: item.id || undefined,
|
||||||
|
|
||||||
title: item.title
|
carrier: item.carrier
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
content: item.content
|
tracking_number: item.tracking_number
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
published_at: item.published_at
|
shipped_at: item.shipped_at
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
is_active: item.is_active
|
delivered_at: item.delivered_at
|
||||||
||
|
||
|
||||||
false
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -99,12 +107,12 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// Bulk create items
|
||||||
const announcements = await db.announcements.bulkCreate(announcementsData, { transaction });
|
const shipments = await db.shipments.bulkCreate(shipmentsData, { transaction });
|
||||||
|
|
||||||
// For each item created, replace relation files
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
return announcements;
|
return shipments;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -112,35 +120,38 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
|
||||||
const announcements = await db.announcements.findByPk(id, {}, {transaction});
|
const shipments = await db.shipments.findByPk(id, {}, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const updatePayload = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
if (data.title !== undefined) updatePayload.title = data.title;
|
if (data.carrier !== undefined) updatePayload.carrier = data.carrier;
|
||||||
|
|
||||||
|
|
||||||
if (data.content !== undefined) updatePayload.content = data.content;
|
if (data.tracking_number !== undefined) updatePayload.tracking_number = data.tracking_number;
|
||||||
|
|
||||||
|
|
||||||
if (data.published_at !== undefined) updatePayload.published_at = data.published_at;
|
if (data.shipped_at !== undefined) updatePayload.shipped_at = data.shipped_at;
|
||||||
|
|
||||||
|
|
||||||
if (data.is_active !== undefined) updatePayload.is_active = data.is_active;
|
if (data.delivered_at !== undefined) updatePayload.delivered_at = data.delivered_at;
|
||||||
|
|
||||||
|
|
||||||
|
if (data.status !== undefined) updatePayload.status = data.status;
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await announcements.update(updatePayload, {transaction});
|
await shipments.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.course !== undefined) {
|
if (data.order !== undefined) {
|
||||||
await announcements.setCourse(
|
await shipments.setOrder(
|
||||||
|
|
||||||
data.course,
|
data.order,
|
||||||
|
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
@ -152,14 +163,14 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return announcements;
|
return shipments;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const announcements = await db.announcements.findAll({
|
const shipments = await db.shipments.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -169,53 +180,53 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of announcements) {
|
for (const record of shipments) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of announcements) {
|
for (const record of shipments) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return announcements;
|
return shipments;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const announcements = await db.announcements.findByPk(id, options);
|
const shipments = await db.shipments.findByPk(id, options);
|
||||||
|
|
||||||
await announcements.update({
|
await shipments.update({
|
||||||
deletedBy: currentUser.id
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await announcements.destroy({
|
await shipments.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return announcements;
|
return shipments;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const announcements = await db.announcements.findOne(
|
const shipments = await db.shipments.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!announcements) {
|
if (!shipments) {
|
||||||
return announcements;
|
return shipments;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = announcements.get({plain: true});
|
const output = shipments.get({plain: true});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -229,9 +240,7 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
output.order = await shipments.getOrder({
|
||||||
|
|
||||||
output.course = await announcements.getCourse({
|
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -262,15 +271,15 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
let include = [
|
let include = [
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.courses,
|
model: db.orders,
|
||||||
as: 'course',
|
as: 'order',
|
||||||
|
|
||||||
where: filter.course ? {
|
where: filter.order ? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ id: { [Op.in]: filter.course.split('|').map(term => Utils.uuid(term)) } },
|
{ id: { [Op.in]: filter.order.split('|').map(term => Utils.uuid(term)) } },
|
||||||
{
|
{
|
||||||
title: {
|
order_number: {
|
||||||
[Op.or]: filter.course.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
[Op.or]: filter.order.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -291,24 +300,24 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.title) {
|
if (filter.carrier) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'announcements',
|
'shipments',
|
||||||
'title',
|
'carrier',
|
||||||
filter.title,
|
filter.carrier,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.content) {
|
if (filter.tracking_number) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'announcements',
|
'shipments',
|
||||||
'content',
|
'tracking_number',
|
||||||
filter.content,
|
filter.tracking_number,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -316,16 +325,34 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (filter.calendarStart && filter.calendarEnd) {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
[Op.or]: [
|
||||||
|
{
|
||||||
|
shipped_at: {
|
||||||
|
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
delivered_at: {
|
||||||
|
[Op.between]: [filter.calendarStart, filter.calendarEnd],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.published_atRange) {
|
if (filter.shipped_atRange) {
|
||||||
const [start, end] = filter.published_atRange;
|
const [start, end] = filter.shipped_atRange;
|
||||||
|
|
||||||
if (start !== undefined && start !== null && start !== '') {
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
published_at: {
|
shipped_at: {
|
||||||
...where.published_at,
|
...where.shipped_at,
|
||||||
[Op.gte]: start,
|
[Op.gte]: start,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -334,8 +361,32 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
if (end !== undefined && end !== null && end !== '') {
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
published_at: {
|
shipped_at: {
|
||||||
...where.published_at,
|
...where.shipped_at,
|
||||||
|
[Op.lte]: end,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.delivered_atRange) {
|
||||||
|
const [start, end] = filter.delivered_atRange;
|
||||||
|
|
||||||
|
if (start !== undefined && start !== null && start !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
delivered_at: {
|
||||||
|
...where.delivered_at,
|
||||||
|
[Op.gte]: start,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end !== undefined && end !== null && end !== '') {
|
||||||
|
where = {
|
||||||
|
...where,
|
||||||
|
delivered_at: {
|
||||||
|
...where.delivered_at,
|
||||||
[Op.lte]: end,
|
[Op.lte]: end,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -351,10 +402,10 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.is_active) {
|
if (filter.status) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
is_active: filter.is_active,
|
status: filter.status,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,7 +460,7 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.announcements.findAndCountAll(queryOptions);
|
const { rows, count } = await db.shipments.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -431,25 +482,25 @@ module.exports = class AnnouncementsDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'announcements',
|
'shipments',
|
||||||
'title',
|
'tracking_number',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.announcements.findAll({
|
const records = await db.shipments.findAll({
|
||||||
attributes: [ 'id', 'title' ],
|
attributes: [ 'id', 'tracking_number' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['title', 'ASC']],
|
orderBy: [['tracking_number', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.title,
|
label: record.tracking_number,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,29 +404,11 @@ module.exports = class UsersDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.courses_instructor = await users.getCourses_instructor({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.enrollments_student = await users.getEnrollments_student({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.progress_student = await users.getProgress_student({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.certificates_student = await users.getCertificates_student({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.avatar = await users.getAvatar({
|
output.avatar = await users.getAvatar({
|
||||||
|
|||||||
@ -15,7 +15,7 @@ module.exports = {
|
|||||||
username: 'postgres',
|
username: 'postgres',
|
||||||
dialect: 'postgres',
|
dialect: 'postgres',
|
||||||
password: '',
|
password: '',
|
||||||
database: 'db_courseflow_lms',
|
database: 'db_store_operations_manager',
|
||||||
host: process.env.DB_HOST || 'localhost',
|
host: process.env.DB_HOST || 'localhost',
|
||||||
logging: console.log,
|
logging: console.log,
|
||||||
seederStorage: 'sequelize',
|
seederStorage: 'sequelize',
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const course_categories = sequelize.define(
|
const categories = sequelize.define(
|
||||||
'course_categories',
|
'categories',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -19,13 +19,6 @@ name: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
slug: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
description: {
|
description: {
|
||||||
@ -48,7 +41,7 @@ description: {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
course_categories.associate = (db) => {
|
categories.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
@ -57,9 +50,8 @@ description: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.categories.hasMany(db.products, {
|
||||||
db.course_categories.hasMany(db.courses, {
|
as: 'products_category',
|
||||||
as: 'courses_category',
|
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'categoryId',
|
name: 'categoryId',
|
||||||
},
|
},
|
||||||
@ -74,26 +66,33 @@ description: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.categories.belongsTo(db.categories, {
|
||||||
|
as: 'parent',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'parentId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.course_categories.belongsTo(db.users, {
|
|
||||||
|
db.categories.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.course_categories.belongsTo(db.users, {
|
db.categories.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return course_categories;
|
return categories;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
const config = require('../../config');
|
|
||||||
const providers = config.providers;
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const bcrypt = require('bcrypt');
|
|
||||||
const moment = require('moment');
|
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
|
||||||
const certificates = sequelize.define(
|
|
||||||
'certificates',
|
|
||||||
{
|
|
||||||
id: {
|
|
||||||
type: DataTypes.UUID,
|
|
||||||
defaultValue: DataTypes.UUIDV4,
|
|
||||||
primaryKey: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
serial: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
issued_at: {
|
|
||||||
type: DataTypes.DATE,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
importHash: {
|
|
||||||
type: DataTypes.STRING(255),
|
|
||||||
allowNull: true,
|
|
||||||
unique: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timestamps: true,
|
|
||||||
paranoid: true,
|
|
||||||
freezeTableName: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
certificates.associate = (db) => {
|
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.certificates.belongsTo(db.users, {
|
|
||||||
as: 'student',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'studentId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
db.certificates.belongsTo(db.courses, {
|
|
||||||
as: 'course',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'courseId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.certificates.hasMany(db.file, {
|
|
||||||
as: 'file',
|
|
||||||
foreignKey: 'belongsToId',
|
|
||||||
constraints: false,
|
|
||||||
scope: {
|
|
||||||
belongsTo: db.certificates.getTableName(),
|
|
||||||
belongsToColumn: 'file',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
db.certificates.belongsTo(db.users, {
|
|
||||||
as: 'createdBy',
|
|
||||||
});
|
|
||||||
|
|
||||||
db.certificates.belongsTo(db.users, {
|
|
||||||
as: 'updatedBy',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return certificates;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,206 +0,0 @@
|
|||||||
const config = require('../../config');
|
|
||||||
const providers = config.providers;
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const bcrypt = require('bcrypt');
|
|
||||||
const moment = require('moment');
|
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
|
||||||
const courses = sequelize.define(
|
|
||||||
'courses',
|
|
||||||
{
|
|
||||||
id: {
|
|
||||||
type: DataTypes.UUID,
|
|
||||||
defaultValue: DataTypes.UUIDV4,
|
|
||||||
primaryKey: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
title: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
short_description: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
description: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
level: {
|
|
||||||
type: DataTypes.ENUM,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
values: [
|
|
||||||
|
|
||||||
"Beginner",
|
|
||||||
|
|
||||||
|
|
||||||
"Intermediate",
|
|
||||||
|
|
||||||
|
|
||||||
"Advanced"
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
language: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
price: {
|
|
||||||
type: DataTypes.DECIMAL,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
published: {
|
|
||||||
type: DataTypes.BOOLEAN,
|
|
||||||
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: false,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
published_at: {
|
|
||||||
type: DataTypes.DATE,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
duration: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
importHash: {
|
|
||||||
type: DataTypes.STRING(255),
|
|
||||||
allowNull: true,
|
|
||||||
unique: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timestamps: true,
|
|
||||||
paranoid: true,
|
|
||||||
freezeTableName: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
courses.associate = (db) => {
|
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.courses.hasMany(db.lessons, {
|
|
||||||
as: 'lessons_course',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'courseId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
db.courses.hasMany(db.enrollments, {
|
|
||||||
as: 'enrollments_course',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'courseId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.courses.hasMany(db.announcements, {
|
|
||||||
as: 'announcements_course',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'courseId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
db.courses.hasMany(db.certificates, {
|
|
||||||
as: 'certificates_course',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'courseId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.courses.belongsTo(db.users, {
|
|
||||||
as: 'instructor',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'instructorId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
db.courses.belongsTo(db.course_categories, {
|
|
||||||
as: 'category',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'categoryId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.courses.hasMany(db.file, {
|
|
||||||
as: 'thumbnail',
|
|
||||||
foreignKey: 'belongsToId',
|
|
||||||
constraints: false,
|
|
||||||
scope: {
|
|
||||||
belongsTo: db.courses.getTableName(),
|
|
||||||
belongsToColumn: 'thumbnail',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
db.courses.belongsTo(db.users, {
|
|
||||||
as: 'createdBy',
|
|
||||||
});
|
|
||||||
|
|
||||||
db.courses.belongsTo(db.users, {
|
|
||||||
as: 'updatedBy',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return courses;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const progress = sequelize.define(
|
const customers = sequelize.define(
|
||||||
'progress',
|
'customers',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,14 +14,42 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
summary: {
|
name: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
completed: {
|
email: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
phone: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
address: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
notes: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
vip: {
|
||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.BOOLEAN,
|
||||||
|
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
@ -31,21 +59,7 @@ completed: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
completed_at: {
|
tax_number: {
|
||||||
type: DataTypes.DATE,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
percent: {
|
|
||||||
type: DataTypes.DECIMAL,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
notes: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
@ -65,7 +79,7 @@ notes: {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
progress.associate = (db) => {
|
customers.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
@ -77,7 +91,13 @@ notes: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.customers.hasMany(db.orders, {
|
||||||
|
as: 'orders_customer',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'customerId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -88,37 +108,21 @@ notes: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.progress.belongsTo(db.users, {
|
|
||||||
as: 'student',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'studentId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
db.progress.belongsTo(db.lessons, {
|
|
||||||
as: 'lesson',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'lessonId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.customers.belongsTo(db.users, {
|
||||||
db.progress.belongsTo(db.users, {
|
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.progress.belongsTo(db.users, {
|
db.customers.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return progress;
|
return customers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const quizzes = sequelize.define(
|
const order_items = sequelize.define(
|
||||||
'quizzes',
|
'order_items',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,39 +14,29 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
title: {
|
name: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
description: {
|
quantity: {
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
passing_score: {
|
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
time_limit: {
|
unit_price: {
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
is_active: {
|
total_price: {
|
||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: false,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -65,7 +55,7 @@ is_active: {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
quizzes.associate = (db) => {
|
order_items.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
@ -80,26 +70,24 @@ is_active: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.quizzes.hasMany(db.quiz_questions, {
|
|
||||||
as: 'quiz_questions_quiz',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'quizId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.quizzes.belongsTo(db.lessons, {
|
db.order_items.belongsTo(db.orders, {
|
||||||
as: 'lesson',
|
as: 'order',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'lessonId',
|
name: 'orderId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.order_items.belongsTo(db.products, {
|
||||||
|
as: 'product',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'productId',
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
@ -107,18 +95,18 @@ is_active: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.quizzes.belongsTo(db.users, {
|
db.order_items.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.quizzes.belongsTo(db.users, {
|
db.order_items.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return quizzes;
|
return order_items;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const lessons = sequelize.define(
|
const orders = sequelize.define(
|
||||||
'lessons',
|
'orders',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,56 +14,99 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
title: {
|
order_number: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
content: {
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"Pending",
|
||||||
|
|
||||||
|
|
||||||
|
"Processing",
|
||||||
|
|
||||||
|
|
||||||
|
"Shipped",
|
||||||
|
|
||||||
|
|
||||||
|
"Delivered",
|
||||||
|
|
||||||
|
|
||||||
|
"Cancelled",
|
||||||
|
|
||||||
|
|
||||||
|
"Returned",
|
||||||
|
|
||||||
|
|
||||||
|
"Refunded"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
total: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
placed_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
shipped_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
delivery_date: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
payment_status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"Pending",
|
||||||
|
|
||||||
|
|
||||||
|
"Paid",
|
||||||
|
|
||||||
|
|
||||||
|
"Failed",
|
||||||
|
|
||||||
|
|
||||||
|
"Refunded"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
shipping_address: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
order: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
duration: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
start_at: {
|
|
||||||
type: DataTypes.DATE,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
end_at: {
|
|
||||||
type: DataTypes.DATE,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
is_published: {
|
|
||||||
type: DataTypes.BOOLEAN,
|
|
||||||
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: false,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
@ -79,7 +122,7 @@ is_published: {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
lessons.associate = (db) => {
|
orders.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
@ -92,25 +135,31 @@ is_published: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.lessons.hasMany(db.progress, {
|
db.orders.hasMany(db.order_items, {
|
||||||
as: 'progress_lesson',
|
as: 'order_items_order',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'lessonId',
|
name: 'orderId',
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
db.lessons.hasMany(db.quizzes, {
|
db.orders.hasMany(db.payments, {
|
||||||
as: 'quizzes_lesson',
|
as: 'payments_order',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'lessonId',
|
name: 'orderId',
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
db.orders.hasMany(db.shipments, {
|
||||||
|
as: 'shipments_order',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'orderId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -118,39 +167,29 @@ is_published: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.lessons.belongsTo(db.courses, {
|
db.orders.belongsTo(db.customers, {
|
||||||
as: 'course',
|
as: 'customer',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'courseId',
|
name: 'customerId',
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.lessons.hasMany(db.file, {
|
|
||||||
as: 'video_files',
|
|
||||||
foreignKey: 'belongsToId',
|
|
||||||
constraints: false,
|
|
||||||
scope: {
|
|
||||||
belongsTo: db.lessons.getTableName(),
|
|
||||||
belongsToColumn: 'video_files',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
db.orders.belongsTo(db.users, {
|
||||||
db.lessons.belongsTo(db.users, {
|
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.lessons.belongsTo(db.users, {
|
db.orders.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return lessons;
|
return orders;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const announcements = sequelize.define(
|
const payments = sequelize.define(
|
||||||
'announcements',
|
'payments',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,35 +14,69 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
title: {
|
reference: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
content: {
|
amount: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
published_at: {
|
method: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"Card",
|
||||||
|
|
||||||
|
|
||||||
|
"PayPal",
|
||||||
|
|
||||||
|
|
||||||
|
"BankTransfer",
|
||||||
|
|
||||||
|
|
||||||
|
"Cash"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"Pending",
|
||||||
|
|
||||||
|
|
||||||
|
"Completed",
|
||||||
|
|
||||||
|
|
||||||
|
"Failed",
|
||||||
|
|
||||||
|
|
||||||
|
"Refunded"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
paid_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
is_active: {
|
|
||||||
type: DataTypes.BOOLEAN,
|
|
||||||
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: false,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
@ -58,7 +92,7 @@ is_active: {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
announcements.associate = (db) => {
|
payments.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
@ -75,16 +109,14 @@ is_active: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.announcements.belongsTo(db.courses, {
|
db.payments.belongsTo(db.orders, {
|
||||||
as: 'course',
|
as: 'order',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'courseId',
|
name: 'orderId',
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
@ -92,18 +124,18 @@ is_active: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.announcements.belongsTo(db.users, {
|
db.payments.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.announcements.belongsTo(db.users, {
|
db.payments.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return announcements;
|
return payments;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -51,8 +51,6 @@ name: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const enrollments = sequelize.define(
|
const products = sequelize.define(
|
||||||
'enrollments',
|
'products',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,15 +14,36 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
enrollment_code: {
|
sku: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
enrolled_at: {
|
name: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
description: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
price: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
stock: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -35,28 +56,18 @@ status: {
|
|||||||
|
|
||||||
values: [
|
values: [
|
||||||
|
|
||||||
"pending",
|
|
||||||
|
|
||||||
|
|
||||||
"active",
|
"active",
|
||||||
|
|
||||||
|
|
||||||
"completed",
|
"inactive",
|
||||||
|
|
||||||
|
|
||||||
"dropped"
|
"archived"
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
progress_percent: {
|
|
||||||
type: DataTypes.DECIMAL,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
type: DataTypes.STRING(255),
|
type: DataTypes.STRING(255),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
@ -70,7 +81,7 @@ progress_percent: {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
enrollments.associate = (db) => {
|
products.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
@ -83,7 +94,13 @@ progress_percent: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.products.hasMany(db.order_items, {
|
||||||
|
as: 'order_items_product',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'productId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -93,47 +110,39 @@ progress_percent: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.enrollments.belongsTo(db.users, {
|
db.products.belongsTo(db.categories, {
|
||||||
as: 'student',
|
as: 'category',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'studentId',
|
name: 'categoryId',
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
db.enrollments.belongsTo(db.courses, {
|
|
||||||
as: 'course',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'courseId',
|
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.enrollments.hasMany(db.file, {
|
db.products.hasMany(db.file, {
|
||||||
as: 'certificate',
|
as: 'images',
|
||||||
foreignKey: 'belongsToId',
|
foreignKey: 'belongsToId',
|
||||||
constraints: false,
|
constraints: false,
|
||||||
scope: {
|
scope: {
|
||||||
belongsTo: db.enrollments.getTableName(),
|
belongsTo: db.products.getTableName(),
|
||||||
belongsToColumn: 'certificate',
|
belongsToColumn: 'images',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
db.enrollments.belongsTo(db.users, {
|
db.products.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.enrollments.belongsTo(db.users, {
|
db.products.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return enrollments;
|
return products;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -84,8 +84,6 @@ role_customization: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const quiz_questions = sequelize.define(
|
const shipments = sequelize.define(
|
||||||
'quiz_questions',
|
'shipments',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,46 +14,56 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
question: {
|
carrier: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
question_type: {
|
tracking_number: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
shipped_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
delivered_at: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
type: DataTypes.ENUM,
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
values: [
|
values: [
|
||||||
|
|
||||||
"multiple_choice",
|
"Pending",
|
||||||
|
|
||||||
|
|
||||||
"true_false",
|
"InTransit",
|
||||||
|
|
||||||
|
|
||||||
"short_answer"
|
"Delivered",
|
||||||
|
|
||||||
|
|
||||||
|
"Returned"
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
choices: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
answer: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
type: DataTypes.STRING(255),
|
type: DataTypes.STRING(255),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
@ -67,7 +77,7 @@ answer: {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
quiz_questions.associate = (db) => {
|
shipments.associate = (db) => {
|
||||||
|
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||||
@ -84,16 +94,14 @@ answer: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.quiz_questions.belongsTo(db.quizzes, {
|
db.shipments.belongsTo(db.orders, {
|
||||||
as: 'quiz',
|
as: 'order',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'quizId',
|
name: 'orderId',
|
||||||
},
|
},
|
||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
@ -101,18 +109,18 @@ answer: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.quiz_questions.belongsTo(db.users, {
|
db.shipments.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.quiz_questions.belongsTo(db.users, {
|
db.shipments.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return quiz_questions;
|
return shipments;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -145,45 +145,11 @@ provider: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.users.hasMany(db.courses, {
|
|
||||||
as: 'courses_instructor',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'instructorId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.users.hasMany(db.enrollments, {
|
|
||||||
as: 'enrollments_student',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'studentId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
db.users.hasMany(db.progress, {
|
|
||||||
as: 'progress_student',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'studentId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.users.hasMany(db.certificates, {
|
|
||||||
as: 'certificates_student',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'studentId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -26,23 +26,19 @@ const rolesRoutes = require('./routes/roles');
|
|||||||
|
|
||||||
const permissionsRoutes = require('./routes/permissions');
|
const permissionsRoutes = require('./routes/permissions');
|
||||||
|
|
||||||
const course_categoriesRoutes = require('./routes/course_categories');
|
const productsRoutes = require('./routes/products');
|
||||||
|
|
||||||
const coursesRoutes = require('./routes/courses');
|
const categoriesRoutes = require('./routes/categories');
|
||||||
|
|
||||||
const lessonsRoutes = require('./routes/lessons');
|
const customersRoutes = require('./routes/customers');
|
||||||
|
|
||||||
const enrollmentsRoutes = require('./routes/enrollments');
|
const ordersRoutes = require('./routes/orders');
|
||||||
|
|
||||||
const progressRoutes = require('./routes/progress');
|
const order_itemsRoutes = require('./routes/order_items');
|
||||||
|
|
||||||
const quizzesRoutes = require('./routes/quizzes');
|
const paymentsRoutes = require('./routes/payments');
|
||||||
|
|
||||||
const quiz_questionsRoutes = require('./routes/quiz_questions');
|
const shipmentsRoutes = require('./routes/shipments');
|
||||||
|
|
||||||
const announcementsRoutes = require('./routes/announcements');
|
|
||||||
|
|
||||||
const certificatesRoutes = require('./routes/certificates');
|
|
||||||
|
|
||||||
|
|
||||||
const getBaseUrl = (url) => {
|
const getBaseUrl = (url) => {
|
||||||
@ -55,8 +51,8 @@ const options = {
|
|||||||
openapi: "3.0.0",
|
openapi: "3.0.0",
|
||||||
info: {
|
info: {
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
title: "CourseFlow LMS",
|
title: "Store Operations Manager",
|
||||||
description: "CourseFlow LMS Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.",
|
description: "Store Operations Manager Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.",
|
||||||
},
|
},
|
||||||
servers: [
|
servers: [
|
||||||
{
|
{
|
||||||
@ -108,23 +104,19 @@ app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoute
|
|||||||
|
|
||||||
app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes);
|
app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes);
|
||||||
|
|
||||||
app.use('/api/course_categories', passport.authenticate('jwt', {session: false}), course_categoriesRoutes);
|
app.use('/api/products', passport.authenticate('jwt', {session: false}), productsRoutes);
|
||||||
|
|
||||||
app.use('/api/courses', passport.authenticate('jwt', {session: false}), coursesRoutes);
|
app.use('/api/categories', passport.authenticate('jwt', {session: false}), categoriesRoutes);
|
||||||
|
|
||||||
app.use('/api/lessons', passport.authenticate('jwt', {session: false}), lessonsRoutes);
|
app.use('/api/customers', passport.authenticate('jwt', {session: false}), customersRoutes);
|
||||||
|
|
||||||
app.use('/api/enrollments', passport.authenticate('jwt', {session: false}), enrollmentsRoutes);
|
app.use('/api/orders', passport.authenticate('jwt', {session: false}), ordersRoutes);
|
||||||
|
|
||||||
app.use('/api/progress', passport.authenticate('jwt', {session: false}), progressRoutes);
|
app.use('/api/order_items', passport.authenticate('jwt', {session: false}), order_itemsRoutes);
|
||||||
|
|
||||||
app.use('/api/quizzes', passport.authenticate('jwt', {session: false}), quizzesRoutes);
|
app.use('/api/payments', passport.authenticate('jwt', {session: false}), paymentsRoutes);
|
||||||
|
|
||||||
app.use('/api/quiz_questions', passport.authenticate('jwt', {session: false}), quiz_questionsRoutes);
|
app.use('/api/shipments', passport.authenticate('jwt', {session: false}), shipmentsRoutes);
|
||||||
|
|
||||||
app.use('/api/announcements', passport.authenticate('jwt', {session: false}), announcementsRoutes);
|
|
||||||
|
|
||||||
app.use('/api/certificates', passport.authenticate('jwt', {session: false}), certificatesRoutes);
|
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
'/api/openai',
|
'/api/openai',
|
||||||
|
|||||||
@ -1,432 +0,0 @@
|
|||||||
|
|
||||||
const express = require('express');
|
|
||||||
|
|
||||||
const AnnouncementsService = require('../services/announcements');
|
|
||||||
const AnnouncementsDBApi = require('../db/api/announcements');
|
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
|
||||||
|
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
const { parse } = require('json2csv');
|
|
||||||
|
|
||||||
|
|
||||||
const {
|
|
||||||
checkCrudPermissions,
|
|
||||||
} = require('../middlewares/check-permissions');
|
|
||||||
|
|
||||||
router.use(checkCrudPermissions('announcements'));
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* components:
|
|
||||||
* schemas:
|
|
||||||
* Announcements:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
|
|
||||||
* title:
|
|
||||||
* type: string
|
|
||||||
* default: title
|
|
||||||
* content:
|
|
||||||
* type: string
|
|
||||||
* default: content
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* tags:
|
|
||||||
* name: Announcements
|
|
||||||
* description: The Announcements managing API
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/announcements:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Add new item
|
|
||||||
* description: Add new item
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated item
|
|
||||||
* type: object
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item was successfully added
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 405:
|
|
||||||
* description: Invalid input data
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.post('/', wrapAsync(async (req, res) => {
|
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
|
||||||
const link = new URL(referer);
|
|
||||||
await AnnouncementsService.create(req.body.data, req.currentUser, true, link.host);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/budgets/bulk-import:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Bulk import items
|
|
||||||
* description: Bulk import items
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated items
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The items were successfully imported
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 405:
|
|
||||||
* description: Invalid input data
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
|
||||||
const link = new URL(referer);
|
|
||||||
await AnnouncementsService.bulkImport(req, res, true, link.host);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/announcements/{id}:
|
|
||||||
* put:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Update the data of the selected item
|
|
||||||
* description: Update the data of the selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: Item ID to update
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* requestBody:
|
|
||||||
* description: Set new item data
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* id:
|
|
||||||
* description: ID of the updated item
|
|
||||||
* type: string
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated item
|
|
||||||
* type: object
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* required:
|
|
||||||
* - id
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item data was successfully updated
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
|
||||||
await AnnouncementsService.update(req.body.data, req.body.id, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/announcements/{id}:
|
|
||||||
* delete:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Delete the selected item
|
|
||||||
* description: Delete the selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: Item ID to delete
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item was successfully deleted
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
|
||||||
await AnnouncementsService.remove(req.params.id, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/announcements/deleteByIds:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Delete the selected item list
|
|
||||||
* description: Delete the selected item list
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* ids:
|
|
||||||
* description: IDs of the updated items
|
|
||||||
* type: array
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The items was successfully deleted
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Items not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
|
||||||
await AnnouncementsService.deleteByIds(req.body.data, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/announcements:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Get all announcements
|
|
||||||
* description: Get all announcements
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Announcements list successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/', wrapAsync(async (req, res) => {
|
|
||||||
const filetype = req.query.filetype
|
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
|
||||||
const payload = await AnnouncementsDBApi.findAll(
|
|
||||||
req.query, { currentUser }
|
|
||||||
);
|
|
||||||
if (filetype && filetype === 'csv') {
|
|
||||||
const fields = ['id','title','content',
|
|
||||||
|
|
||||||
|
|
||||||
'published_at',
|
|
||||||
];
|
|
||||||
const opts = { fields };
|
|
||||||
try {
|
|
||||||
const csv = parse(payload.rows, opts);
|
|
||||||
res.status(200).attachment(csv);
|
|
||||||
res.send(csv)
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/announcements/count:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Count all announcements
|
|
||||||
* description: Count all announcements
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Announcements count successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/count', wrapAsync(async (req, res) => {
|
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
|
||||||
const payload = await AnnouncementsDBApi.findAll(
|
|
||||||
req.query,
|
|
||||||
null,
|
|
||||||
{ countOnly: true, currentUser }
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/announcements/autocomplete:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Find all announcements that match search criteria
|
|
||||||
* description: Find all announcements that match search criteria
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Announcements list successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/autocomplete', async (req, res) => {
|
|
||||||
|
|
||||||
const payload = await AnnouncementsDBApi.findAllAutocomplete(
|
|
||||||
req.query.query,
|
|
||||||
req.query.limit,
|
|
||||||
req.query.offset,
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/announcements/{id}:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Announcements]
|
|
||||||
* summary: Get selected item
|
|
||||||
* description: Get selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: ID of item to get
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Selected item successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Announcements"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
|
||||||
const payload = await AnnouncementsDBApi.findBy(
|
|
||||||
{ id: req.params.id },
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
router.use('/', require('../helpers').commonErrorHandler);
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
432
backend/src/routes/categories.js
Normal file
432
backend/src/routes/categories.js
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const CategoriesService = require('../services/categories');
|
||||||
|
const CategoriesDBApi = require('../db/api/categories');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const { parse } = require('json2csv');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
checkCrudPermissions,
|
||||||
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('categories'));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* components:
|
||||||
|
* schemas:
|
||||||
|
* Categories:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
|
* description:
|
||||||
|
* type: string
|
||||||
|
* default: description
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* tags:
|
||||||
|
* name: Categories
|
||||||
|
* description: The Categories managing API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/categories:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Add new item
|
||||||
|
* description: Add new item
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated item
|
||||||
|
* type: object
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully added
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 405:
|
||||||
|
* description: Invalid input data
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await CategoriesService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/budgets/bulk-import:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Bulk import items
|
||||||
|
* description: Bulk import items
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated items
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items were successfully imported
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 405:
|
||||||
|
* description: Invalid input data
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
|
const link = new URL(referer);
|
||||||
|
await CategoriesService.bulkImport(req, res, true, link.host);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/categories/{id}:
|
||||||
|
* put:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Update the data of the selected item
|
||||||
|
* description: Update the data of the selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: Item ID to update
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* requestBody:
|
||||||
|
* description: Set new item data
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* description: ID of the updated item
|
||||||
|
* type: string
|
||||||
|
* data:
|
||||||
|
* description: Data of the updated item
|
||||||
|
* type: object
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* required:
|
||||||
|
* - id
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item data was successfully updated
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await CategoriesService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/categories/{id}:
|
||||||
|
* delete:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Delete the selected item
|
||||||
|
* description: Delete the selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: Item ID to delete
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The item was successfully deleted
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await CategoriesService.remove(req.params.id, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/categories/deleteByIds:
|
||||||
|
* post:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Delete the selected item list
|
||||||
|
* description: Delete the selected item list
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* ids:
|
||||||
|
* description: IDs of the updated items
|
||||||
|
* type: array
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The items was successfully deleted
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Items not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await CategoriesService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
const payload = true;
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/categories:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Get all categories
|
||||||
|
* description: Get all categories
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Categories list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await CategoriesDBApi.findAll(
|
||||||
|
req.query, { currentUser }
|
||||||
|
);
|
||||||
|
if (filetype && filetype === 'csv') {
|
||||||
|
const fields = ['id','name','description',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
|
const opts = { fields };
|
||||||
|
try {
|
||||||
|
const csv = parse(payload.rows, opts);
|
||||||
|
res.status(200).attachment(csv);
|
||||||
|
res.send(csv)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/categories/count:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Count all categories
|
||||||
|
* description: Count all categories
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Categories count successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await CategoriesDBApi.findAll(
|
||||||
|
req.query,
|
||||||
|
null,
|
||||||
|
{ countOnly: true, currentUser }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/categories/autocomplete:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Find all categories that match search criteria
|
||||||
|
* description: Find all categories that match search criteria
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Categories list successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Data not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
|
const payload = await CategoriesDBApi.findAllAutocomplete(
|
||||||
|
req.query.query,
|
||||||
|
req.query.limit,
|
||||||
|
req.query.offset,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/categories/{id}:
|
||||||
|
* get:
|
||||||
|
* security:
|
||||||
|
* - bearerAuth: []
|
||||||
|
* tags: [Categories]
|
||||||
|
* summary: Get selected item
|
||||||
|
* description: Get selected item
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* description: ID of item to get
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Selected item successfully received
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: "#/components/schemas/Categories"
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID supplied
|
||||||
|
* 401:
|
||||||
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
|
* 404:
|
||||||
|
* description: Item not found
|
||||||
|
* 500:
|
||||||
|
* description: Some server error
|
||||||
|
*/
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await CategoriesDBApi.findBy(
|
||||||
|
{ id: req.params.id },
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@ -1,435 +0,0 @@
|
|||||||
|
|
||||||
const express = require('express');
|
|
||||||
|
|
||||||
const Course_categoriesService = require('../services/course_categories');
|
|
||||||
const Course_categoriesDBApi = require('../db/api/course_categories');
|
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
|
||||||
|
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
const { parse } = require('json2csv');
|
|
||||||
|
|
||||||
|
|
||||||
const {
|
|
||||||
checkCrudPermissions,
|
|
||||||
} = require('../middlewares/check-permissions');
|
|
||||||
|
|
||||||
router.use(checkCrudPermissions('course_categories'));
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* components:
|
|
||||||
* schemas:
|
|
||||||
* Course_categories:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
|
|
||||||
* name:
|
|
||||||
* type: string
|
|
||||||
* default: name
|
|
||||||
* slug:
|
|
||||||
* type: string
|
|
||||||
* default: slug
|
|
||||||
* description:
|
|
||||||
* type: string
|
|
||||||
* default: description
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* tags:
|
|
||||||
* name: Course_categories
|
|
||||||
* description: The Course_categories managing API
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/course_categories:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Add new item
|
|
||||||
* description: Add new item
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated item
|
|
||||||
* type: object
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item was successfully added
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 405:
|
|
||||||
* description: Invalid input data
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.post('/', wrapAsync(async (req, res) => {
|
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
|
||||||
const link = new URL(referer);
|
|
||||||
await Course_categoriesService.create(req.body.data, req.currentUser, true, link.host);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/budgets/bulk-import:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Bulk import items
|
|
||||||
* description: Bulk import items
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated items
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The items were successfully imported
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 405:
|
|
||||||
* description: Invalid input data
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
|
||||||
const link = new URL(referer);
|
|
||||||
await Course_categoriesService.bulkImport(req, res, true, link.host);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/course_categories/{id}:
|
|
||||||
* put:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Update the data of the selected item
|
|
||||||
* description: Update the data of the selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: Item ID to update
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* requestBody:
|
|
||||||
* description: Set new item data
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* id:
|
|
||||||
* description: ID of the updated item
|
|
||||||
* type: string
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated item
|
|
||||||
* type: object
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* required:
|
|
||||||
* - id
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item data was successfully updated
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
|
||||||
await Course_categoriesService.update(req.body.data, req.body.id, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/course_categories/{id}:
|
|
||||||
* delete:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Delete the selected item
|
|
||||||
* description: Delete the selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: Item ID to delete
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item was successfully deleted
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
|
||||||
await Course_categoriesService.remove(req.params.id, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/course_categories/deleteByIds:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Delete the selected item list
|
|
||||||
* description: Delete the selected item list
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* ids:
|
|
||||||
* description: IDs of the updated items
|
|
||||||
* type: array
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The items was successfully deleted
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Items not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
|
||||||
await Course_categoriesService.deleteByIds(req.body.data, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/course_categories:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Get all course_categories
|
|
||||||
* description: Get all course_categories
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Course_categories list successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/', wrapAsync(async (req, res) => {
|
|
||||||
const filetype = req.query.filetype
|
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
|
||||||
const payload = await Course_categoriesDBApi.findAll(
|
|
||||||
req.query, { currentUser }
|
|
||||||
);
|
|
||||||
if (filetype && filetype === 'csv') {
|
|
||||||
const fields = ['id','name','slug','description',
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
];
|
|
||||||
const opts = { fields };
|
|
||||||
try {
|
|
||||||
const csv = parse(payload.rows, opts);
|
|
||||||
res.status(200).attachment(csv);
|
|
||||||
res.send(csv)
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/course_categories/count:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Count all course_categories
|
|
||||||
* description: Count all course_categories
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Course_categories count successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/count', wrapAsync(async (req, res) => {
|
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
|
||||||
const payload = await Course_categoriesDBApi.findAll(
|
|
||||||
req.query,
|
|
||||||
null,
|
|
||||||
{ countOnly: true, currentUser }
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/course_categories/autocomplete:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Find all course_categories that match search criteria
|
|
||||||
* description: Find all course_categories that match search criteria
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Course_categories list successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/autocomplete', async (req, res) => {
|
|
||||||
|
|
||||||
const payload = await Course_categoriesDBApi.findAllAutocomplete(
|
|
||||||
req.query.query,
|
|
||||||
req.query.limit,
|
|
||||||
req.query.offset,
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/course_categories/{id}:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Course_categories]
|
|
||||||
* summary: Get selected item
|
|
||||||
* description: Get selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: ID of item to get
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Selected item successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Course_categories"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
|
||||||
const payload = await Course_categoriesDBApi.findBy(
|
|
||||||
{ id: req.params.id },
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
router.use('/', require('../helpers').commonErrorHandler);
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const CoursesService = require('../services/courses');
|
const CustomersService = require('../services/customers');
|
||||||
const CoursesDBApi = require('../db/api/courses');
|
const CustomersDBApi = require('../db/api/customers');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,55 +15,54 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('courses'));
|
router.use(checkCrudPermissions('customers'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Courses:
|
* Customers:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
* title:
|
* name:
|
||||||
* type: string
|
* type: string
|
||||||
* default: title
|
* default: name
|
||||||
* short_description:
|
* email:
|
||||||
* type: string
|
* type: string
|
||||||
* default: short_description
|
* default: email
|
||||||
* description:
|
* phone:
|
||||||
* type: string
|
* type: string
|
||||||
* default: description
|
* default: phone
|
||||||
* language:
|
* address:
|
||||||
* type: string
|
* type: string
|
||||||
* default: language
|
* default: address
|
||||||
|
* notes:
|
||||||
|
* type: string
|
||||||
|
* default: notes
|
||||||
|
* tax_number:
|
||||||
|
* type: string
|
||||||
|
* default: tax_number
|
||||||
|
|
||||||
* duration:
|
|
||||||
* type: integer
|
|
||||||
* format: int64
|
|
||||||
|
|
||||||
* price:
|
|
||||||
* type: integer
|
|
||||||
* format: int64
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Courses
|
* name: Customers
|
||||||
* description: The Courses managing API
|
* description: The Customers managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/courses:
|
* /api/customers:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -75,14 +74,14 @@ router.use(checkCrudPermissions('courses'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -93,7 +92,7 @@ router.use(checkCrudPermissions('courses'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await CoursesService.create(req.body.data, req.currentUser, true, link.host);
|
await CustomersService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -104,7 +103,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -117,14 +116,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -136,18 +135,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await CoursesService.bulkImport(req, res, true, link.host);
|
await CustomersService.bulkImport(req, res, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/courses/{id}:
|
* /api/customers/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -170,7 +169,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -179,7 +178,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -190,18 +189,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
await CoursesService.update(req.body.data, req.body.id, req.currentUser);
|
await CustomersService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/courses/{id}:
|
* /api/customers/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -217,7 +216,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -228,18 +227,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
await CoursesService.remove(req.params.id, req.currentUser);
|
await CustomersService.remove(req.params.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/courses/deleteByIds:
|
* /api/customers/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -257,7 +256,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -266,29 +265,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
await CoursesService.deleteByIds(req.body.data, req.currentUser);
|
await CustomersService.deleteByIds(req.body.data, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/courses:
|
* /api/customers:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Get all courses
|
* summary: Get all customers
|
||||||
* description: Get all courses
|
* description: Get all customers
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Courses list successfully received
|
* description: Customers list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -300,14 +299,14 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await CoursesDBApi.findAll(
|
const payload = await CustomersDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id','title','short_description','description','language',
|
const fields = ['id','name','email','phone','address','notes','tax_number',
|
||||||
'duration',
|
|
||||||
'price',
|
|
||||||
'published_at',
|
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
@ -326,22 +325,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/courses/count:
|
* /api/customers/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Count all courses
|
* summary: Count all customers
|
||||||
* description: Count all courses
|
* description: Count all customers
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Courses count successfully received
|
* description: Customers count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -352,7 +351,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await CoursesDBApi.findAll(
|
const payload = await CustomersDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -363,22 +362,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/courses/autocomplete:
|
* /api/customers/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Find all courses that match search criteria
|
* summary: Find all customers that match search criteria
|
||||||
* description: Find all courses that match search criteria
|
* description: Find all customers that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Courses list successfully received
|
* description: Customers list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -388,7 +387,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', async (req, res) => {
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
const payload = await CoursesDBApi.findAllAutocomplete(
|
const payload = await CustomersDBApi.findAllAutocomplete(
|
||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -400,11 +399,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/courses/{id}:
|
* /api/customers/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Courses]
|
* tags: [Customers]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -420,7 +419,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Courses"
|
* $ref: "#/components/schemas/Customers"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -431,7 +430,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await CoursesDBApi.findBy(
|
const payload = await CustomersDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const EnrollmentsService = require('../services/enrollments');
|
const Order_itemsService = require('../services/order_items');
|
||||||
const EnrollmentsDBApi = require('../db/api/enrollments');
|
const Order_itemsDBApi = require('../db/api/order_items');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,43 +15,48 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('enrollments'));
|
router.use(checkCrudPermissions('order_items'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Enrollments:
|
* Order_items:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
* enrollment_code:
|
* name:
|
||||||
* type: string
|
* type: string
|
||||||
* default: enrollment_code
|
* default: name
|
||||||
|
|
||||||
|
* quantity:
|
||||||
* progress_percent:
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
* unit_price:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
* total_price:
|
||||||
* type: integer
|
* type: integer
|
||||||
* format: int64
|
* format: int64
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Enrollments
|
* name: Order_items
|
||||||
* description: The Enrollments managing API
|
* description: The Order_items managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/enrollments:
|
* /api/order_items:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -63,14 +68,14 @@ router.use(checkCrudPermissions('enrollments'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -81,7 +86,7 @@ router.use(checkCrudPermissions('enrollments'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await EnrollmentsService.create(req.body.data, req.currentUser, true, link.host);
|
await Order_itemsService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -92,7 +97,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -105,14 +110,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -124,18 +129,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await EnrollmentsService.bulkImport(req, res, true, link.host);
|
await Order_itemsService.bulkImport(req, res, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/enrollments/{id}:
|
* /api/order_items/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -158,7 +163,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -167,7 +172,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -178,18 +183,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
await EnrollmentsService.update(req.body.data, req.body.id, req.currentUser);
|
await Order_itemsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/enrollments/{id}:
|
* /api/order_items/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -205,7 +210,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -216,18 +221,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
await EnrollmentsService.remove(req.params.id, req.currentUser);
|
await Order_itemsService.remove(req.params.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/enrollments/deleteByIds:
|
* /api/order_items/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -245,7 +250,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -254,29 +259,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
await EnrollmentsService.deleteByIds(req.body.data, req.currentUser);
|
await Order_itemsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/enrollments:
|
* /api/order_items:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Get all enrollments
|
* summary: Get all order_items
|
||||||
* description: Get all enrollments
|
* description: Get all order_items
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Enrollments list successfully received
|
* description: Order_items list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -288,14 +293,14 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await EnrollmentsDBApi.findAll(
|
const payload = await Order_itemsDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id','enrollment_code',
|
const fields = ['id','name',
|
||||||
|
'quantity',
|
||||||
'progress_percent',
|
'unit_price','total_price',
|
||||||
'enrolled_at',
|
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
@ -314,22 +319,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/enrollments/count:
|
* /api/order_items/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Count all enrollments
|
* summary: Count all order_items
|
||||||
* description: Count all enrollments
|
* description: Count all order_items
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Enrollments count successfully received
|
* description: Order_items count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -340,7 +345,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await EnrollmentsDBApi.findAll(
|
const payload = await Order_itemsDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -351,22 +356,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/enrollments/autocomplete:
|
* /api/order_items/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Find all enrollments that match search criteria
|
* summary: Find all order_items that match search criteria
|
||||||
* description: Find all enrollments that match search criteria
|
* description: Find all order_items that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Enrollments list successfully received
|
* description: Order_items list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -376,7 +381,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', async (req, res) => {
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
const payload = await EnrollmentsDBApi.findAllAutocomplete(
|
const payload = await Order_itemsDBApi.findAllAutocomplete(
|
||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -388,11 +393,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/enrollments/{id}:
|
* /api/order_items/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Enrollments]
|
* tags: [Order_items]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -408,7 +413,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Enrollments"
|
* $ref: "#/components/schemas/Order_items"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -419,7 +424,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await EnrollmentsDBApi.findBy(
|
const payload = await Order_itemsDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const LessonsService = require('../services/lessons');
|
const OrdersService = require('../services/orders');
|
||||||
const LessonsDBApi = require('../db/api/lessons');
|
const OrdersDBApi = require('../db/api/orders');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,48 +15,47 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('lessons'));
|
router.use(checkCrudPermissions('orders'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Lessons:
|
* Orders:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
* title:
|
* order_number:
|
||||||
* type: string
|
* type: string
|
||||||
* default: title
|
* default: order_number
|
||||||
* content:
|
* shipping_address:
|
||||||
* type: string
|
* type: string
|
||||||
* default: content
|
* default: shipping_address
|
||||||
|
|
||||||
* order:
|
|
||||||
* type: integer
|
* total:
|
||||||
* format: int64
|
|
||||||
* duration:
|
|
||||||
* type: integer
|
* type: integer
|
||||||
* format: int64
|
* format: int64
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Lessons
|
* name: Orders
|
||||||
* description: The Lessons managing API
|
* description: The Orders managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/lessons:
|
* /api/orders:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -68,14 +67,14 @@ router.use(checkCrudPermissions('lessons'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -86,7 +85,7 @@ router.use(checkCrudPermissions('lessons'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await LessonsService.create(req.body.data, req.currentUser, true, link.host);
|
await OrdersService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -97,7 +96,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -110,14 +109,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -129,18 +128,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await LessonsService.bulkImport(req, res, true, link.host);
|
await OrdersService.bulkImport(req, res, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/lessons/{id}:
|
* /api/orders/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -163,7 +162,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -172,7 +171,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -183,18 +182,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
await LessonsService.update(req.body.data, req.body.id, req.currentUser);
|
await OrdersService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/lessons/{id}:
|
* /api/orders/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -210,7 +209,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -221,18 +220,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
await LessonsService.remove(req.params.id, req.currentUser);
|
await OrdersService.remove(req.params.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/lessons/deleteByIds:
|
* /api/orders/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -250,7 +249,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -259,29 +258,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
await LessonsService.deleteByIds(req.body.data, req.currentUser);
|
await OrdersService.deleteByIds(req.body.data, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/lessons:
|
* /api/orders:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Get all lessons
|
* summary: Get all orders
|
||||||
* description: Get all lessons
|
* description: Get all orders
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Lessons list successfully received
|
* description: Orders list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -293,14 +292,14 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await LessonsDBApi.findAll(
|
const payload = await OrdersDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id','title','content',
|
const fields = ['id','order_number','shipping_address',
|
||||||
'order','duration',
|
|
||||||
|
|
||||||
'start_at','end_at',
|
'total',
|
||||||
|
'placed_at','shipped_at','delivery_date',
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
@ -319,22 +318,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/lessons/count:
|
* /api/orders/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Count all lessons
|
* summary: Count all orders
|
||||||
* description: Count all lessons
|
* description: Count all orders
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Lessons count successfully received
|
* description: Orders count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -345,7 +344,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await LessonsDBApi.findAll(
|
const payload = await OrdersDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -356,22 +355,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/lessons/autocomplete:
|
* /api/orders/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Find all lessons that match search criteria
|
* summary: Find all orders that match search criteria
|
||||||
* description: Find all lessons that match search criteria
|
* description: Find all orders that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Lessons list successfully received
|
* description: Orders list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -381,7 +380,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', async (req, res) => {
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
const payload = await LessonsDBApi.findAllAutocomplete(
|
const payload = await OrdersDBApi.findAllAutocomplete(
|
||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -393,11 +392,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/lessons/{id}:
|
* /api/orders/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Lessons]
|
* tags: [Orders]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -413,7 +412,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Lessons"
|
* $ref: "#/components/schemas/Orders"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -424,7 +423,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await LessonsDBApi.findBy(
|
const payload = await OrdersDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const ProgressService = require('../services/progress');
|
const PaymentsService = require('../services/payments');
|
||||||
const ProgressDBApi = require('../db/api/progress');
|
const PaymentsDBApi = require('../db/api/payments');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,45 +15,44 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('progress'));
|
router.use(checkCrudPermissions('payments'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Progress:
|
* Payments:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
* summary:
|
* reference:
|
||||||
* type: string
|
* type: string
|
||||||
* default: summary
|
* default: reference
|
||||||
* notes:
|
|
||||||
* type: string
|
|
||||||
* default: notes
|
|
||||||
|
|
||||||
|
|
||||||
* percent:
|
* amount:
|
||||||
* type: integer
|
* type: integer
|
||||||
* format: int64
|
* format: int64
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Progress
|
* name: Payments
|
||||||
* description: The Progress managing API
|
* description: The Payments managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/progress:
|
* /api/payments:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -65,14 +64,14 @@ router.use(checkCrudPermissions('progress'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -83,7 +82,7 @@ router.use(checkCrudPermissions('progress'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await ProgressService.create(req.body.data, req.currentUser, true, link.host);
|
await PaymentsService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -94,7 +93,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -107,14 +106,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -126,18 +125,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await ProgressService.bulkImport(req, res, true, link.host);
|
await PaymentsService.bulkImport(req, res, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/progress/{id}:
|
* /api/payments/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -160,7 +159,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -169,7 +168,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -180,18 +179,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
await ProgressService.update(req.body.data, req.body.id, req.currentUser);
|
await PaymentsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/progress/{id}:
|
* /api/payments/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -207,7 +206,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -218,18 +217,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
await ProgressService.remove(req.params.id, req.currentUser);
|
await PaymentsService.remove(req.params.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/progress/deleteByIds:
|
* /api/payments/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -247,7 +246,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -256,29 +255,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
await ProgressService.deleteByIds(req.body.data, req.currentUser);
|
await PaymentsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/progress:
|
* /api/payments:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Get all progress
|
* summary: Get all payments
|
||||||
* description: Get all progress
|
* description: Get all payments
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Progress list successfully received
|
* description: Payments list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -290,14 +289,14 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await ProgressDBApi.findAll(
|
const payload = await PaymentsDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id','summary','notes',
|
const fields = ['id','reference',
|
||||||
|
|
||||||
'percent',
|
'amount',
|
||||||
'completed_at',
|
'paid_at',
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
@ -316,22 +315,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/progress/count:
|
* /api/payments/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Count all progress
|
* summary: Count all payments
|
||||||
* description: Count all progress
|
* description: Count all payments
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Progress count successfully received
|
* description: Payments count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -342,7 +341,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await ProgressDBApi.findAll(
|
const payload = await PaymentsDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -353,22 +352,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/progress/autocomplete:
|
* /api/payments/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Find all progress that match search criteria
|
* summary: Find all payments that match search criteria
|
||||||
* description: Find all progress that match search criteria
|
* description: Find all payments that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Progress list successfully received
|
* description: Payments list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -378,7 +377,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', async (req, res) => {
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
const payload = await ProgressDBApi.findAllAutocomplete(
|
const payload = await PaymentsDBApi.findAllAutocomplete(
|
||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -390,11 +389,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/progress/{id}:
|
* /api/payments/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Progress]
|
* tags: [Payments]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -410,7 +409,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Progress"
|
* $ref: "#/components/schemas/Payments"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -421,7 +420,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await ProgressDBApi.findBy(
|
const payload = await PaymentsDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const QuizzesService = require('../services/quizzes');
|
const ProductsService = require('../services/products');
|
||||||
const QuizzesDBApi = require('../db/api/quizzes');
|
const ProductsDBApi = require('../db/api/products');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,48 +15,52 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('quizzes'));
|
router.use(checkCrudPermissions('products'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Quizzes:
|
* Products:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
* title:
|
* sku:
|
||||||
* type: string
|
* type: string
|
||||||
* default: title
|
* default: sku
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* default: name
|
||||||
* description:
|
* description:
|
||||||
* type: string
|
* type: string
|
||||||
* default: description
|
* default: description
|
||||||
|
|
||||||
* passing_score:
|
* stock:
|
||||||
* type: integer
|
|
||||||
* format: int64
|
|
||||||
* time_limit:
|
|
||||||
* type: integer
|
* type: integer
|
||||||
* format: int64
|
* format: int64
|
||||||
|
|
||||||
|
* price:
|
||||||
|
* type: integer
|
||||||
|
* format: int64
|
||||||
|
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Quizzes
|
* name: Products
|
||||||
* description: The Quizzes managing API
|
* description: The Products managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/quizzes:
|
* /api/products:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -68,14 +72,14 @@ router.use(checkCrudPermissions('quizzes'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -86,7 +90,7 @@ router.use(checkCrudPermissions('quizzes'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await QuizzesService.create(req.body.data, req.currentUser, true, link.host);
|
await ProductsService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -97,7 +101,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -110,14 +114,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -129,18 +133,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await QuizzesService.bulkImport(req, res, true, link.host);
|
await ProductsService.bulkImport(req, res, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/quizzes/{id}:
|
* /api/products/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -163,7 +167,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -172,7 +176,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -183,18 +187,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
await QuizzesService.update(req.body.data, req.body.id, req.currentUser);
|
await ProductsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/quizzes/{id}:
|
* /api/products/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -210,7 +214,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -221,18 +225,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
await QuizzesService.remove(req.params.id, req.currentUser);
|
await ProductsService.remove(req.params.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/quizzes/deleteByIds:
|
* /api/products/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -250,7 +254,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -259,29 +263,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
await QuizzesService.deleteByIds(req.body.data, req.currentUser);
|
await ProductsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/quizzes:
|
* /api/products:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Get all quizzes
|
* summary: Get all products
|
||||||
* description: Get all quizzes
|
* description: Get all products
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Quizzes list successfully received
|
* description: Products list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -293,13 +297,13 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await QuizzesDBApi.findAll(
|
const payload = await ProductsDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id','title','description',
|
const fields = ['id','sku','name','description',
|
||||||
'passing_score','time_limit',
|
'stock',
|
||||||
|
'price',
|
||||||
|
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
@ -319,22 +323,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/quizzes/count:
|
* /api/products/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Count all quizzes
|
* summary: Count all products
|
||||||
* description: Count all quizzes
|
* description: Count all products
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Quizzes count successfully received
|
* description: Products count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -345,7 +349,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await QuizzesDBApi.findAll(
|
const payload = await ProductsDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -356,22 +360,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/quizzes/autocomplete:
|
* /api/products/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Find all quizzes that match search criteria
|
* summary: Find all products that match search criteria
|
||||||
* description: Find all quizzes that match search criteria
|
* description: Find all products that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Quizzes list successfully received
|
* description: Products list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -381,7 +385,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', async (req, res) => {
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
const payload = await QuizzesDBApi.findAllAutocomplete(
|
const payload = await ProductsDBApi.findAllAutocomplete(
|
||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -393,11 +397,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/quizzes/{id}:
|
* /api/products/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Quizzes]
|
* tags: [Products]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -413,7 +417,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Quizzes"
|
* $ref: "#/components/schemas/Products"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -424,7 +428,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await QuizzesDBApi.findBy(
|
const payload = await ProductsDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1,436 +0,0 @@
|
|||||||
|
|
||||||
const express = require('express');
|
|
||||||
|
|
||||||
const Quiz_questionsService = require('../services/quiz_questions');
|
|
||||||
const Quiz_questionsDBApi = require('../db/api/quiz_questions');
|
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
|
||||||
|
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
const { parse } = require('json2csv');
|
|
||||||
|
|
||||||
|
|
||||||
const {
|
|
||||||
checkCrudPermissions,
|
|
||||||
} = require('../middlewares/check-permissions');
|
|
||||||
|
|
||||||
router.use(checkCrudPermissions('quiz_questions'));
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* components:
|
|
||||||
* schemas:
|
|
||||||
* Quiz_questions:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
|
|
||||||
* question:
|
|
||||||
* type: string
|
|
||||||
* default: question
|
|
||||||
* choices:
|
|
||||||
* type: string
|
|
||||||
* default: choices
|
|
||||||
* answer:
|
|
||||||
* type: string
|
|
||||||
* default: answer
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* tags:
|
|
||||||
* name: Quiz_questions
|
|
||||||
* description: The Quiz_questions managing API
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/quiz_questions:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Add new item
|
|
||||||
* description: Add new item
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated item
|
|
||||||
* type: object
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item was successfully added
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 405:
|
|
||||||
* description: Invalid input data
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.post('/', wrapAsync(async (req, res) => {
|
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
|
||||||
const link = new URL(referer);
|
|
||||||
await Quiz_questionsService.create(req.body.data, req.currentUser, true, link.host);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/budgets/bulk-import:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Bulk import items
|
|
||||||
* description: Bulk import items
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated items
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The items were successfully imported
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 405:
|
|
||||||
* description: Invalid input data
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
|
||||||
const link = new URL(referer);
|
|
||||||
await Quiz_questionsService.bulkImport(req, res, true, link.host);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/quiz_questions/{id}:
|
|
||||||
* put:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Update the data of the selected item
|
|
||||||
* description: Update the data of the selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: Item ID to update
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* requestBody:
|
|
||||||
* description: Set new item data
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* id:
|
|
||||||
* description: ID of the updated item
|
|
||||||
* type: string
|
|
||||||
* data:
|
|
||||||
* description: Data of the updated item
|
|
||||||
* type: object
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* required:
|
|
||||||
* - id
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item data was successfully updated
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
|
||||||
await Quiz_questionsService.update(req.body.data, req.body.id, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/quiz_questions/{id}:
|
|
||||||
* delete:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Delete the selected item
|
|
||||||
* description: Delete the selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: Item ID to delete
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The item was successfully deleted
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
|
||||||
await Quiz_questionsService.remove(req.params.id, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/quiz_questions/deleteByIds:
|
|
||||||
* post:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Delete the selected item list
|
|
||||||
* description: Delete the selected item list
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* properties:
|
|
||||||
* ids:
|
|
||||||
* description: IDs of the updated items
|
|
||||||
* type: array
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The items was successfully deleted
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Items not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
|
||||||
await Quiz_questionsService.deleteByIds(req.body.data, req.currentUser);
|
|
||||||
const payload = true;
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/quiz_questions:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Get all quiz_questions
|
|
||||||
* description: Get all quiz_questions
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Quiz_questions list successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/', wrapAsync(async (req, res) => {
|
|
||||||
const filetype = req.query.filetype
|
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
|
||||||
const payload = await Quiz_questionsDBApi.findAll(
|
|
||||||
req.query, { currentUser }
|
|
||||||
);
|
|
||||||
if (filetype && filetype === 'csv') {
|
|
||||||
const fields = ['id','question','choices','answer',
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
];
|
|
||||||
const opts = { fields };
|
|
||||||
try {
|
|
||||||
const csv = parse(payload.rows, opts);
|
|
||||||
res.status(200).attachment(csv);
|
|
||||||
res.send(csv)
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/quiz_questions/count:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Count all quiz_questions
|
|
||||||
* description: Count all quiz_questions
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Quiz_questions count successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/count', wrapAsync(async (req, res) => {
|
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
|
||||||
const payload = await Quiz_questionsDBApi.findAll(
|
|
||||||
req.query,
|
|
||||||
null,
|
|
||||||
{ countOnly: true, currentUser }
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/quiz_questions/autocomplete:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Find all quiz_questions that match search criteria
|
|
||||||
* description: Find all quiz_questions that match search criteria
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Quiz_questions list successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: array
|
|
||||||
* items:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Data not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/autocomplete', async (req, res) => {
|
|
||||||
|
|
||||||
const payload = await Quiz_questionsDBApi.findAllAutocomplete(
|
|
||||||
req.query.query,
|
|
||||||
req.query.limit,
|
|
||||||
req.query.offset,
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/quiz_questions/{id}:
|
|
||||||
* get:
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* tags: [Quiz_questions]
|
|
||||||
* summary: Get selected item
|
|
||||||
* description: Get selected item
|
|
||||||
* parameters:
|
|
||||||
* - in: path
|
|
||||||
* name: id
|
|
||||||
* description: ID of item to get
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: Selected item successfully received
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* $ref: "#/components/schemas/Quiz_questions"
|
|
||||||
* 400:
|
|
||||||
* description: Invalid ID supplied
|
|
||||||
* 401:
|
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
|
||||||
* 404:
|
|
||||||
* description: Item not found
|
|
||||||
* 500:
|
|
||||||
* description: Some server error
|
|
||||||
*/
|
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
|
||||||
const payload = await Quiz_questionsDBApi.findBy(
|
|
||||||
{ id: req.params.id },
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
res.status(200).send(payload);
|
|
||||||
}));
|
|
||||||
|
|
||||||
router.use('/', require('../helpers').commonErrorHandler);
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const CertificatesService = require('../services/certificates');
|
const ShipmentsService = require('../services/shipments');
|
||||||
const CertificatesDBApi = require('../db/api/certificates');
|
const ShipmentsDBApi = require('../db/api/shipments');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,39 +15,43 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('certificates'));
|
router.use(checkCrudPermissions('shipments'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Certificates:
|
* Shipments:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
* serial:
|
* carrier:
|
||||||
* type: string
|
* type: string
|
||||||
* default: serial
|
* default: carrier
|
||||||
|
* tracking_number:
|
||||||
|
* type: string
|
||||||
|
* default: tracking_number
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Certificates
|
* name: Shipments
|
||||||
* description: The Certificates managing API
|
* description: The Shipments managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/certificates:
|
* /api/shipments:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -59,14 +63,14 @@ router.use(checkCrudPermissions('certificates'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -77,7 +81,7 @@ router.use(checkCrudPermissions('certificates'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await CertificatesService.create(req.body.data, req.currentUser, true, link.host);
|
await ShipmentsService.create(req.body.data, req.currentUser, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -88,7 +92,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -101,14 +105,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -120,18 +124,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
const link = new URL(referer);
|
||||||
await CertificatesService.bulkImport(req, res, true, link.host);
|
await ShipmentsService.bulkImport(req, res, true, link.host);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/certificates/{id}:
|
* /api/shipments/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -154,7 +158,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -163,7 +167,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -174,18 +178,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
await CertificatesService.update(req.body.data, req.body.id, req.currentUser);
|
await ShipmentsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/certificates/{id}:
|
* /api/shipments/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -201,7 +205,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -212,18 +216,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
await CertificatesService.remove(req.params.id, req.currentUser);
|
await ShipmentsService.remove(req.params.id, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/certificates/deleteByIds:
|
* /api/shipments/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -241,7 +245,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -250,29 +254,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
await CertificatesService.deleteByIds(req.body.data, req.currentUser);
|
await ShipmentsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
const payload = true;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/certificates:
|
* /api/shipments:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Get all certificates
|
* summary: Get all shipments
|
||||||
* description: Get all certificates
|
* description: Get all shipments
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Certificates list successfully received
|
* description: Shipments list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -284,14 +288,14 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await CertificatesDBApi.findAll(
|
const payload = await ShipmentsDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
const fields = ['id','serial',
|
const fields = ['id','carrier','tracking_number',
|
||||||
|
|
||||||
|
|
||||||
'issued_at',
|
'shipped_at','delivered_at',
|
||||||
];
|
];
|
||||||
const opts = { fields };
|
const opts = { fields };
|
||||||
try {
|
try {
|
||||||
@ -310,22 +314,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/certificates/count:
|
* /api/shipments/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Count all certificates
|
* summary: Count all shipments
|
||||||
* description: Count all certificates
|
* description: Count all shipments
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Certificates count successfully received
|
* description: Shipments count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -336,7 +340,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await CertificatesDBApi.findAll(
|
const payload = await ShipmentsDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -347,22 +351,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/certificates/autocomplete:
|
* /api/shipments/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Find all certificates that match search criteria
|
* summary: Find all shipments that match search criteria
|
||||||
* description: Find all certificates that match search criteria
|
* description: Find all shipments that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Certificates list successfully received
|
* description: Shipments list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -372,7 +376,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', async (req, res) => {
|
router.get('/autocomplete', async (req, res) => {
|
||||||
|
|
||||||
const payload = await CertificatesDBApi.findAllAutocomplete(
|
const payload = await ShipmentsDBApi.findAllAutocomplete(
|
||||||
req.query.query,
|
req.query.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -384,11 +388,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/certificates/{id}:
|
* /api/shipments/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Certificates]
|
* tags: [Shipments]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -404,7 +408,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Certificates"
|
* $ref: "#/components/schemas/Shipments"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -415,7 +419,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await CertificatesDBApi.findBy(
|
const payload = await ShipmentsDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
const db = require('../db/models');
|
|
||||||
const AnnouncementsDBApi = require('../db/api/announcements');
|
|
||||||
const processFile = require("../middlewares/upload");
|
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
|
||||||
const csv = require('csv-parser');
|
|
||||||
const axios = require('axios');
|
|
||||||
const config = require('../config');
|
|
||||||
const stream = require('stream');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class AnnouncementsService {
|
|
||||||
static async create(data, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
try {
|
|
||||||
await AnnouncementsDBApi.create(
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await processFile(req, res);
|
|
||||||
const bufferStream = new stream.PassThrough();
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
bufferStream
|
|
||||||
.pipe(csv())
|
|
||||||
.on('data', (data) => results.push(data))
|
|
||||||
.on('end', async () => {
|
|
||||||
console.log('CSV results', results);
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.on('error', (error) => reject(error));
|
|
||||||
})
|
|
||||||
|
|
||||||
await AnnouncementsDBApi.bulkImport(results, {
|
|
||||||
transaction,
|
|
||||||
ignoreDuplicates: true,
|
|
||||||
validate: true,
|
|
||||||
currentUser: req.currentUser
|
|
||||||
});
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(data, id, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
try {
|
|
||||||
let announcements = await AnnouncementsDBApi.findBy(
|
|
||||||
{id},
|
|
||||||
{transaction},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!announcements) {
|
|
||||||
throw new ValidationError(
|
|
||||||
'announcementsNotFound',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedAnnouncements = await AnnouncementsDBApi.update(
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
return updatedAnnouncements;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static async deleteByIds(ids, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await AnnouncementsDBApi.deleteByIds(ids, {
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async remove(id, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await AnnouncementsDBApi.remove(
|
|
||||||
id,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
138
backend/src/services/categories.js
Normal file
138
backend/src/services/categories.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const CategoriesDBApi = require('../db/api/categories');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class CategoriesService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await CategoriesDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await CategoriesDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let categories = await CategoriesDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!categories) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'categoriesNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedCategories = await CategoriesDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedCategories;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CategoriesDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CategoriesDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
const db = require('../db/models');
|
|
||||||
const CertificatesDBApi = require('../db/api/certificates');
|
|
||||||
const processFile = require("../middlewares/upload");
|
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
|
||||||
const csv = require('csv-parser');
|
|
||||||
const axios = require('axios');
|
|
||||||
const config = require('../config');
|
|
||||||
const stream = require('stream');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class CertificatesService {
|
|
||||||
static async create(data, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
try {
|
|
||||||
await CertificatesDBApi.create(
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await processFile(req, res);
|
|
||||||
const bufferStream = new stream.PassThrough();
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
bufferStream
|
|
||||||
.pipe(csv())
|
|
||||||
.on('data', (data) => results.push(data))
|
|
||||||
.on('end', async () => {
|
|
||||||
console.log('CSV results', results);
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.on('error', (error) => reject(error));
|
|
||||||
})
|
|
||||||
|
|
||||||
await CertificatesDBApi.bulkImport(results, {
|
|
||||||
transaction,
|
|
||||||
ignoreDuplicates: true,
|
|
||||||
validate: true,
|
|
||||||
currentUser: req.currentUser
|
|
||||||
});
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(data, id, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
try {
|
|
||||||
let certificates = await CertificatesDBApi.findBy(
|
|
||||||
{id},
|
|
||||||
{transaction},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!certificates) {
|
|
||||||
throw new ValidationError(
|
|
||||||
'certificatesNotFound',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedCertificates = await CertificatesDBApi.update(
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
return updatedCertificates;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static async deleteByIds(ids, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await CertificatesDBApi.deleteByIds(ids, {
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async remove(id, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await CertificatesDBApi.remove(
|
|
||||||
id,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
const db = require('../db/models');
|
|
||||||
const Course_categoriesDBApi = require('../db/api/course_categories');
|
|
||||||
const processFile = require("../middlewares/upload");
|
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
|
||||||
const csv = require('csv-parser');
|
|
||||||
const axios = require('axios');
|
|
||||||
const config = require('../config');
|
|
||||||
const stream = require('stream');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class Course_categoriesService {
|
|
||||||
static async create(data, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
try {
|
|
||||||
await Course_categoriesDBApi.create(
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await processFile(req, res);
|
|
||||||
const bufferStream = new stream.PassThrough();
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
bufferStream
|
|
||||||
.pipe(csv())
|
|
||||||
.on('data', (data) => results.push(data))
|
|
||||||
.on('end', async () => {
|
|
||||||
console.log('CSV results', results);
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.on('error', (error) => reject(error));
|
|
||||||
})
|
|
||||||
|
|
||||||
await Course_categoriesDBApi.bulkImport(results, {
|
|
||||||
transaction,
|
|
||||||
ignoreDuplicates: true,
|
|
||||||
validate: true,
|
|
||||||
currentUser: req.currentUser
|
|
||||||
});
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(data, id, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
try {
|
|
||||||
let course_categories = await Course_categoriesDBApi.findBy(
|
|
||||||
{id},
|
|
||||||
{transaction},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!course_categories) {
|
|
||||||
throw new ValidationError(
|
|
||||||
'course_categoriesNotFound',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedCourse_categories = await Course_categoriesDBApi.update(
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
return updatedCourse_categories;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static async deleteByIds(ids, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Course_categoriesDBApi.deleteByIds(ids, {
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async remove(id, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Course_categoriesDBApi.remove(
|
|
||||||
id,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const db = require('../db/models');
|
const db = require('../db/models');
|
||||||
const CoursesDBApi = require('../db/api/courses');
|
const CustomersDBApi = require('../db/api/customers');
|
||||||
const processFile = require("../middlewares/upload");
|
const processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
const csv = require('csv-parser');
|
||||||
@ -11,11 +11,11 @@ const stream = require('stream');
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class CoursesService {
|
module.exports = class CustomersService {
|
||||||
static async create(data, currentUser) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
await CoursesDBApi.create(
|
await CustomersDBApi.create(
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -51,7 +51,7 @@ module.exports = class CoursesService {
|
|||||||
.on('error', (error) => reject(error));
|
.on('error', (error) => reject(error));
|
||||||
})
|
})
|
||||||
|
|
||||||
await CoursesDBApi.bulkImport(results, {
|
await CustomersDBApi.bulkImport(results, {
|
||||||
transaction,
|
transaction,
|
||||||
ignoreDuplicates: true,
|
ignoreDuplicates: true,
|
||||||
validate: true,
|
validate: true,
|
||||||
@ -68,18 +68,18 @@ module.exports = class CoursesService {
|
|||||||
static async update(data, id, currentUser) {
|
static async update(data, id, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
let courses = await CoursesDBApi.findBy(
|
let customers = await CustomersDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{transaction},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!courses) {
|
if (!customers) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'coursesNotFound',
|
'customersNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedCourses = await CoursesDBApi.update(
|
const updatedCustomers = await CustomersDBApi.update(
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ module.exports = class CoursesService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
return updatedCourses;
|
return updatedCustomers;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@ -101,7 +101,7 @@ module.exports = class CoursesService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await CoursesDBApi.deleteByIds(ids, {
|
await CustomersDBApi.deleteByIds(ids, {
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ module.exports = class CoursesService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await CoursesDBApi.remove(
|
await CustomersDBApi.remove(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -1,6 +1,6 @@
|
|||||||
const errors = {
|
const errors = {
|
||||||
app: {
|
app: {
|
||||||
title: 'CourseFlow LMS',
|
title: 'Store Operations Manager',
|
||||||
},
|
},
|
||||||
|
|
||||||
auth: {
|
auth: {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
const db = require('../db/models');
|
const db = require('../db/models');
|
||||||
const EnrollmentsDBApi = require('../db/api/enrollments');
|
const Order_itemsDBApi = require('../db/api/order_items');
|
||||||
const processFile = require("../middlewares/upload");
|
const processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
const csv = require('csv-parser');
|
||||||
@ -11,11 +11,11 @@ const stream = require('stream');
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class EnrollmentsService {
|
module.exports = class Order_itemsService {
|
||||||
static async create(data, currentUser) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
await EnrollmentsDBApi.create(
|
await Order_itemsDBApi.create(
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -51,7 +51,7 @@ module.exports = class EnrollmentsService {
|
|||||||
.on('error', (error) => reject(error));
|
.on('error', (error) => reject(error));
|
||||||
})
|
})
|
||||||
|
|
||||||
await EnrollmentsDBApi.bulkImport(results, {
|
await Order_itemsDBApi.bulkImport(results, {
|
||||||
transaction,
|
transaction,
|
||||||
ignoreDuplicates: true,
|
ignoreDuplicates: true,
|
||||||
validate: true,
|
validate: true,
|
||||||
@ -68,18 +68,18 @@ module.exports = class EnrollmentsService {
|
|||||||
static async update(data, id, currentUser) {
|
static async update(data, id, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
let enrollments = await EnrollmentsDBApi.findBy(
|
let order_items = await Order_itemsDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{transaction},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!enrollments) {
|
if (!order_items) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'enrollmentsNotFound',
|
'order_itemsNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedEnrollments = await EnrollmentsDBApi.update(
|
const updatedOrder_items = await Order_itemsDBApi.update(
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ module.exports = class EnrollmentsService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
return updatedEnrollments;
|
return updatedOrder_items;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@ -101,7 +101,7 @@ module.exports = class EnrollmentsService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await EnrollmentsDBApi.deleteByIds(ids, {
|
await Order_itemsDBApi.deleteByIds(ids, {
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ module.exports = class EnrollmentsService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await EnrollmentsDBApi.remove(
|
await Order_itemsDBApi.remove(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const db = require('../db/models');
|
const db = require('../db/models');
|
||||||
const LessonsDBApi = require('../db/api/lessons');
|
const OrdersDBApi = require('../db/api/orders');
|
||||||
const processFile = require("../middlewares/upload");
|
const processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
const csv = require('csv-parser');
|
||||||
@ -11,11 +11,11 @@ const stream = require('stream');
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class LessonsService {
|
module.exports = class OrdersService {
|
||||||
static async create(data, currentUser) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
await LessonsDBApi.create(
|
await OrdersDBApi.create(
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -51,7 +51,7 @@ module.exports = class LessonsService {
|
|||||||
.on('error', (error) => reject(error));
|
.on('error', (error) => reject(error));
|
||||||
})
|
})
|
||||||
|
|
||||||
await LessonsDBApi.bulkImport(results, {
|
await OrdersDBApi.bulkImport(results, {
|
||||||
transaction,
|
transaction,
|
||||||
ignoreDuplicates: true,
|
ignoreDuplicates: true,
|
||||||
validate: true,
|
validate: true,
|
||||||
@ -68,18 +68,18 @@ module.exports = class LessonsService {
|
|||||||
static async update(data, id, currentUser) {
|
static async update(data, id, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
let lessons = await LessonsDBApi.findBy(
|
let orders = await OrdersDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{transaction},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!lessons) {
|
if (!orders) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'lessonsNotFound',
|
'ordersNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedLessons = await LessonsDBApi.update(
|
const updatedOrders = await OrdersDBApi.update(
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ module.exports = class LessonsService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
return updatedLessons;
|
return updatedOrders;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@ -101,7 +101,7 @@ module.exports = class LessonsService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await LessonsDBApi.deleteByIds(ids, {
|
await OrdersDBApi.deleteByIds(ids, {
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ module.exports = class LessonsService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await LessonsDBApi.remove(
|
await OrdersDBApi.remove(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const db = require('../db/models');
|
const db = require('../db/models');
|
||||||
const ProgressDBApi = require('../db/api/progress');
|
const PaymentsDBApi = require('../db/api/payments');
|
||||||
const processFile = require("../middlewares/upload");
|
const processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
const csv = require('csv-parser');
|
||||||
@ -11,11 +11,11 @@ const stream = require('stream');
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class ProgressService {
|
module.exports = class PaymentsService {
|
||||||
static async create(data, currentUser) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
await ProgressDBApi.create(
|
await PaymentsDBApi.create(
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -51,7 +51,7 @@ module.exports = class ProgressService {
|
|||||||
.on('error', (error) => reject(error));
|
.on('error', (error) => reject(error));
|
||||||
})
|
})
|
||||||
|
|
||||||
await ProgressDBApi.bulkImport(results, {
|
await PaymentsDBApi.bulkImport(results, {
|
||||||
transaction,
|
transaction,
|
||||||
ignoreDuplicates: true,
|
ignoreDuplicates: true,
|
||||||
validate: true,
|
validate: true,
|
||||||
@ -68,18 +68,18 @@ module.exports = class ProgressService {
|
|||||||
static async update(data, id, currentUser) {
|
static async update(data, id, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
let progress = await ProgressDBApi.findBy(
|
let payments = await PaymentsDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{transaction},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!progress) {
|
if (!payments) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'progressNotFound',
|
'paymentsNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedProgress = await ProgressDBApi.update(
|
const updatedPayments = await PaymentsDBApi.update(
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ module.exports = class ProgressService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
return updatedProgress;
|
return updatedPayments;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@ -101,7 +101,7 @@ module.exports = class ProgressService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ProgressDBApi.deleteByIds(ids, {
|
await PaymentsDBApi.deleteByIds(ids, {
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ module.exports = class ProgressService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ProgressDBApi.remove(
|
await PaymentsDBApi.remove(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const db = require('../db/models');
|
const db = require('../db/models');
|
||||||
const QuizzesDBApi = require('../db/api/quizzes');
|
const ProductsDBApi = require('../db/api/products');
|
||||||
const processFile = require("../middlewares/upload");
|
const processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
const csv = require('csv-parser');
|
||||||
@ -11,11 +11,11 @@ const stream = require('stream');
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class QuizzesService {
|
module.exports = class ProductsService {
|
||||||
static async create(data, currentUser) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
await QuizzesDBApi.create(
|
await ProductsDBApi.create(
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -51,7 +51,7 @@ module.exports = class QuizzesService {
|
|||||||
.on('error', (error) => reject(error));
|
.on('error', (error) => reject(error));
|
||||||
})
|
})
|
||||||
|
|
||||||
await QuizzesDBApi.bulkImport(results, {
|
await ProductsDBApi.bulkImport(results, {
|
||||||
transaction,
|
transaction,
|
||||||
ignoreDuplicates: true,
|
ignoreDuplicates: true,
|
||||||
validate: true,
|
validate: true,
|
||||||
@ -68,18 +68,18 @@ module.exports = class QuizzesService {
|
|||||||
static async update(data, id, currentUser) {
|
static async update(data, id, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
let quizzes = await QuizzesDBApi.findBy(
|
let products = await ProductsDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{transaction},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!quizzes) {
|
if (!products) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'quizzesNotFound',
|
'productsNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedQuizzes = await QuizzesDBApi.update(
|
const updatedProducts = await ProductsDBApi.update(
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ module.exports = class QuizzesService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
return updatedQuizzes;
|
return updatedProducts;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@ -101,7 +101,7 @@ module.exports = class QuizzesService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await QuizzesDBApi.deleteByIds(ids, {
|
await ProductsDBApi.deleteByIds(ids, {
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ module.exports = class QuizzesService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await QuizzesDBApi.remove(
|
await ProductsDBApi.remove(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -1,138 +0,0 @@
|
|||||||
const db = require('../db/models');
|
|
||||||
const Quiz_questionsDBApi = require('../db/api/quiz_questions');
|
|
||||||
const processFile = require("../middlewares/upload");
|
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
|
||||||
const csv = require('csv-parser');
|
|
||||||
const axios = require('axios');
|
|
||||||
const config = require('../config');
|
|
||||||
const stream = require('stream');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class Quiz_questionsService {
|
|
||||||
static async create(data, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
try {
|
|
||||||
await Quiz_questionsDBApi.create(
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await processFile(req, res);
|
|
||||||
const bufferStream = new stream.PassThrough();
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
bufferStream
|
|
||||||
.pipe(csv())
|
|
||||||
.on('data', (data) => results.push(data))
|
|
||||||
.on('end', async () => {
|
|
||||||
console.log('CSV results', results);
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.on('error', (error) => reject(error));
|
|
||||||
})
|
|
||||||
|
|
||||||
await Quiz_questionsDBApi.bulkImport(results, {
|
|
||||||
transaction,
|
|
||||||
ignoreDuplicates: true,
|
|
||||||
validate: true,
|
|
||||||
currentUser: req.currentUser
|
|
||||||
});
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(data, id, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
try {
|
|
||||||
let quiz_questions = await Quiz_questionsDBApi.findBy(
|
|
||||||
{id},
|
|
||||||
{transaction},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!quiz_questions) {
|
|
||||||
throw new ValidationError(
|
|
||||||
'quiz_questionsNotFound',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedQuiz_questions = await Quiz_questionsDBApi.update(
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
return updatedQuiz_questions;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static async deleteByIds(ids, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Quiz_questionsDBApi.deleteByIds(ids, {
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async remove(id, currentUser) {
|
|
||||||
const transaction = await db.sequelize.transaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Quiz_questionsDBApi.remove(
|
|
||||||
id,
|
|
||||||
{
|
|
||||||
currentUser,
|
|
||||||
transaction,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await transaction.commit();
|
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -66,11 +66,24 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
"course_categories": [
|
"products": [
|
||||||
|
|
||||||
|
"sku",
|
||||||
|
|
||||||
"name",
|
"name",
|
||||||
|
|
||||||
"slug",
|
"description",
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"categories": [
|
||||||
|
|
||||||
|
"name",
|
||||||
|
|
||||||
"description",
|
"description",
|
||||||
|
|
||||||
@ -81,65 +94,19 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
"courses": [
|
"customers": [
|
||||||
|
|
||||||
"title",
|
"name",
|
||||||
|
|
||||||
"short_description",
|
"email",
|
||||||
|
|
||||||
"description",
|
"phone",
|
||||||
|
|
||||||
"language",
|
"address",
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"lessons": [
|
|
||||||
|
|
||||||
"title",
|
|
||||||
|
|
||||||
"content",
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"enrollments": [
|
|
||||||
|
|
||||||
"enrollment_code",
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"progress": [
|
|
||||||
|
|
||||||
"summary",
|
|
||||||
|
|
||||||
"notes",
|
"notes",
|
||||||
|
|
||||||
],
|
"tax_number",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"quizzes": [
|
|
||||||
|
|
||||||
"title",
|
|
||||||
|
|
||||||
"description",
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -148,13 +115,11 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
"quiz_questions": [
|
"orders": [
|
||||||
|
|
||||||
"question",
|
"order_number",
|
||||||
|
|
||||||
"choices",
|
"shipping_address",
|
||||||
|
|
||||||
"answer",
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -163,11 +128,9 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
"announcements": [
|
"order_items": [
|
||||||
|
|
||||||
"title",
|
"name",
|
||||||
|
|
||||||
"content",
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -176,9 +139,22 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
"certificates": [
|
"payments": [
|
||||||
|
|
||||||
"serial",
|
"reference",
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"shipments": [
|
||||||
|
|
||||||
|
"carrier",
|
||||||
|
|
||||||
|
"tracking_number",
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -195,59 +171,11 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"products": [
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"courses": [
|
|
||||||
|
|
||||||
"price",
|
"price",
|
||||||
|
|
||||||
"duration",
|
"stock",
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"lessons": [
|
|
||||||
|
|
||||||
"order",
|
|
||||||
|
|
||||||
"duration",
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"enrollments": [
|
|
||||||
|
|
||||||
"progress_percent",
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"progress": [
|
|
||||||
|
|
||||||
"percent",
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"quizzes": [
|
|
||||||
|
|
||||||
"passing_score",
|
|
||||||
|
|
||||||
"time_limit",
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -263,6 +191,40 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"orders": [
|
||||||
|
|
||||||
|
"total",
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"order_items": [
|
||||||
|
|
||||||
|
"quantity",
|
||||||
|
|
||||||
|
"unit_price",
|
||||||
|
|
||||||
|
"total_price",
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"payments": [
|
||||||
|
|
||||||
|
"amount",
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
138
backend/src/services/shipments.js
Normal file
138
backend/src/services/shipments.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const ShipmentsDBApi = require('../db/api/shipments');
|
||||||
|
const processFile = require("../middlewares/upload");
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
const csv = require('csv-parser');
|
||||||
|
const axios = require('axios');
|
||||||
|
const config = require('../config');
|
||||||
|
const stream = require('stream');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class ShipmentsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await ShipmentsDBApi.create(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await processFile(req, res);
|
||||||
|
const bufferStream = new stream.PassThrough();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
bufferStream
|
||||||
|
.pipe(csv())
|
||||||
|
.on('data', (data) => results.push(data))
|
||||||
|
.on('end', async () => {
|
||||||
|
console.log('CSV results', results);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', (error) => reject(error));
|
||||||
|
})
|
||||||
|
|
||||||
|
await ShipmentsDBApi.bulkImport(results, {
|
||||||
|
transaction,
|
||||||
|
ignoreDuplicates: true,
|
||||||
|
validate: true,
|
||||||
|
currentUser: req.currentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
let shipments = await ShipmentsDBApi.findBy(
|
||||||
|
{id},
|
||||||
|
{transaction},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!shipments) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'shipmentsNotFound',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedShipments = await ShipmentsDBApi.update(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
return updatedShipments;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ShipmentsDBApi.deleteByIds(ids, {
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ShipmentsDBApi.remove(
|
||||||
|
id,
|
||||||
|
{
|
||||||
|
currentUser,
|
||||||
|
transaction,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -2,7 +2,7 @@ const chokidar = require('chokidar');
|
|||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
const nodemon = require('nodemon');
|
const nodemon = require('nodemon');
|
||||||
|
|
||||||
const nodeEnv = process.env.NODE_ENV || 'dev_stage';
|
const nodeEnv = 'dev_stage';
|
||||||
const childEnv = { ...process.env, NODE_ENV: nodeEnv };
|
const childEnv = { ...process.env, NODE_ENV: nodeEnv };
|
||||||
|
|
||||||
const migrationsWatcher = chokidar.watch('./src/db/migrations', {
|
const migrationsWatcher = chokidar.watch('./src/db/migrations', {
|
||||||
|
|||||||
@ -25,7 +25,7 @@ services:
|
|||||||
- ./data/db:/var/lib/postgresql/data
|
- ./data/db:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||||
- POSTGRES_DB=db_courseflow_lms
|
- POSTGRES_DB=db_store_operations_manager
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
logging:
|
logging:
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# CourseFlow LMS
|
# Store Operations Manager
|
||||||
|
|
||||||
## This project was generated by Flatlogic Platform.
|
## This project was generated by Flatlogic Platform.
|
||||||
## Install
|
## Install
|
||||||
|
|||||||
@ -1,476 +0,0 @@
|
|||||||
import React, { useEffect, useState, useMemo } from 'react'
|
|
||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
|
||||||
import BaseButton from '../BaseButton'
|
|
||||||
import CardBoxModal from '../CardBoxModal'
|
|
||||||
import CardBox from "../CardBox";
|
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/announcements/announcementsSlice'
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Field, Form, Formik } from "formik";
|
|
||||||
import {
|
|
||||||
DataGrid,
|
|
||||||
GridColDef,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import {loadColumns} from "./configureAnnouncementsCols";
|
|
||||||
import _ from 'lodash';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import {dataGridStyles} from "../../styles";
|
|
||||||
|
|
||||||
|
|
||||||
import ListAnnouncements from './ListAnnouncements';
|
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
|
||||||
|
|
||||||
const TableSampleAnnouncements = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const pagesList = [];
|
|
||||||
const [id, setId] = useState(null);
|
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
|
||||||
const [filterRequest, setFilterRequest] = React.useState('');
|
|
||||||
const [columns, setColumns] = useState<GridColDef[]>([]);
|
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
|
||||||
const [sortModel, setSortModel] = useState([
|
|
||||||
{
|
|
||||||
field: '',
|
|
||||||
sort: 'desc',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { announcements, loading, count, notify: announcementsNotify, refetch } = useAppSelector((state) => state.announcements)
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
|
|
||||||
for (let i = 0; i < numPages; i++) {
|
|
||||||
pagesList.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadData = async (page = currentPage, request = filterRequest) => {
|
|
||||||
if (page !== currentPage) setCurrentPage(page);
|
|
||||||
if (request !== filterRequest) setFilterRequest(request);
|
|
||||||
const { sort, field } = sortModel[0];
|
|
||||||
|
|
||||||
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
|
|
||||||
dispatch(fetch({ limit: perPage, page, query }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (announcementsNotify.showNotification) {
|
|
||||||
notify(announcementsNotify.typeNotification, announcementsNotify.textNotification);
|
|
||||||
}
|
|
||||||
}, [announcementsNotify.showNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
loadData();
|
|
||||||
}, [sortModel, currentUser]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (refetch) {
|
|
||||||
loadData(0);
|
|
||||||
dispatch(setRefetch(false));
|
|
||||||
}
|
|
||||||
}, [refetch, dispatch]);
|
|
||||||
|
|
||||||
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
|
|
||||||
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
|
|
||||||
|
|
||||||
const handleModalAction = () => {
|
|
||||||
setIsModalInfoActive(false)
|
|
||||||
setIsModalTrashActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteModalAction = (id: string) => {
|
|
||||||
setId(id)
|
|
||||||
setIsModalTrashActive(true)
|
|
||||||
}
|
|
||||||
const handleDeleteAction = async () => {
|
|
||||||
if (id) {
|
|
||||||
await dispatch(deleteItem(id));
|
|
||||||
await loadData(0);
|
|
||||||
setIsModalTrashActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateFilterRequests = useMemo(() => {
|
|
||||||
let request = '&';
|
|
||||||
filterItems.forEach((item) => {
|
|
||||||
const isRangeFilter = filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title === item.fields.selectedField &&
|
|
||||||
(filter.number || filter.date),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRangeFilter) {
|
|
||||||
const from = item.fields.filterValueFrom;
|
|
||||||
const to = item.fields.filterValueTo;
|
|
||||||
if (from) {
|
|
||||||
request += `${item.fields.selectedField}Range=${from}&`;
|
|
||||||
}
|
|
||||||
if (to) {
|
|
||||||
request += `${item.fields.selectedField}Range=${to}&`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const value = item.fields.filterValue;
|
|
||||||
if (value) {
|
|
||||||
request += `${item.fields.selectedField}=${value}&`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return request;
|
|
||||||
}, [filterItems, filters]);
|
|
||||||
|
|
||||||
const deleteFilter = (value) => {
|
|
||||||
const newItems = filterItems.filter((item) => item.id !== value);
|
|
||||||
|
|
||||||
if (newItems.length) {
|
|
||||||
setFilterItems(newItems);
|
|
||||||
} else {
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
setFilterItems(newItems);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
loadData(0, generateFilterRequests);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (id) => (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
const name = e.target.name;
|
|
||||||
|
|
||||||
setFilterItems(
|
|
||||||
filterItems.map((item) => {
|
|
||||||
if (item.id !== id) return item;
|
|
||||||
if (name === 'selectedField') return { id, fields: { [name]: value } };
|
|
||||||
|
|
||||||
return { id, fields: { ...item.fields, [name]: value } }
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setFilterItems([]);
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
|
||||||
loadData(page);
|
|
||||||
setCurrentPage(page);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
|
|
||||||
loadColumns(
|
|
||||||
handleDeleteModalAction,
|
|
||||||
`announcements`,
|
|
||||||
currentUser,
|
|
||||||
).then((newCols) => setColumns(newCols));
|
|
||||||
}, [currentUser]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleTableSubmit = async (id: string, data) => {
|
|
||||||
|
|
||||||
if (!_.isEmpty(data)) {
|
|
||||||
await dispatch(update({ id, data }))
|
|
||||||
.unwrap()
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteRows = async (selectedRows) => {
|
|
||||||
await dispatch(deleteItemsByIds(selectedRows));
|
|
||||||
await loadData(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
const controlClasses =
|
|
||||||
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
|
|
||||||
` ${bgColor} ${focusRing} ${corners} ` +
|
|
||||||
'dark:bg-slate-800 border';
|
|
||||||
|
|
||||||
|
|
||||||
const dataGrid = (
|
|
||||||
<div className='relative overflow-x-auto'>
|
|
||||||
<DataGrid
|
|
||||||
autoHeight
|
|
||||||
rowHeight={64}
|
|
||||||
sx={dataGridStyles}
|
|
||||||
className={'datagrid--table'}
|
|
||||||
getRowClassName={() => `datagrid--row`}
|
|
||||||
rows={announcements ?? []}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{
|
|
||||||
pagination: {
|
|
||||||
paginationModel: {
|
|
||||||
pageSize: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
disableRowSelectionOnClick
|
|
||||||
onProcessRowUpdateError={(params) => {
|
|
||||||
console.log('Error', params);
|
|
||||||
}}
|
|
||||||
processRowUpdate={async (newRow, oldRow) => {
|
|
||||||
const data = dataFormatter.dataGridEditFormatter(newRow);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await handleTableSubmit(newRow.id, data);
|
|
||||||
return newRow;
|
|
||||||
} catch {
|
|
||||||
return oldRow;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
sortingMode={'server'}
|
|
||||||
checkboxSelection
|
|
||||||
onRowSelectionModelChange={(ids) => {
|
|
||||||
setSelectedRows(ids)
|
|
||||||
}}
|
|
||||||
onSortModelChange={(params) => {
|
|
||||||
params.length
|
|
||||||
? setSortModel(params)
|
|
||||||
: setSortModel([{ field: '', sort: 'desc' }]);
|
|
||||||
}}
|
|
||||||
rowCount={count}
|
|
||||||
pageSizeOptions={[10]}
|
|
||||||
paginationMode={'server'}
|
|
||||||
loading={loading}
|
|
||||||
onPaginationModelChange={(params) => {
|
|
||||||
onPageChange(params.page);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
|
||||||
<CardBox>
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
checkboxes: ['lorem'],
|
|
||||||
switches: ['lorem'],
|
|
||||||
radio: 'lorem',
|
|
||||||
}}
|
|
||||||
onSubmit={() => null}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<>
|
|
||||||
{filterItems && filterItems.map((filterItem) => {
|
|
||||||
return (
|
|
||||||
<div key={filterItem.id} className="flex mb-4">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Filter</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='selectedField'
|
|
||||||
id='selectedField'
|
|
||||||
component='select'
|
|
||||||
value={filterItem?.fields?.selectedField || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
{filters.map((selectOption) => (
|
|
||||||
<option
|
|
||||||
key={selectOption.title}
|
|
||||||
value={`${selectOption.title}`}
|
|
||||||
>
|
|
||||||
{selectOption.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.type === 'enum' ? (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className="text-gray-500 font-bold">
|
|
||||||
Value
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name="filterValue"
|
|
||||||
id='filterValue'
|
|
||||||
component="select"
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
<option value="">Select Value</option>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.options?.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
) : filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.number ? (
|
|
||||||
<div className="flex flex-row w-full mr-3">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">From</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className=" text-gray-500 font-bold">To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title ===
|
|
||||||
filterItem?.fields?.selectedField
|
|
||||||
)?.date ? (
|
|
||||||
<div className='flex flex-row w-full mr-3'>
|
|
||||||
<div className='flex flex-col w-full mr-3'>
|
|
||||||
<div className=' text-gray-500 font-bold'>
|
|
||||||
From
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col w-full'>
|
|
||||||
<div className=' text-gray-500 font-bold'>To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Contains</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValue'
|
|
||||||
placeholder='Contained'
|
|
||||||
id='filterValue'
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className=" text-gray-500 font-bold">Action</div>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
type='reset'
|
|
||||||
color='danger'
|
|
||||||
label='Delete'
|
|
||||||
onClick={() => {
|
|
||||||
deleteFilter(filterItem.id)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="flex">
|
|
||||||
<BaseButton
|
|
||||||
className="my-2 mr-3"
|
|
||||||
color="success"
|
|
||||||
label='Apply'
|
|
||||||
onClick={handleSubmit}
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
color='info'
|
|
||||||
label='Cancel'
|
|
||||||
onClick={handleReset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</CardBox> : null
|
|
||||||
}
|
|
||||||
<CardBoxModal
|
|
||||||
title="Please confirm"
|
|
||||||
buttonColor="info"
|
|
||||||
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
|
||||||
isActive={isModalTrashActive}
|
|
||||||
onConfirm={handleDeleteAction}
|
|
||||||
onCancel={handleModalAction}
|
|
||||||
>
|
|
||||||
<p>Are you sure you want to delete this item?</p>
|
|
||||||
</CardBoxModal>
|
|
||||||
|
|
||||||
|
|
||||||
{announcements && Array.isArray(announcements) && !showGrid && (
|
|
||||||
<ListAnnouncements
|
|
||||||
announcements={announcements}
|
|
||||||
loading={loading}
|
|
||||||
onDelete={handleDeleteModalAction}
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
onPageChange={onPageChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{showGrid && dataGrid}
|
|
||||||
|
|
||||||
|
|
||||||
{selectedRows.length > 0 &&
|
|
||||||
createPortal(
|
|
||||||
<BaseButton
|
|
||||||
className='me-4'
|
|
||||||
color='danger'
|
|
||||||
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
|
||||||
onClick={() => onDeleteRows(selectedRows)}
|
|
||||||
/>,
|
|
||||||
document.getElementById('delete-rows-button'),
|
|
||||||
)}
|
|
||||||
<ToastContainer />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TableSampleAnnouncements
|
|
||||||
@ -39,7 +39,7 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props
|
|||||||
>
|
>
|
||||||
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0">
|
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0">
|
||||||
|
|
||||||
<b className="font-black">CourseFlow LMS</b>
|
<b className="font-black">Store Operations Manager</b>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
course_categories: any[];
|
categories: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -20,8 +20,8 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardCourse_categories = ({
|
const CardCategories = ({
|
||||||
course_categories,
|
categories,
|
||||||
loading,
|
loading,
|
||||||
onDelete,
|
onDelete,
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -37,7 +37,7 @@ const CardCourse_categories = ({
|
|||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_COURSE_CATEGORIES')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_CATEGORIES')
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,7 +47,7 @@ const CardCourse_categories = ({
|
|||||||
role='list'
|
role='list'
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
>
|
>
|
||||||
{!loading && course_categories.map((item, index) => (
|
{!loading && categories.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
@ -57,7 +57,7 @@ const CardCourse_categories = ({
|
|||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
<Link href={`/course_categories/course_categories-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
<Link href={`/categories/categories-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ const CardCourse_categories = ({
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/course_categories/course_categories-edit/?id=${item.id}`}
|
pathEdit={`/categories/categories-edit/?id=${item.id}`}
|
||||||
pathView={`/course_categories/course_categories-view/?id=${item.id}`}
|
pathView={`/categories/categories-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -89,18 +89,6 @@ const CardCourse_categories = ({
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Slug</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.slug }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Description</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Description</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
@ -112,10 +100,22 @@ const CardCourse_categories = ({
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>ParentCategory</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.categoriesOneListFormatter(item.parent) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{!loading && course_categories.length === 0 && (
|
{!loading && categories.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -132,4 +132,4 @@ const CardCourse_categories = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardCourse_categories;
|
export default CardCategories;
|
||||||
@ -13,7 +13,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
course_categories: any[];
|
categories: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -21,10 +21,10 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListCourse_categories = ({ course_categories, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
const ListCategories = ({ categories, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_COURSE_CATEGORIES')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_CATEGORIES')
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
||||||
@ -34,13 +34,13 @@ const ListCourse_categories = ({ course_categories, loading, onDelete, currentPa
|
|||||||
<>
|
<>
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
{loading && <LoadingSpinner />}
|
{loading && <LoadingSpinner />}
|
||||||
{!loading && course_categories.map((item) => (
|
{!loading && categories.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/course_categories/course_categories-view/?id=${item.id}`}
|
href={`/categories/categories-view/?id=${item.id}`}
|
||||||
className={
|
className={
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
}
|
}
|
||||||
@ -56,16 +56,16 @@ const ListCourse_categories = ({ course_categories, loading, onDelete, currentPa
|
|||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Slug</p>
|
<p className={'text-xs text-gray-500 '}>Description</p>
|
||||||
<p className={'line-clamp-2'}>{ item.slug }</p>
|
<p className={'line-clamp-2'}>{ item.description }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Description</p>
|
<p className={'text-xs text-gray-500 '}>ParentCategory</p>
|
||||||
<p className={'line-clamp-2'}>{ item.description }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.categoriesOneListFormatter(item.parent) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -74,8 +74,8 @@ const ListCourse_categories = ({ course_categories, loading, onDelete, currentPa
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/course_categories/course_categories-edit/?id=${item.id}`}
|
pathEdit={`/categories/categories-edit/?id=${item.id}`}
|
||||||
pathView={`/course_categories/course_categories-view/?id=${item.id}`}
|
pathView={`/categories/categories-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ const ListCourse_categories = ({ course_categories, loading, onDelete, currentPa
|
|||||||
</CardBox>
|
</CardBox>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!loading && course_categories.length === 0 && (
|
{!loading && categories.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -101,4 +101,4 @@ const ListCourse_categories = ({ course_categories, loading, onDelete, currentPa
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListCourse_categories
|
export default ListCategories
|
||||||
@ -4,7 +4,7 @@ import { ToastContainer, toast } from 'react-toastify';
|
|||||||
import BaseButton from '../BaseButton'
|
import BaseButton from '../BaseButton'
|
||||||
import CardBoxModal from '../CardBoxModal'
|
import CardBoxModal from '../CardBoxModal'
|
||||||
import CardBox from "../CardBox";
|
import CardBox from "../CardBox";
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/courses/coursesSlice'
|
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/categories/categoriesSlice'
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Field, Form, Formik } from "formik";
|
import { Field, Form, Formik } from "formik";
|
||||||
@ -12,18 +12,18 @@ import {
|
|||||||
DataGrid,
|
DataGrid,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import {loadColumns} from "./configureCoursesCols";
|
import {loadColumns} from "./configureCategoriesCols";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
|
|
||||||
|
|
||||||
import CardCourses from './CardCourses';
|
import ListCategories from './ListCategories';
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
const perPage = 10
|
||||||
|
|
||||||
const TableSampleCourses = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
const TableSampleCategories = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -42,7 +42,7 @@ const TableSampleCourses = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { courses, loading, count, notify: coursesNotify, refetch } = useAppSelector((state) => state.courses)
|
const { categories, loading, count, notify: categoriesNotify, refetch } = useAppSelector((state) => state.categories)
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
@ -62,10 +62,10 @@ const TableSampleCourses = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (coursesNotify.showNotification) {
|
if (categoriesNotify.showNotification) {
|
||||||
notify(coursesNotify.typeNotification, coursesNotify.textNotification);
|
notify(categoriesNotify.typeNotification, categoriesNotify.textNotification);
|
||||||
}
|
}
|
||||||
}, [coursesNotify.showNotification]);
|
}, [categoriesNotify.showNotification]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
@ -179,7 +179,7 @@ const TableSampleCourses = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
|
|
||||||
loadColumns(
|
loadColumns(
|
||||||
handleDeleteModalAction,
|
handleDeleteModalAction,
|
||||||
`courses`,
|
`categories`,
|
||||||
currentUser,
|
currentUser,
|
||||||
).then((newCols) => setColumns(newCols));
|
).then((newCols) => setColumns(newCols));
|
||||||
}, [currentUser]);
|
}, [currentUser]);
|
||||||
@ -217,7 +217,7 @@ const TableSampleCourses = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
sx={dataGridStyles}
|
sx={dataGridStyles}
|
||||||
className={'datagrid--table'}
|
className={'datagrid--table'}
|
||||||
getRowClassName={() => `datagrid--row`}
|
getRowClassName={() => `datagrid--row`}
|
||||||
rows={courses ?? []}
|
rows={categories ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -442,9 +442,9 @@ const TableSampleCourses = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
</CardBoxModal>
|
</CardBoxModal>
|
||||||
|
|
||||||
|
|
||||||
{courses && Array.isArray(courses) && !showGrid && (
|
{categories && Array.isArray(categories) && !showGrid && (
|
||||||
<CardCourses
|
<ListCategories
|
||||||
courses={courses}
|
categories={categories}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onDelete={handleDeleteModalAction}
|
onDelete={handleDeleteModalAction}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
@ -473,4 +473,4 @@ const TableSampleCourses = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TableSampleCourses
|
export default TableSampleCategories
|
||||||
@ -37,7 +37,7 @@ export const loadColumns = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_COURSE_CATEGORIES')
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_CATEGORIES')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
@ -54,21 +54,6 @@ export const loadColumns = async (
|
|||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'slug',
|
|
||||||
headerName: 'Slug',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -86,6 +71,28 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'parent',
|
||||||
|
headerName: 'ParentCategory',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
sortable: false,
|
||||||
|
type: 'singleSelect',
|
||||||
|
getOptionValue: (value: any) => value?.id,
|
||||||
|
getOptionLabel: (value: any) => value?.label,
|
||||||
|
valueOptions: await callOptionsApi('categories'),
|
||||||
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
|
params?.value?.id ?? params?.value,
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
@ -99,8 +106,8 @@ export const loadColumns = async (
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={params?.row?.id}
|
itemId={params?.row?.id}
|
||||||
pathEdit={`/course_categories/course_categories-edit/?id=${params?.row?.id}`}
|
pathEdit={`/categories/categories-edit/?id=${params?.row?.id}`}
|
||||||
pathView={`/course_categories/course_categories-view/?id=${params?.row?.id}`}
|
pathView={`/categories/categories-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -1,166 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
import { useAppSelector } from '../../stores/hooks';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import { Pagination } from '../Pagination';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
certificates: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CardCertificates = ({
|
|
||||||
certificates,
|
|
||||||
loading,
|
|
||||||
onDelete,
|
|
||||||
currentPage,
|
|
||||||
numPages,
|
|
||||||
onPageChange,
|
|
||||||
}: Props) => {
|
|
||||||
const asideScrollbarsStyle = useAppSelector(
|
|
||||||
(state) => state.style.asideScrollbarsStyle,
|
|
||||||
);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_CERTIFICATES')
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'p-4'}>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
<ul
|
|
||||||
role='list'
|
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
|
||||||
>
|
|
||||||
{!loading && certificates.map((item, index) => (
|
|
||||||
<li
|
|
||||||
key={item.id}
|
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
|
||||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
|
||||||
|
|
||||||
<Link href={`/certificates/certificates-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
|
||||||
{item.serial}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='ml-auto '>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/certificates/certificates-edit/?id=${item.id}`}
|
|
||||||
pathView={`/certificates/certificates-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Serial</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.serial }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Student</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ dataFormatter.usersOneListFormatter(item.student) }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Course</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ dataFormatter.coursesOneListFormatter(item.course) }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>IssuedAt</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ dataFormatter.dateTimeFormatter(item.issued_at) }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>File</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium'>
|
|
||||||
{dataFormatter.filesFormatter(item.file).map(link => (
|
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{!loading && certificates.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardCertificates;
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import CardBox from '../CardBox';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import ListActionsPopover from "../ListActionsPopover";
|
|
||||||
import {useAppSelector} from "../../stores/hooks";
|
|
||||||
import {Pagination} from "../Pagination";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
certificates: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ListCertificates = ({ certificates, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_CERTIFICATES')
|
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
{!loading && certificates.map((item) => (
|
|
||||||
<div key={item.id}>
|
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href={`/certificates/certificates-view/?id=${item.id}`}
|
|
||||||
className={
|
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Serial</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.serial }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Student</p>
|
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.usersOneListFormatter(item.student) }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Course</p>
|
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.coursesOneListFormatter(item.course) }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>IssuedAt</p>
|
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.issued_at) }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>File</p>
|
|
||||||
{dataFormatter.filesFormatter(item.file).map(link => (
|
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Link>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/certificates/certificates-edit/?id=${item.id}`}
|
|
||||||
pathView={`/certificates/certificates-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{!loading && certificates.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListCertificates
|
|
||||||
@ -1,171 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
|
|
||||||
import axios from 'axios';
|
|
||||||
import {
|
|
||||||
GridActionsCellItem,
|
|
||||||
GridRowParams,
|
|
||||||
GridValueGetterParams,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import DataGridMultiSelect from "../DataGridMultiSelect";
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
type Params = (id: string) => void;
|
|
||||||
|
|
||||||
export const loadColumns = async (
|
|
||||||
onDelete: Params,
|
|
||||||
entityName: string,
|
|
||||||
|
|
||||||
user
|
|
||||||
|
|
||||||
) => {
|
|
||||||
async function callOptionsApi(entityName: string) {
|
|
||||||
|
|
||||||
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
|
||||||
return data.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_CERTIFICATES')
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'serial',
|
|
||||||
headerName: 'Serial',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'student',
|
|
||||||
headerName: 'Student',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
sortable: false,
|
|
||||||
type: 'singleSelect',
|
|
||||||
getOptionValue: (value: any) => value?.id,
|
|
||||||
getOptionLabel: (value: any) => value?.label,
|
|
||||||
valueOptions: await callOptionsApi('users'),
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
params?.value?.id ?? params?.value,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'course',
|
|
||||||
headerName: 'Course',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
sortable: false,
|
|
||||||
type: 'singleSelect',
|
|
||||||
getOptionValue: (value: any) => value?.id,
|
|
||||||
getOptionLabel: (value: any) => value?.label,
|
|
||||||
valueOptions: await callOptionsApi('courses'),
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
params?.value?.id ?? params?.value,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'issued_at',
|
|
||||||
headerName: 'IssuedAt',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'dateTime',
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
new Date(params.row.issued_at),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'file',
|
|
||||||
headerName: 'File',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
editable: false,
|
|
||||||
sortable: false,
|
|
||||||
renderCell: (params: GridValueGetterParams) => (
|
|
||||||
<>
|
|
||||||
{dataFormatter.filesFormatter(params.row.file).map(link => (
|
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'actions',
|
|
||||||
type: 'actions',
|
|
||||||
minWidth: 30,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
getActions: (params: GridRowParams) => {
|
|
||||||
|
|
||||||
return [
|
|
||||||
<div key={params?.row?.id}>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={params?.row?.id}
|
|
||||||
pathEdit={`/certificates/certificates-edit/?id=${params?.row?.id}`}
|
|
||||||
pathView={`/certificates/certificates-view/?id=${params?.row?.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
@ -1,476 +0,0 @@
|
|||||||
import React, { useEffect, useState, useMemo } from 'react'
|
|
||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
|
||||||
import BaseButton from '../BaseButton'
|
|
||||||
import CardBoxModal from '../CardBoxModal'
|
|
||||||
import CardBox from "../CardBox";
|
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/course_categories/course_categoriesSlice'
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Field, Form, Formik } from "formik";
|
|
||||||
import {
|
|
||||||
DataGrid,
|
|
||||||
GridColDef,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import {loadColumns} from "./configureCourse_categoriesCols";
|
|
||||||
import _ from 'lodash';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import {dataGridStyles} from "../../styles";
|
|
||||||
|
|
||||||
|
|
||||||
import ListCourse_categories from './ListCourse_categories';
|
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
|
||||||
|
|
||||||
const TableSampleCourse_categories = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const pagesList = [];
|
|
||||||
const [id, setId] = useState(null);
|
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
|
||||||
const [filterRequest, setFilterRequest] = React.useState('');
|
|
||||||
const [columns, setColumns] = useState<GridColDef[]>([]);
|
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
|
||||||
const [sortModel, setSortModel] = useState([
|
|
||||||
{
|
|
||||||
field: '',
|
|
||||||
sort: 'desc',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { course_categories, loading, count, notify: course_categoriesNotify, refetch } = useAppSelector((state) => state.course_categories)
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
|
|
||||||
for (let i = 0; i < numPages; i++) {
|
|
||||||
pagesList.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadData = async (page = currentPage, request = filterRequest) => {
|
|
||||||
if (page !== currentPage) setCurrentPage(page);
|
|
||||||
if (request !== filterRequest) setFilterRequest(request);
|
|
||||||
const { sort, field } = sortModel[0];
|
|
||||||
|
|
||||||
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
|
|
||||||
dispatch(fetch({ limit: perPage, page, query }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (course_categoriesNotify.showNotification) {
|
|
||||||
notify(course_categoriesNotify.typeNotification, course_categoriesNotify.textNotification);
|
|
||||||
}
|
|
||||||
}, [course_categoriesNotify.showNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
loadData();
|
|
||||||
}, [sortModel, currentUser]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (refetch) {
|
|
||||||
loadData(0);
|
|
||||||
dispatch(setRefetch(false));
|
|
||||||
}
|
|
||||||
}, [refetch, dispatch]);
|
|
||||||
|
|
||||||
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
|
|
||||||
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
|
|
||||||
|
|
||||||
const handleModalAction = () => {
|
|
||||||
setIsModalInfoActive(false)
|
|
||||||
setIsModalTrashActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteModalAction = (id: string) => {
|
|
||||||
setId(id)
|
|
||||||
setIsModalTrashActive(true)
|
|
||||||
}
|
|
||||||
const handleDeleteAction = async () => {
|
|
||||||
if (id) {
|
|
||||||
await dispatch(deleteItem(id));
|
|
||||||
await loadData(0);
|
|
||||||
setIsModalTrashActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateFilterRequests = useMemo(() => {
|
|
||||||
let request = '&';
|
|
||||||
filterItems.forEach((item) => {
|
|
||||||
const isRangeFilter = filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title === item.fields.selectedField &&
|
|
||||||
(filter.number || filter.date),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRangeFilter) {
|
|
||||||
const from = item.fields.filterValueFrom;
|
|
||||||
const to = item.fields.filterValueTo;
|
|
||||||
if (from) {
|
|
||||||
request += `${item.fields.selectedField}Range=${from}&`;
|
|
||||||
}
|
|
||||||
if (to) {
|
|
||||||
request += `${item.fields.selectedField}Range=${to}&`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const value = item.fields.filterValue;
|
|
||||||
if (value) {
|
|
||||||
request += `${item.fields.selectedField}=${value}&`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return request;
|
|
||||||
}, [filterItems, filters]);
|
|
||||||
|
|
||||||
const deleteFilter = (value) => {
|
|
||||||
const newItems = filterItems.filter((item) => item.id !== value);
|
|
||||||
|
|
||||||
if (newItems.length) {
|
|
||||||
setFilterItems(newItems);
|
|
||||||
} else {
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
setFilterItems(newItems);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
loadData(0, generateFilterRequests);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (id) => (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
const name = e.target.name;
|
|
||||||
|
|
||||||
setFilterItems(
|
|
||||||
filterItems.map((item) => {
|
|
||||||
if (item.id !== id) return item;
|
|
||||||
if (name === 'selectedField') return { id, fields: { [name]: value } };
|
|
||||||
|
|
||||||
return { id, fields: { ...item.fields, [name]: value } }
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setFilterItems([]);
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
|
||||||
loadData(page);
|
|
||||||
setCurrentPage(page);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
|
|
||||||
loadColumns(
|
|
||||||
handleDeleteModalAction,
|
|
||||||
`course_categories`,
|
|
||||||
currentUser,
|
|
||||||
).then((newCols) => setColumns(newCols));
|
|
||||||
}, [currentUser]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleTableSubmit = async (id: string, data) => {
|
|
||||||
|
|
||||||
if (!_.isEmpty(data)) {
|
|
||||||
await dispatch(update({ id, data }))
|
|
||||||
.unwrap()
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteRows = async (selectedRows) => {
|
|
||||||
await dispatch(deleteItemsByIds(selectedRows));
|
|
||||||
await loadData(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
const controlClasses =
|
|
||||||
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
|
|
||||||
` ${bgColor} ${focusRing} ${corners} ` +
|
|
||||||
'dark:bg-slate-800 border';
|
|
||||||
|
|
||||||
|
|
||||||
const dataGrid = (
|
|
||||||
<div className='relative overflow-x-auto'>
|
|
||||||
<DataGrid
|
|
||||||
autoHeight
|
|
||||||
rowHeight={64}
|
|
||||||
sx={dataGridStyles}
|
|
||||||
className={'datagrid--table'}
|
|
||||||
getRowClassName={() => `datagrid--row`}
|
|
||||||
rows={course_categories ?? []}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{
|
|
||||||
pagination: {
|
|
||||||
paginationModel: {
|
|
||||||
pageSize: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
disableRowSelectionOnClick
|
|
||||||
onProcessRowUpdateError={(params) => {
|
|
||||||
console.log('Error', params);
|
|
||||||
}}
|
|
||||||
processRowUpdate={async (newRow, oldRow) => {
|
|
||||||
const data = dataFormatter.dataGridEditFormatter(newRow);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await handleTableSubmit(newRow.id, data);
|
|
||||||
return newRow;
|
|
||||||
} catch {
|
|
||||||
return oldRow;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
sortingMode={'server'}
|
|
||||||
checkboxSelection
|
|
||||||
onRowSelectionModelChange={(ids) => {
|
|
||||||
setSelectedRows(ids)
|
|
||||||
}}
|
|
||||||
onSortModelChange={(params) => {
|
|
||||||
params.length
|
|
||||||
? setSortModel(params)
|
|
||||||
: setSortModel([{ field: '', sort: 'desc' }]);
|
|
||||||
}}
|
|
||||||
rowCount={count}
|
|
||||||
pageSizeOptions={[10]}
|
|
||||||
paginationMode={'server'}
|
|
||||||
loading={loading}
|
|
||||||
onPaginationModelChange={(params) => {
|
|
||||||
onPageChange(params.page);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
|
||||||
<CardBox>
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
checkboxes: ['lorem'],
|
|
||||||
switches: ['lorem'],
|
|
||||||
radio: 'lorem',
|
|
||||||
}}
|
|
||||||
onSubmit={() => null}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<>
|
|
||||||
{filterItems && filterItems.map((filterItem) => {
|
|
||||||
return (
|
|
||||||
<div key={filterItem.id} className="flex mb-4">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Filter</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='selectedField'
|
|
||||||
id='selectedField'
|
|
||||||
component='select'
|
|
||||||
value={filterItem?.fields?.selectedField || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
{filters.map((selectOption) => (
|
|
||||||
<option
|
|
||||||
key={selectOption.title}
|
|
||||||
value={`${selectOption.title}`}
|
|
||||||
>
|
|
||||||
{selectOption.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.type === 'enum' ? (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className="text-gray-500 font-bold">
|
|
||||||
Value
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name="filterValue"
|
|
||||||
id='filterValue'
|
|
||||||
component="select"
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
<option value="">Select Value</option>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.options?.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
) : filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.number ? (
|
|
||||||
<div className="flex flex-row w-full mr-3">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">From</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className=" text-gray-500 font-bold">To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title ===
|
|
||||||
filterItem?.fields?.selectedField
|
|
||||||
)?.date ? (
|
|
||||||
<div className='flex flex-row w-full mr-3'>
|
|
||||||
<div className='flex flex-col w-full mr-3'>
|
|
||||||
<div className=' text-gray-500 font-bold'>
|
|
||||||
From
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col w-full'>
|
|
||||||
<div className=' text-gray-500 font-bold'>To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Contains</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValue'
|
|
||||||
placeholder='Contained'
|
|
||||||
id='filterValue'
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className=" text-gray-500 font-bold">Action</div>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
type='reset'
|
|
||||||
color='danger'
|
|
||||||
label='Delete'
|
|
||||||
onClick={() => {
|
|
||||||
deleteFilter(filterItem.id)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="flex">
|
|
||||||
<BaseButton
|
|
||||||
className="my-2 mr-3"
|
|
||||||
color="success"
|
|
||||||
label='Apply'
|
|
||||||
onClick={handleSubmit}
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
color='info'
|
|
||||||
label='Cancel'
|
|
||||||
onClick={handleReset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</CardBox> : null
|
|
||||||
}
|
|
||||||
<CardBoxModal
|
|
||||||
title="Please confirm"
|
|
||||||
buttonColor="info"
|
|
||||||
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
|
||||||
isActive={isModalTrashActive}
|
|
||||||
onConfirm={handleDeleteAction}
|
|
||||||
onCancel={handleModalAction}
|
|
||||||
>
|
|
||||||
<p>Are you sure you want to delete this item?</p>
|
|
||||||
</CardBoxModal>
|
|
||||||
|
|
||||||
|
|
||||||
{course_categories && Array.isArray(course_categories) && !showGrid && (
|
|
||||||
<ListCourse_categories
|
|
||||||
course_categories={course_categories}
|
|
||||||
loading={loading}
|
|
||||||
onDelete={handleDeleteModalAction}
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
onPageChange={onPageChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{showGrid && dataGrid}
|
|
||||||
|
|
||||||
|
|
||||||
{selectedRows.length > 0 &&
|
|
||||||
createPortal(
|
|
||||||
<BaseButton
|
|
||||||
className='me-4'
|
|
||||||
color='danger'
|
|
||||||
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
|
||||||
onClick={() => onDeleteRows(selectedRows)}
|
|
||||||
/>,
|
|
||||||
document.getElementById('delete-rows-button'),
|
|
||||||
)}
|
|
||||||
<ToastContainer />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TableSampleCourse_categories
|
|
||||||
@ -1,256 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
import { useAppSelector } from '../../stores/hooks';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import { Pagination } from '../Pagination';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
courses: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CardCourses = ({
|
|
||||||
courses,
|
|
||||||
loading,
|
|
||||||
onDelete,
|
|
||||||
currentPage,
|
|
||||||
numPages,
|
|
||||||
onPageChange,
|
|
||||||
}: Props) => {
|
|
||||||
const asideScrollbarsStyle = useAppSelector(
|
|
||||||
(state) => state.style.asideScrollbarsStyle,
|
|
||||||
);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_COURSES')
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'p-4'}>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
<ul
|
|
||||||
role='list'
|
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
|
||||||
>
|
|
||||||
{!loading && courses.map((item, index) => (
|
|
||||||
<li
|
|
||||||
key={item.id}
|
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
|
||||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 md:p-0 md:block gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href={`/courses/courses-view/?id=${item.id}`}
|
|
||||||
className={'cursor-pointer'}
|
|
||||||
>
|
|
||||||
<ImageField
|
|
||||||
name={'Avatar'}
|
|
||||||
image={item.thumbnail}
|
|
||||||
className='w-12 h-12 md:w-full md:h-44 rounded-lg md:rounded-b-none overflow-hidden ring-1 ring-gray-900/10'
|
|
||||||
imageClassName='h-full w-full flex-none rounded-lg md:rounded-b-none bg-white object-cover'
|
|
||||||
/>
|
|
||||||
<p className={'px-6 py-2 font-semibold'}>{item.title}</p>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='ml-auto md:absolute md:top-0 md:right-0 '>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/courses/courses-edit/?id=${item.id}`}
|
|
||||||
pathView={`/courses/courses-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Title</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.title }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>ShortDescription</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.short_description }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Description</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.description }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Instructor</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ dataFormatter.usersOneListFormatter(item.instructor) }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Category</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ dataFormatter.course_categoriesOneListFormatter(item.category) }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Level</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.level }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Language</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.language }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Price</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.price }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Published</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ dataFormatter.booleanFormatter(item.published) }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>PublishedAt</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ dataFormatter.dateTimeFormatter(item.published_at) }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Thumbnail</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium'>
|
|
||||||
<ImageField
|
|
||||||
name={'Avatar'}
|
|
||||||
image={item.thumbnail}
|
|
||||||
className='mx-auto w-8 h-8'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Duration(minutes)</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.duration }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{!loading && courses.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardCourses;
|
|
||||||
@ -1,187 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import CardBox from '../CardBox';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import ListActionsPopover from "../ListActionsPopover";
|
|
||||||
import {useAppSelector} from "../../stores/hooks";
|
|
||||||
import {Pagination} from "../Pagination";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
courses: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ListCourses = ({ courses, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_COURSES')
|
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
{!loading && courses.map((item) => (
|
|
||||||
<div key={item.id}>
|
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
|
||||||
|
|
||||||
<ImageField
|
|
||||||
name={'Avatar'}
|
|
||||||
image={item.thumbnail}
|
|
||||||
className='w-24 h-24 rounded-l overflow-hidden hidden md:block'
|
|
||||||
imageClassName={'rounded-l rounded-r-none h-full object-cover'}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href={`/courses/courses-view/?id=${item.id}`}
|
|
||||||
className={
|
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Title</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.title }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>ShortDescription</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.short_description }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Description</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.description }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Instructor</p>
|
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.usersOneListFormatter(item.instructor) }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Category</p>
|
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.course_categoriesOneListFormatter(item.category) }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Level</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.level }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Language</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.language }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Price</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.price }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Published</p>
|
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.published) }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>PublishedAt</p>
|
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.published_at) }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Thumbnail</p>
|
|
||||||
<ImageField
|
|
||||||
name={'Avatar'}
|
|
||||||
image={item.thumbnail}
|
|
||||||
className='mx-auto w-8 h-8'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Duration(minutes)</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.duration }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Link>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/courses/courses-edit/?id=${item.id}`}
|
|
||||||
pathView={`/courses/courses-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{!loading && courses.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListCourses
|
|
||||||
@ -1,274 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
|
|
||||||
import axios from 'axios';
|
|
||||||
import {
|
|
||||||
GridActionsCellItem,
|
|
||||||
GridRowParams,
|
|
||||||
GridValueGetterParams,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import DataGridMultiSelect from "../DataGridMultiSelect";
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
type Params = (id: string) => void;
|
|
||||||
|
|
||||||
export const loadColumns = async (
|
|
||||||
onDelete: Params,
|
|
||||||
entityName: string,
|
|
||||||
|
|
||||||
user
|
|
||||||
|
|
||||||
) => {
|
|
||||||
async function callOptionsApi(entityName: string) {
|
|
||||||
|
|
||||||
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
|
||||||
return data.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_COURSES')
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'title',
|
|
||||||
headerName: 'Title',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'short_description',
|
|
||||||
headerName: 'ShortDescription',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'description',
|
|
||||||
headerName: 'Description',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'instructor',
|
|
||||||
headerName: 'Instructor',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
sortable: false,
|
|
||||||
type: 'singleSelect',
|
|
||||||
getOptionValue: (value: any) => value?.id,
|
|
||||||
getOptionLabel: (value: any) => value?.label,
|
|
||||||
valueOptions: await callOptionsApi('users'),
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
params?.value?.id ?? params?.value,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'category',
|
|
||||||
headerName: 'Category',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
sortable: false,
|
|
||||||
type: 'singleSelect',
|
|
||||||
getOptionValue: (value: any) => value?.id,
|
|
||||||
getOptionLabel: (value: any) => value?.label,
|
|
||||||
valueOptions: await callOptionsApi('course_categories'),
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
params?.value?.id ?? params?.value,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'level',
|
|
||||||
headerName: 'Level',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'language',
|
|
||||||
headerName: 'Language',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'price',
|
|
||||||
headerName: 'Price',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'published',
|
|
||||||
headerName: 'Published',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'boolean',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'published_at',
|
|
||||||
headerName: 'PublishedAt',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'dateTime',
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
new Date(params.row.published_at),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'thumbnail',
|
|
||||||
headerName: 'Thumbnail',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
editable: false,
|
|
||||||
sortable: false,
|
|
||||||
renderCell: (params: GridValueGetterParams) => (
|
|
||||||
<ImageField
|
|
||||||
name={'Avatar'}
|
|
||||||
image={params?.row?.thumbnail}
|
|
||||||
className='w-24 h-24 mx-auto lg:w-6 lg:h-6'
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'duration',
|
|
||||||
headerName: 'Duration(minutes)',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'actions',
|
|
||||||
type: 'actions',
|
|
||||||
minWidth: 30,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
getActions: (params: GridRowParams) => {
|
|
||||||
|
|
||||||
return [
|
|
||||||
<div key={params?.row?.id}>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={params?.row?.id}
|
|
||||||
pathEdit={`/courses/courses-edit/?id=${params?.row?.id}`}
|
|
||||||
pathView={`/courses/courses-view/?id=${params?.row?.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
@ -12,7 +12,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
progress: any[];
|
customers: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -20,8 +20,8 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardProgress = ({
|
const CardCustomers = ({
|
||||||
progress,
|
customers,
|
||||||
loading,
|
loading,
|
||||||
onDelete,
|
onDelete,
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -37,7 +37,7 @@ const CardProgress = ({
|
|||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PROGRESS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_CUSTOMERS')
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,7 +47,7 @@ const CardProgress = ({
|
|||||||
role='list'
|
role='list'
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
>
|
>
|
||||||
{!loading && progress.map((item, index) => (
|
{!loading && customers.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
@ -57,8 +57,8 @@ const CardProgress = ({
|
|||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
<Link href={`/progress/progress-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
<Link href={`/customers/customers-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
{item.summary}
|
{item.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ const CardProgress = ({
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/progress/progress-edit/?id=${item.id}`}
|
pathEdit={`/customers/customers-edit/?id=${item.id}`}
|
||||||
pathView={`/progress/progress-view/?id=${item.id}`}
|
pathView={`/customers/customers-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -78,10 +78,10 @@ const CardProgress = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Summary</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Name</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.summary }
|
{ item.name }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -90,10 +90,10 @@ const CardProgress = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Student</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Email</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.usersOneListFormatter(item.student) }
|
{ item.email }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -102,10 +102,10 @@ const CardProgress = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Lesson</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Phone</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.lessonsOneListFormatter(item.lesson) }
|
{ item.phone }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -114,34 +114,10 @@ const CardProgress = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Completed</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Address</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.booleanFormatter(item.completed) }
|
{ item.address }
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>CompletedAt</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ dataFormatter.dateTimeFormatter(item.completed_at) }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Percent</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.percent }
|
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -160,10 +136,34 @@ const CardProgress = ({
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>VIP</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.booleanFormatter(item.vip) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>TaxNumber</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.tax_number }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{!loading && progress.length === 0 && (
|
{!loading && customers.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -180,4 +180,4 @@ const CardProgress = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardProgress;
|
export default CardCustomers;
|
||||||
@ -13,7 +13,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
progress: any[];
|
customers: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -21,10 +21,10 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListProgress = ({ progress, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
const ListCustomers = ({ customers, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PROGRESS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_CUSTOMERS')
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
||||||
@ -34,13 +34,13 @@ const ListProgress = ({ progress, loading, onDelete, currentPage, numPages, onPa
|
|||||||
<>
|
<>
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
{loading && <LoadingSpinner />}
|
{loading && <LoadingSpinner />}
|
||||||
{!loading && progress.map((item) => (
|
{!loading && customers.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/progress/progress-view/?id=${item.id}`}
|
href={`/customers/customers-view/?id=${item.id}`}
|
||||||
className={
|
className={
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
}
|
}
|
||||||
@ -48,48 +48,32 @@ const ListProgress = ({ progress, loading, onDelete, currentPage, numPages, onPa
|
|||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Summary</p>
|
<p className={'text-xs text-gray-500 '}>Name</p>
|
||||||
<p className={'line-clamp-2'}>{ item.summary }</p>
|
<p className={'line-clamp-2'}>{ item.name }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Student</p>
|
<p className={'text-xs text-gray-500 '}>Email</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.usersOneListFormatter(item.student) }</p>
|
<p className={'line-clamp-2'}>{ item.email }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Lesson</p>
|
<p className={'text-xs text-gray-500 '}>Phone</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.lessonsOneListFormatter(item.lesson) }</p>
|
<p className={'line-clamp-2'}>{ item.phone }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Completed</p>
|
<p className={'text-xs text-gray-500 '}>Address</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.completed) }</p>
|
<p className={'line-clamp-2'}>{ item.address }</p>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>CompletedAt</p>
|
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.completed_at) }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Percent</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.percent }</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -102,12 +86,28 @@ const ListProgress = ({ progress, loading, onDelete, currentPage, numPages, onPa
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>VIP</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.vip) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>TaxNumber</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.tax_number }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/progress/progress-edit/?id=${item.id}`}
|
pathEdit={`/customers/customers-edit/?id=${item.id}`}
|
||||||
pathView={`/progress/progress-view/?id=${item.id}`}
|
pathView={`/customers/customers-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ const ListProgress = ({ progress, loading, onDelete, currentPage, numPages, onPa
|
|||||||
</CardBox>
|
</CardBox>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!loading && progress.length === 0 && (
|
{!loading && customers.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -133,4 +133,4 @@ const ListProgress = ({ progress, loading, onDelete, currentPage, numPages, onPa
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListProgress
|
export default ListCustomers
|
||||||
@ -4,7 +4,7 @@ import { ToastContainer, toast } from 'react-toastify';
|
|||||||
import BaseButton from '../BaseButton'
|
import BaseButton from '../BaseButton'
|
||||||
import CardBoxModal from '../CardBoxModal'
|
import CardBoxModal from '../CardBoxModal'
|
||||||
import CardBox from "../CardBox";
|
import CardBox from "../CardBox";
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/certificates/certificatesSlice'
|
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/customers/customersSlice'
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Field, Form, Formik } from "formik";
|
import { Field, Form, Formik } from "formik";
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
DataGrid,
|
DataGrid,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import {loadColumns} from "./configureCertificatesCols";
|
import {loadColumns} from "./configureCustomersCols";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
@ -21,7 +21,7 @@ import {dataGridStyles} from "../../styles";
|
|||||||
|
|
||||||
const perPage = 10
|
const perPage = 10
|
||||||
|
|
||||||
const TableSampleCertificates = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
const TableSampleCustomers = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -40,7 +40,7 @@ const TableSampleCertificates = ({ filterItems, setFilterItems, filters, showGri
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { certificates, loading, count, notify: certificatesNotify, refetch } = useAppSelector((state) => state.certificates)
|
const { customers, loading, count, notify: customersNotify, refetch } = useAppSelector((state) => state.customers)
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
@ -60,10 +60,10 @@ const TableSampleCertificates = ({ filterItems, setFilterItems, filters, showGri
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (certificatesNotify.showNotification) {
|
if (customersNotify.showNotification) {
|
||||||
notify(certificatesNotify.typeNotification, certificatesNotify.textNotification);
|
notify(customersNotify.typeNotification, customersNotify.textNotification);
|
||||||
}
|
}
|
||||||
}, [certificatesNotify.showNotification]);
|
}, [customersNotify.showNotification]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
@ -177,7 +177,7 @@ const TableSampleCertificates = ({ filterItems, setFilterItems, filters, showGri
|
|||||||
|
|
||||||
loadColumns(
|
loadColumns(
|
||||||
handleDeleteModalAction,
|
handleDeleteModalAction,
|
||||||
`certificates`,
|
`customers`,
|
||||||
currentUser,
|
currentUser,
|
||||||
).then((newCols) => setColumns(newCols));
|
).then((newCols) => setColumns(newCols));
|
||||||
}, [currentUser]);
|
}, [currentUser]);
|
||||||
@ -215,7 +215,7 @@ const TableSampleCertificates = ({ filterItems, setFilterItems, filters, showGri
|
|||||||
sx={dataGridStyles}
|
sx={dataGridStyles}
|
||||||
className={'datagrid--table'}
|
className={'datagrid--table'}
|
||||||
getRowClassName={() => `datagrid--row`}
|
getRowClassName={() => `datagrid--row`}
|
||||||
rows={certificates ?? []}
|
rows={customers ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -460,4 +460,4 @@ const TableSampleCertificates = ({ filterItems, setFilterItems, filters, showGri
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TableSampleCertificates
|
export default TableSampleCustomers
|
||||||
@ -37,35 +37,13 @@ export const loadColumns = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_QUIZ_QUESTIONS')
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_CUSTOMERS')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'quiz',
|
field: 'name',
|
||||||
headerName: 'Quiz',
|
headerName: 'Name',
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
sortable: false,
|
|
||||||
type: 'singleSelect',
|
|
||||||
getOptionValue: (value: any) => value?.id,
|
|
||||||
getOptionLabel: (value: any) => value?.label,
|
|
||||||
valueOptions: await callOptionsApi('quizzes'),
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
params?.value?.id ?? params?.value,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'question',
|
|
||||||
headerName: 'Question',
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -79,8 +57,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'question_type',
|
field: 'email',
|
||||||
headerName: 'QuestionType',
|
headerName: 'Email',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -94,8 +72,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'choices',
|
field: 'phone',
|
||||||
headerName: 'Choices',
|
headerName: 'Phone',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -109,8 +87,54 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'answer',
|
field: 'address',
|
||||||
headerName: 'Answer',
|
headerName: 'Address',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'notes',
|
||||||
|
headerName: 'Notes',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'vip',
|
||||||
|
headerName: 'VIP',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'boolean',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'tax_number',
|
||||||
|
headerName: 'TaxNumber',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -136,8 +160,8 @@ export const loadColumns = async (
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={params?.row?.id}
|
itemId={params?.row?.id}
|
||||||
pathEdit={`/quiz_questions/quiz_questions-edit/?id=${params?.row?.id}`}
|
pathEdit={`/customers/customers-edit/?id=${params?.row?.id}`}
|
||||||
pathView={`/quiz_questions/quiz_questions-view/?id=${params?.row?.id}`}
|
pathView={`/customers/customers-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
quiz_questions: any[];
|
order_items: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -20,8 +20,8 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardQuiz_questions = ({
|
const CardOrder_items = ({
|
||||||
quiz_questions,
|
order_items,
|
||||||
loading,
|
loading,
|
||||||
onDelete,
|
onDelete,
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -37,7 +37,7 @@ const CardQuiz_questions = ({
|
|||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_QUIZ_QUESTIONS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ORDER_ITEMS')
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,7 +47,7 @@ const CardQuiz_questions = ({
|
|||||||
role='list'
|
role='list'
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
>
|
>
|
||||||
{!loading && quiz_questions.map((item, index) => (
|
{!loading && order_items.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
@ -57,8 +57,8 @@ const CardQuiz_questions = ({
|
|||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
<Link href={`/quiz_questions/quiz_questions-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
<Link href={`/order_items/order_items-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
{item.question}
|
{item.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ const CardQuiz_questions = ({
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/quiz_questions/quiz_questions-edit/?id=${item.id}`}
|
pathEdit={`/order_items/order_items-edit/?id=${item.id}`}
|
||||||
pathView={`/quiz_questions/quiz_questions-view/?id=${item.id}`}
|
pathView={`/order_items/order_items-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -78,10 +78,10 @@ const CardQuiz_questions = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Quiz</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Name</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.quizzesOneListFormatter(item.quiz) }
|
{ item.name }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -90,10 +90,10 @@ const CardQuiz_questions = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Question</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Order</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.question }
|
{ dataFormatter.ordersOneListFormatter(item.order) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -102,10 +102,10 @@ const CardQuiz_questions = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>QuestionType</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Product</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.question_type }
|
{ dataFormatter.productsOneListFormatter(item.product) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -114,10 +114,10 @@ const CardQuiz_questions = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Choices</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Quantity</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.choices }
|
{ item.quantity }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -126,10 +126,22 @@ const CardQuiz_questions = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Answer</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>UnitPrice</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.answer }
|
{ item.unit_price }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>TotalPrice</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.total_price }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -139,7 +151,7 @@ const CardQuiz_questions = ({
|
|||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{!loading && quiz_questions.length === 0 && (
|
{!loading && order_items.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -156,4 +168,4 @@ const CardQuiz_questions = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardQuiz_questions;
|
export default CardOrder_items;
|
||||||
@ -13,7 +13,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
announcements: any[];
|
order_items: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -21,10 +21,10 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListAnnouncements = ({ announcements, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
const ListOrder_items = ({ order_items, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ANNOUNCEMENTS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ORDER_ITEMS')
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
||||||
@ -34,13 +34,13 @@ const ListAnnouncements = ({ announcements, loading, onDelete, currentPage, numP
|
|||||||
<>
|
<>
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
{loading && <LoadingSpinner />}
|
{loading && <LoadingSpinner />}
|
||||||
{!loading && announcements.map((item) => (
|
{!loading && order_items.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/announcements/announcements-view/?id=${item.id}`}
|
href={`/order_items/order_items-view/?id=${item.id}`}
|
||||||
className={
|
className={
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
}
|
}
|
||||||
@ -48,40 +48,48 @@ const ListAnnouncements = ({ announcements, loading, onDelete, currentPage, numP
|
|||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Course</p>
|
<p className={'text-xs text-gray-500 '}>Name</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.coursesOneListFormatter(item.course) }</p>
|
<p className={'line-clamp-2'}>{ item.name }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Title</p>
|
<p className={'text-xs text-gray-500 '}>Order</p>
|
||||||
<p className={'line-clamp-2'}>{ item.title }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.ordersOneListFormatter(item.order) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Content</p>
|
<p className={'text-xs text-gray-500 '}>Product</p>
|
||||||
<p className={'line-clamp-2'}>{ item.content }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.productsOneListFormatter(item.product) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>PublishedAt</p>
|
<p className={'text-xs text-gray-500 '}>Quantity</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.published_at) }</p>
|
<p className={'line-clamp-2'}>{ item.quantity }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Active</p>
|
<p className={'text-xs text-gray-500 '}>UnitPrice</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.is_active) }</p>
|
<p className={'line-clamp-2'}>{ item.unit_price }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>TotalPrice</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.total_price }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -90,8 +98,8 @@ const ListAnnouncements = ({ announcements, loading, onDelete, currentPage, numP
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/announcements/announcements-edit/?id=${item.id}`}
|
pathEdit={`/order_items/order_items-edit/?id=${item.id}`}
|
||||||
pathView={`/announcements/announcements-view/?id=${item.id}`}
|
pathView={`/order_items/order_items-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -100,7 +108,7 @@ const ListAnnouncements = ({ announcements, loading, onDelete, currentPage, numP
|
|||||||
</CardBox>
|
</CardBox>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!loading && announcements.length === 0 && (
|
{!loading && order_items.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -117,4 +125,4 @@ const ListAnnouncements = ({ announcements, loading, onDelete, currentPage, numP
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListAnnouncements
|
export default ListOrder_items
|
||||||
@ -4,7 +4,7 @@ import { ToastContainer, toast } from 'react-toastify';
|
|||||||
import BaseButton from '../BaseButton'
|
import BaseButton from '../BaseButton'
|
||||||
import CardBoxModal from '../CardBoxModal'
|
import CardBoxModal from '../CardBoxModal'
|
||||||
import CardBox from "../CardBox";
|
import CardBox from "../CardBox";
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/quiz_questions/quiz_questionsSlice'
|
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/order_items/order_itemsSlice'
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Field, Form, Formik } from "formik";
|
import { Field, Form, Formik } from "formik";
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
DataGrid,
|
DataGrid,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import {loadColumns} from "./configureQuiz_questionsCols";
|
import {loadColumns} from "./configureOrder_itemsCols";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
@ -21,7 +21,7 @@ import {dataGridStyles} from "../../styles";
|
|||||||
|
|
||||||
const perPage = 10
|
const perPage = 10
|
||||||
|
|
||||||
const TableSampleQuiz_questions = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
const TableSampleOrder_items = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -40,7 +40,7 @@ const TableSampleQuiz_questions = ({ filterItems, setFilterItems, filters, showG
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { quiz_questions, loading, count, notify: quiz_questionsNotify, refetch } = useAppSelector((state) => state.quiz_questions)
|
const { order_items, loading, count, notify: order_itemsNotify, refetch } = useAppSelector((state) => state.order_items)
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
@ -60,10 +60,10 @@ const TableSampleQuiz_questions = ({ filterItems, setFilterItems, filters, showG
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (quiz_questionsNotify.showNotification) {
|
if (order_itemsNotify.showNotification) {
|
||||||
notify(quiz_questionsNotify.typeNotification, quiz_questionsNotify.textNotification);
|
notify(order_itemsNotify.typeNotification, order_itemsNotify.textNotification);
|
||||||
}
|
}
|
||||||
}, [quiz_questionsNotify.showNotification]);
|
}, [order_itemsNotify.showNotification]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
@ -177,7 +177,7 @@ const TableSampleQuiz_questions = ({ filterItems, setFilterItems, filters, showG
|
|||||||
|
|
||||||
loadColumns(
|
loadColumns(
|
||||||
handleDeleteModalAction,
|
handleDeleteModalAction,
|
||||||
`quiz_questions`,
|
`order_items`,
|
||||||
currentUser,
|
currentUser,
|
||||||
).then((newCols) => setColumns(newCols));
|
).then((newCols) => setColumns(newCols));
|
||||||
}, [currentUser]);
|
}, [currentUser]);
|
||||||
@ -215,7 +215,7 @@ const TableSampleQuiz_questions = ({ filterItems, setFilterItems, filters, showG
|
|||||||
sx={dataGridStyles}
|
sx={dataGridStyles}
|
||||||
className={'datagrid--table'}
|
className={'datagrid--table'}
|
||||||
getRowClassName={() => `datagrid--row`}
|
getRowClassName={() => `datagrid--row`}
|
||||||
rows={quiz_questions ?? []}
|
rows={order_items ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -460,4 +460,4 @@ const TableSampleQuiz_questions = ({ filterItems, setFilterItems, filters, showG
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TableSampleQuiz_questions
|
export default TableSampleOrder_items
|
||||||
@ -37,13 +37,13 @@ export const loadColumns = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_PROGRESS')
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_ORDER_ITEMS')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'summary',
|
field: 'name',
|
||||||
headerName: 'Summary',
|
headerName: 'Name',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -57,8 +57,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'student',
|
field: 'order',
|
||||||
headerName: 'Student',
|
headerName: 'Order',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -72,15 +72,15 @@ export const loadColumns = async (
|
|||||||
type: 'singleSelect',
|
type: 'singleSelect',
|
||||||
getOptionValue: (value: any) => value?.id,
|
getOptionValue: (value: any) => value?.id,
|
||||||
getOptionLabel: (value: any) => value?.label,
|
getOptionLabel: (value: any) => value?.label,
|
||||||
valueOptions: await callOptionsApi('users'),
|
valueOptions: await callOptionsApi('orders'),
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
params?.value?.id ?? params?.value,
|
params?.value?.id ?? params?.value,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'lesson',
|
field: 'product',
|
||||||
headerName: 'Lesson',
|
headerName: 'Product',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -94,49 +94,15 @@ export const loadColumns = async (
|
|||||||
type: 'singleSelect',
|
type: 'singleSelect',
|
||||||
getOptionValue: (value: any) => value?.id,
|
getOptionValue: (value: any) => value?.id,
|
||||||
getOptionLabel: (value: any) => value?.label,
|
getOptionLabel: (value: any) => value?.label,
|
||||||
valueOptions: await callOptionsApi('lessons'),
|
valueOptions: await callOptionsApi('products'),
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
params?.value?.id ?? params?.value,
|
params?.value?.id ?? params?.value,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'completed',
|
field: 'quantity',
|
||||||
headerName: 'Completed',
|
headerName: 'Quantity',
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'boolean',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'completed_at',
|
|
||||||
headerName: 'CompletedAt',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'dateTime',
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
new Date(params.row.completed_at),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'percent',
|
|
||||||
headerName: 'Percent',
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -151,8 +117,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'notes',
|
field: 'unit_price',
|
||||||
headerName: 'Notes',
|
headerName: 'UnitPrice',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -162,7 +128,24 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'number',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'total_price',
|
||||||
|
headerName: 'TotalPrice',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'number',
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -178,8 +161,8 @@ export const loadColumns = async (
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={params?.row?.id}
|
itemId={params?.row?.id}
|
||||||
pathEdit={`/progress/progress-edit/?id=${params?.row?.id}`}
|
pathEdit={`/order_items/order_items-edit/?id=${params?.row?.id}`}
|
||||||
pathView={`/progress/progress-view/?id=${params?.row?.id}`}
|
pathView={`/order_items/order_items-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
lessons: any[];
|
orders: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -20,8 +20,8 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardLessons = ({
|
const CardOrders = ({
|
||||||
lessons,
|
orders,
|
||||||
loading,
|
loading,
|
||||||
onDelete,
|
onDelete,
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -37,7 +37,7 @@ const CardLessons = ({
|
|||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_LESSONS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ORDERS')
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,7 +47,7 @@ const CardLessons = ({
|
|||||||
role='list'
|
role='list'
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
>
|
>
|
||||||
{!loading && lessons.map((item, index) => (
|
{!loading && orders.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
@ -57,8 +57,8 @@ const CardLessons = ({
|
|||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
<Link href={`/lessons/lessons-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
<Link href={`/orders/orders-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
{item.title}
|
{item.order_number}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ const CardLessons = ({
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/lessons/lessons-edit/?id=${item.id}`}
|
pathEdit={`/orders/orders-edit/?id=${item.id}`}
|
||||||
pathView={`/lessons/lessons-view/?id=${item.id}`}
|
pathView={`/orders/orders-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -78,10 +78,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Title</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>OrderNumber</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.title }
|
{ item.order_number }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -90,10 +90,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Course</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Customer</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.coursesOneListFormatter(item.course) }
|
{ dataFormatter.customersOneListFormatter(item.customer) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -102,10 +102,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Content</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Status</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.content }
|
{ item.status }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -114,17 +114,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>VideoFiles</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>TotalAmount</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{dataFormatter.filesFormatter(item.video_files).map(link => (
|
{ item.total }
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -133,10 +126,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Order</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>PlacedAt</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.order }
|
{ dataFormatter.dateTimeFormatter(item.placed_at) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -145,10 +138,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Duration(minutes)</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>ShippedAt</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.duration }
|
{ dataFormatter.dateTimeFormatter(item.shipped_at) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -157,10 +150,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>StartAt</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>DeliveryDate</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.dateTimeFormatter(item.start_at) }
|
{ dataFormatter.dateTimeFormatter(item.delivery_date) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -169,10 +162,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>EndAt</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>PaymentStatus</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.dateTimeFormatter(item.end_at) }
|
{ item.payment_status }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -181,10 +174,10 @@ const CardLessons = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Published</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>ShippingAddress</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.booleanFormatter(item.is_published) }
|
{ item.shipping_address }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -194,7 +187,7 @@ const CardLessons = ({
|
|||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{!loading && lessons.length === 0 && (
|
{!loading && orders.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -211,4 +204,4 @@ const CardLessons = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardLessons;
|
export default CardOrders;
|
||||||
@ -13,7 +13,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
lessons: any[];
|
orders: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -21,10 +21,10 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListLessons = ({ lessons, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
const ListOrders = ({ orders, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_LESSONS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ORDERS')
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
||||||
@ -34,13 +34,13 @@ const ListLessons = ({ lessons, loading, onDelete, currentPage, numPages, onPage
|
|||||||
<>
|
<>
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
{loading && <LoadingSpinner />}
|
{loading && <LoadingSpinner />}
|
||||||
{!loading && lessons.map((item) => (
|
{!loading && orders.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/lessons/lessons-view/?id=${item.id}`}
|
href={`/orders/orders-view/?id=${item.id}`}
|
||||||
className={
|
className={
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
}
|
}
|
||||||
@ -48,79 +48,72 @@ const ListLessons = ({ lessons, loading, onDelete, currentPage, numPages, onPage
|
|||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Title</p>
|
<p className={'text-xs text-gray-500 '}>OrderNumber</p>
|
||||||
<p className={'line-clamp-2'}>{ item.title }</p>
|
<p className={'line-clamp-2'}>{ item.order_number }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Course</p>
|
<p className={'text-xs text-gray-500 '}>Customer</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.coursesOneListFormatter(item.course) }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.customersOneListFormatter(item.customer) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Content</p>
|
<p className={'text-xs text-gray-500 '}>Status</p>
|
||||||
<p className={'line-clamp-2'}>{ item.content }</p>
|
<p className={'line-clamp-2'}>{ item.status }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>VideoFiles</p>
|
<p className={'text-xs text-gray-500 '}>TotalAmount</p>
|
||||||
{dataFormatter.filesFormatter(item.video_files).map(link => (
|
<p className={'line-clamp-2'}>{ item.total }</p>
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Order</p>
|
<p className={'text-xs text-gray-500 '}>PlacedAt</p>
|
||||||
<p className={'line-clamp-2'}>{ item.order }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.placed_at) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Duration(minutes)</p>
|
<p className={'text-xs text-gray-500 '}>ShippedAt</p>
|
||||||
<p className={'line-clamp-2'}>{ item.duration }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.shipped_at) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>StartAt</p>
|
<p className={'text-xs text-gray-500 '}>DeliveryDate</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.start_at) }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.delivery_date) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>EndAt</p>
|
<p className={'text-xs text-gray-500 '}>PaymentStatus</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.end_at) }</p>
|
<p className={'line-clamp-2'}>{ item.payment_status }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Published</p>
|
<p className={'text-xs text-gray-500 '}>ShippingAddress</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.is_published) }</p>
|
<p className={'line-clamp-2'}>{ item.shipping_address }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -129,8 +122,8 @@ const ListLessons = ({ lessons, loading, onDelete, currentPage, numPages, onPage
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/lessons/lessons-edit/?id=${item.id}`}
|
pathEdit={`/orders/orders-edit/?id=${item.id}`}
|
||||||
pathView={`/lessons/lessons-view/?id=${item.id}`}
|
pathView={`/orders/orders-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -139,7 +132,7 @@ const ListLessons = ({ lessons, loading, onDelete, currentPage, numPages, onPage
|
|||||||
</CardBox>
|
</CardBox>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!loading && lessons.length === 0 && (
|
{!loading && orders.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -156,4 +149,4 @@ const ListLessons = ({ lessons, loading, onDelete, currentPage, numPages, onPage
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListLessons
|
export default ListOrders
|
||||||
@ -4,7 +4,7 @@ import { ToastContainer, toast } from 'react-toastify';
|
|||||||
import BaseButton from '../BaseButton'
|
import BaseButton from '../BaseButton'
|
||||||
import CardBoxModal from '../CardBoxModal'
|
import CardBoxModal from '../CardBoxModal'
|
||||||
import CardBox from "../CardBox";
|
import CardBox from "../CardBox";
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/enrollments/enrollmentsSlice'
|
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/orders/ordersSlice'
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Field, Form, Formik } from "formik";
|
import { Field, Form, Formik } from "formik";
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
DataGrid,
|
DataGrid,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import {loadColumns} from "./configureEnrollmentsCols";
|
import {loadColumns} from "./configureOrdersCols";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
@ -24,7 +24,7 @@ import axios from 'axios';
|
|||||||
|
|
||||||
const perPage = 10
|
const perPage = 10
|
||||||
|
|
||||||
const TableSampleEnrollments = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
const TableSampleOrders = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -46,7 +46,7 @@ const TableSampleEnrollments = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
const [kanbanColumns, setKanbanColumns] = useState<Array<{id: string, label: string}> | null>(null);
|
const [kanbanColumns, setKanbanColumns] = useState<Array<{id: string, label: string}> | null>(null);
|
||||||
const [kanbanFilters, setKanbanFilters] = useState('');
|
const [kanbanFilters, setKanbanFilters] = useState('');
|
||||||
|
|
||||||
const { enrollments, loading, count, notify: enrollmentsNotify, refetch } = useAppSelector((state) => state.enrollments)
|
const { orders, loading, count, notify: ordersNotify, refetch } = useAppSelector((state) => state.orders)
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
@ -66,10 +66,10 @@ const TableSampleEnrollments = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (enrollmentsNotify.showNotification) {
|
if (ordersNotify.showNotification) {
|
||||||
notify(enrollmentsNotify.typeNotification, enrollmentsNotify.textNotification);
|
notify(ordersNotify.typeNotification, ordersNotify.textNotification);
|
||||||
}
|
}
|
||||||
}, [enrollmentsNotify.showNotification]);
|
}, [ordersNotify.showNotification]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
@ -98,13 +98,19 @@ const TableSampleEnrollments = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
|
|
||||||
setKanbanColumns([
|
setKanbanColumns([
|
||||||
|
|
||||||
{ id: "pending", label: "pending" },
|
{ id: "Pending", label: "Pending" },
|
||||||
|
|
||||||
{ id: "active", label: "active" },
|
{ id: "Processing", label: "Processing" },
|
||||||
|
|
||||||
{ id: "completed", label: "completed" },
|
{ id: "Shipped", label: "Shipped" },
|
||||||
|
|
||||||
{ id: "dropped", label: "dropped" },
|
{ id: "Delivered", label: "Delivered" },
|
||||||
|
|
||||||
|
{ id: "Cancelled", label: "Cancelled" },
|
||||||
|
|
||||||
|
{ id: "Returned", label: "Returned" },
|
||||||
|
|
||||||
|
{ id: "Refunded", label: "Refunded" },
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -208,7 +214,7 @@ const TableSampleEnrollments = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
|
|
||||||
loadColumns(
|
loadColumns(
|
||||||
handleDeleteModalAction,
|
handleDeleteModalAction,
|
||||||
`enrollments`,
|
`orders`,
|
||||||
currentUser,
|
currentUser,
|
||||||
).then((newCols) => setColumns(newCols));
|
).then((newCols) => setColumns(newCols));
|
||||||
}, [currentUser]);
|
}, [currentUser]);
|
||||||
@ -246,7 +252,7 @@ const TableSampleEnrollments = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
sx={dataGridStyles}
|
sx={dataGridStyles}
|
||||||
className={'datagrid--table'}
|
className={'datagrid--table'}
|
||||||
getRowClassName={() => `datagrid--row`}
|
getRowClassName={() => `datagrid--row`}
|
||||||
rows={enrollments ?? []}
|
rows={orders ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -475,8 +481,8 @@ const TableSampleEnrollments = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
{!showGrid && kanbanColumns && (
|
{!showGrid && kanbanColumns && (
|
||||||
<KanbanBoard
|
<KanbanBoard
|
||||||
columnFieldName={'status'}
|
columnFieldName={'status'}
|
||||||
showFieldName={'enrollment_code'}
|
showFieldName={'order_number'}
|
||||||
entityName={'enrollments'}
|
entityName={'orders'}
|
||||||
filtersQuery={kanbanFilters}
|
filtersQuery={kanbanFilters}
|
||||||
deleteThunk={deleteItem}
|
deleteThunk={deleteItem}
|
||||||
updateThunk={update}
|
updateThunk={update}
|
||||||
@ -504,4 +510,4 @@ const TableSampleEnrollments = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TableSampleEnrollments
|
export default TableSampleOrders
|
||||||
@ -37,13 +37,13 @@ export const loadColumns = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_LESSONS')
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_ORDERS')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'title',
|
field: 'order_number',
|
||||||
headerName: 'Title',
|
headerName: 'OrderNumber',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -57,8 +57,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'course',
|
field: 'customer',
|
||||||
headerName: 'Course',
|
headerName: 'Customer',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -72,15 +72,15 @@ export const loadColumns = async (
|
|||||||
type: 'singleSelect',
|
type: 'singleSelect',
|
||||||
getOptionValue: (value: any) => value?.id,
|
getOptionValue: (value: any) => value?.id,
|
||||||
getOptionLabel: (value: any) => value?.label,
|
getOptionLabel: (value: any) => value?.label,
|
||||||
valueOptions: await callOptionsApi('courses'),
|
valueOptions: await callOptionsApi('customers'),
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
params?.value?.id ?? params?.value,
|
params?.value?.id ?? params?.value,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'content',
|
field: 'status',
|
||||||
headerName: 'Content',
|
headerName: 'Status',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -94,34 +94,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'video_files',
|
field: 'total',
|
||||||
headerName: 'VideoFiles',
|
headerName: 'TotalAmount',
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
editable: false,
|
|
||||||
sortable: false,
|
|
||||||
renderCell: (params: GridValueGetterParams) => (
|
|
||||||
<>
|
|
||||||
{dataFormatter.filesFormatter(params.row.video_files).map(link => (
|
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'order',
|
|
||||||
headerName: 'Order',
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -136,24 +110,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'duration',
|
field: 'placed_at',
|
||||||
headerName: 'Duration(minutes)',
|
headerName: 'PlacedAt',
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'start_at',
|
|
||||||
headerName: 'StartAt',
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -165,13 +123,13 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
new Date(params.row.start_at),
|
new Date(params.row.placed_at),
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'end_at',
|
field: 'shipped_at',
|
||||||
headerName: 'EndAt',
|
headerName: 'ShippedAt',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -183,13 +141,13 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
new Date(params.row.end_at),
|
new Date(params.row.shipped_at),
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'is_published',
|
field: 'delivery_date',
|
||||||
headerName: 'Published',
|
headerName: 'DeliveryDate',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -199,10 +157,42 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
type: 'boolean',
|
type: 'dateTime',
|
||||||
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
|
new Date(params.row.delivery_date),
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'payment_status',
|
||||||
|
headerName: 'PaymentStatus',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'shipping_address',
|
||||||
|
headerName: 'ShippingAddress',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
@ -216,8 +206,8 @@ export const loadColumns = async (
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={params?.row?.id}
|
itemId={params?.row?.id}
|
||||||
pathEdit={`/lessons/lessons-edit/?id=${params?.row?.id}`}
|
pathEdit={`/orders/orders-edit/?id=${params?.row?.id}`}
|
||||||
pathView={`/lessons/lessons-view/?id=${params?.row?.id}`}
|
pathView={`/orders/orders-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
quizzes: any[];
|
payments: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -20,8 +20,8 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardQuizzes = ({
|
const CardPayments = ({
|
||||||
quizzes,
|
payments,
|
||||||
loading,
|
loading,
|
||||||
onDelete,
|
onDelete,
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -37,7 +37,7 @@ const CardQuizzes = ({
|
|||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_QUIZZES')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PAYMENTS')
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,7 +47,7 @@ const CardQuizzes = ({
|
|||||||
role='list'
|
role='list'
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
>
|
>
|
||||||
{!loading && quizzes.map((item, index) => (
|
{!loading && payments.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
@ -57,8 +57,8 @@ const CardQuizzes = ({
|
|||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
<Link href={`/quizzes/quizzes-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
<Link href={`/payments/payments-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
{item.title}
|
{item.reference}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ const CardQuizzes = ({
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/quizzes/quizzes-edit/?id=${item.id}`}
|
pathEdit={`/payments/payments-edit/?id=${item.id}`}
|
||||||
pathView={`/quizzes/quizzes-view/?id=${item.id}`}
|
pathView={`/payments/payments-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -78,10 +78,10 @@ const CardQuizzes = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Title</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Reference</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.title }
|
{ item.reference }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -90,10 +90,10 @@ const CardQuizzes = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Lesson</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Order</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.lessonsOneListFormatter(item.lesson) }
|
{ dataFormatter.ordersOneListFormatter(item.order) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -102,10 +102,10 @@ const CardQuizzes = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Description</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Amount</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.description }
|
{ item.amount }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -114,10 +114,10 @@ const CardQuizzes = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>PassingScore</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Method</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.passing_score }
|
{ item.method }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -126,10 +126,10 @@ const CardQuizzes = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>TimeLimit(minutes)</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Status</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.time_limit }
|
{ item.status }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -138,10 +138,10 @@ const CardQuizzes = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Active</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>PaidAt</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.booleanFormatter(item.is_active) }
|
{ dataFormatter.dateTimeFormatter(item.paid_at) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -151,7 +151,7 @@ const CardQuizzes = ({
|
|||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{!loading && quizzes.length === 0 && (
|
{!loading && payments.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -168,4 +168,4 @@ const CardQuizzes = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardQuizzes;
|
export default CardPayments;
|
||||||
@ -13,7 +13,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
quizzes: any[];
|
payments: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -21,10 +21,10 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListQuizzes = ({ quizzes, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
const ListPayments = ({ payments, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_QUIZZES')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PAYMENTS')
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
||||||
@ -34,13 +34,13 @@ const ListQuizzes = ({ quizzes, loading, onDelete, currentPage, numPages, onPage
|
|||||||
<>
|
<>
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
{loading && <LoadingSpinner />}
|
{loading && <LoadingSpinner />}
|
||||||
{!loading && quizzes.map((item) => (
|
{!loading && payments.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/quizzes/quizzes-view/?id=${item.id}`}
|
href={`/payments/payments-view/?id=${item.id}`}
|
||||||
className={
|
className={
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
}
|
}
|
||||||
@ -48,48 +48,48 @@ const ListQuizzes = ({ quizzes, loading, onDelete, currentPage, numPages, onPage
|
|||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Title</p>
|
<p className={'text-xs text-gray-500 '}>Reference</p>
|
||||||
<p className={'line-clamp-2'}>{ item.title }</p>
|
<p className={'line-clamp-2'}>{ item.reference }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Lesson</p>
|
<p className={'text-xs text-gray-500 '}>Order</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.lessonsOneListFormatter(item.lesson) }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.ordersOneListFormatter(item.order) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Description</p>
|
<p className={'text-xs text-gray-500 '}>Amount</p>
|
||||||
<p className={'line-clamp-2'}>{ item.description }</p>
|
<p className={'line-clamp-2'}>{ item.amount }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>PassingScore</p>
|
<p className={'text-xs text-gray-500 '}>Method</p>
|
||||||
<p className={'line-clamp-2'}>{ item.passing_score }</p>
|
<p className={'line-clamp-2'}>{ item.method }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>TimeLimit(minutes)</p>
|
<p className={'text-xs text-gray-500 '}>Status</p>
|
||||||
<p className={'line-clamp-2'}>{ item.time_limit }</p>
|
<p className={'line-clamp-2'}>{ item.status }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Active</p>
|
<p className={'text-xs text-gray-500 '}>PaidAt</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.is_active) }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.paid_at) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -98,8 +98,8 @@ const ListQuizzes = ({ quizzes, loading, onDelete, currentPage, numPages, onPage
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/quizzes/quizzes-edit/?id=${item.id}`}
|
pathEdit={`/payments/payments-edit/?id=${item.id}`}
|
||||||
pathView={`/quizzes/quizzes-view/?id=${item.id}`}
|
pathView={`/payments/payments-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ const ListQuizzes = ({ quizzes, loading, onDelete, currentPage, numPages, onPage
|
|||||||
</CardBox>
|
</CardBox>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!loading && quizzes.length === 0 && (
|
{!loading && payments.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -125,4 +125,4 @@ const ListQuizzes = ({ quizzes, loading, onDelete, currentPage, numPages, onPage
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListQuizzes
|
export default ListPayments
|
||||||
@ -4,7 +4,7 @@ import { ToastContainer, toast } from 'react-toastify';
|
|||||||
import BaseButton from '../BaseButton'
|
import BaseButton from '../BaseButton'
|
||||||
import CardBoxModal from '../CardBoxModal'
|
import CardBoxModal from '../CardBoxModal'
|
||||||
import CardBox from "../CardBox";
|
import CardBox from "../CardBox";
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/progress/progressSlice'
|
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/payments/paymentsSlice'
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Field, Form, Formik } from "formik";
|
import { Field, Form, Formik } from "formik";
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
DataGrid,
|
DataGrid,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import {loadColumns} from "./configureProgressCols";
|
import {loadColumns} from "./configurePaymentsCols";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
@ -21,7 +21,7 @@ import {dataGridStyles} from "../../styles";
|
|||||||
|
|
||||||
const perPage = 10
|
const perPage = 10
|
||||||
|
|
||||||
const TableSampleProgress = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
const TableSamplePayments = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -40,7 +40,7 @@ const TableSampleProgress = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { progress, loading, count, notify: progressNotify, refetch } = useAppSelector((state) => state.progress)
|
const { payments, loading, count, notify: paymentsNotify, refetch } = useAppSelector((state) => state.payments)
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
@ -60,10 +60,10 @@ const TableSampleProgress = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (progressNotify.showNotification) {
|
if (paymentsNotify.showNotification) {
|
||||||
notify(progressNotify.typeNotification, progressNotify.textNotification);
|
notify(paymentsNotify.typeNotification, paymentsNotify.textNotification);
|
||||||
}
|
}
|
||||||
}, [progressNotify.showNotification]);
|
}, [paymentsNotify.showNotification]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
@ -177,7 +177,7 @@ const TableSampleProgress = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
|
|
||||||
loadColumns(
|
loadColumns(
|
||||||
handleDeleteModalAction,
|
handleDeleteModalAction,
|
||||||
`progress`,
|
`payments`,
|
||||||
currentUser,
|
currentUser,
|
||||||
).then((newCols) => setColumns(newCols));
|
).then((newCols) => setColumns(newCols));
|
||||||
}, [currentUser]);
|
}, [currentUser]);
|
||||||
@ -215,7 +215,7 @@ const TableSampleProgress = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
sx={dataGridStyles}
|
sx={dataGridStyles}
|
||||||
className={'datagrid--table'}
|
className={'datagrid--table'}
|
||||||
getRowClassName={() => `datagrid--row`}
|
getRowClassName={() => `datagrid--row`}
|
||||||
rows={progress ?? []}
|
rows={payments ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -460,4 +460,4 @@ const TableSampleProgress = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TableSampleProgress
|
export default TableSamplePayments
|
||||||
@ -37,13 +37,13 @@ export const loadColumns = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_QUIZZES')
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_PAYMENTS')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'title',
|
field: 'reference',
|
||||||
headerName: 'Title',
|
headerName: 'Reference',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -57,8 +57,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'lesson',
|
field: 'order',
|
||||||
headerName: 'Lesson',
|
headerName: 'Order',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -72,30 +72,15 @@ export const loadColumns = async (
|
|||||||
type: 'singleSelect',
|
type: 'singleSelect',
|
||||||
getOptionValue: (value: any) => value?.id,
|
getOptionValue: (value: any) => value?.id,
|
||||||
getOptionLabel: (value: any) => value?.label,
|
getOptionLabel: (value: any) => value?.label,
|
||||||
valueOptions: await callOptionsApi('lessons'),
|
valueOptions: await callOptionsApi('orders'),
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
params?.value?.id ?? params?.value,
|
params?.value?.id ?? params?.value,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'description',
|
field: 'amount',
|
||||||
headerName: 'Description',
|
headerName: 'Amount',
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'passing_score',
|
|
||||||
headerName: 'PassingScore',
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -110,8 +95,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'time_limit',
|
field: 'method',
|
||||||
headerName: 'TimeLimit(minutes)',
|
headerName: 'Method',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -121,13 +106,12 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'is_active',
|
field: 'status',
|
||||||
headerName: 'Active',
|
headerName: 'Status',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -137,7 +121,24 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
type: 'boolean',
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'paid_at',
|
||||||
|
headerName: 'PaidAt',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'dateTime',
|
||||||
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
|
new Date(params.row.paid_at),
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -154,8 +155,8 @@ export const loadColumns = async (
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={params?.row?.id}
|
itemId={params?.row?.id}
|
||||||
pathEdit={`/quizzes/quizzes-edit/?id=${params?.row?.id}`}
|
pathEdit={`/payments/payments-edit/?id=${params?.row?.id}`}
|
||||||
pathView={`/quizzes/quizzes-view/?id=${params?.row?.id}`}
|
pathView={`/payments/payments-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
enrollments: any[];
|
products: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -20,8 +20,8 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardEnrollments = ({
|
const CardProducts = ({
|
||||||
enrollments,
|
products,
|
||||||
loading,
|
loading,
|
||||||
onDelete,
|
onDelete,
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -37,7 +37,7 @@ const CardEnrollments = ({
|
|||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ENROLLMENTS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PRODUCTS')
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,7 +47,7 @@ const CardEnrollments = ({
|
|||||||
role='list'
|
role='list'
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
>
|
>
|
||||||
{!loading && enrollments.map((item, index) => (
|
{!loading && products.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
@ -55,19 +55,28 @@ const CardEnrollments = ({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
<div className={`flex items-center ${bgColor} p-6 md:p-0 md:block gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
<Link href={`/enrollments/enrollments-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
<Link
|
||||||
{item.enrollment_code}
|
href={`/products/products-view/?id=${item.id}`}
|
||||||
|
className={'cursor-pointer'}
|
||||||
|
>
|
||||||
|
<ImageField
|
||||||
|
name={'Avatar'}
|
||||||
|
image={item.images}
|
||||||
|
className='w-12 h-12 md:w-full md:h-44 rounded-lg md:rounded-b-none overflow-hidden ring-1 ring-gray-900/10'
|
||||||
|
imageClassName='h-full w-full flex-none rounded-lg md:rounded-b-none bg-white object-cover'
|
||||||
|
/>
|
||||||
|
<p className={'px-6 py-2 font-semibold'}>{item.name}</p>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
<div className='ml-auto '>
|
<div className='ml-auto md:absolute md:top-0 md:right-0 '>
|
||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/enrollments/enrollments-edit/?id=${item.id}`}
|
pathEdit={`/products/products-edit/?id=${item.id}`}
|
||||||
pathView={`/enrollments/enrollments-view/?id=${item.id}`}
|
pathView={`/products/products-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -78,10 +87,10 @@ const CardEnrollments = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>EnrollmentCode</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>SKU</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.enrollment_code }
|
{ item.sku }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -90,10 +99,10 @@ const CardEnrollments = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Student</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Name</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.usersOneListFormatter(item.student) }
|
{ item.name }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -102,10 +111,10 @@ const CardEnrollments = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Course</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Description</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.coursesOneListFormatter(item.course) }
|
{ item.description }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -114,10 +123,50 @@ const CardEnrollments = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>EnrolledAt</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Price</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.dateTimeFormatter(item.enrolled_at) }
|
{ item.price }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Stock</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.stock }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Images</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium'>
|
||||||
|
<ImageField
|
||||||
|
name={'Avatar'}
|
||||||
|
image={item.images}
|
||||||
|
className='mx-auto w-8 h-8'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Category</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ dataFormatter.categoriesOneListFormatter(item.category) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -136,41 +185,10 @@ const CardEnrollments = ({
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Progress(%)</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium line-clamp-4'>
|
|
||||||
{ item.progress_percent }
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>CertificateFile</dt>
|
|
||||||
<dd className='flex items-start gap-x-2'>
|
|
||||||
<div className='font-medium'>
|
|
||||||
{dataFormatter.filesFormatter(item.certificate).map(link => (
|
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{!loading && enrollments.length === 0 && (
|
{!loading && products.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -187,4 +205,4 @@ const CardEnrollments = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardEnrollments;
|
export default CardProducts;
|
||||||
@ -13,7 +13,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
enrollments: any[];
|
products: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -21,10 +21,10 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListEnrollments = ({ enrollments, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
const ListProducts = ({ products, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ENROLLMENTS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PRODUCTS')
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
||||||
@ -34,13 +34,20 @@ const ListEnrollments = ({ enrollments, loading, onDelete, currentPage, numPages
|
|||||||
<>
|
<>
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
{loading && <LoadingSpinner />}
|
{loading && <LoadingSpinner />}
|
||||||
{!loading && enrollments.map((item) => (
|
{!loading && products.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
|
<ImageField
|
||||||
|
name={'Avatar'}
|
||||||
|
image={item.images}
|
||||||
|
className='w-24 h-24 rounded-l overflow-hidden hidden md:block'
|
||||||
|
imageClassName={'rounded-l rounded-r-none h-full object-cover'}
|
||||||
|
/>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/enrollments/enrollments-view/?id=${item.id}`}
|
href={`/products/products-view/?id=${item.id}`}
|
||||||
className={
|
className={
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
}
|
}
|
||||||
@ -48,32 +55,60 @@ const ListEnrollments = ({ enrollments, loading, onDelete, currentPage, numPages
|
|||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>EnrollmentCode</p>
|
<p className={'text-xs text-gray-500 '}>SKU</p>
|
||||||
<p className={'line-clamp-2'}>{ item.enrollment_code }</p>
|
<p className={'line-clamp-2'}>{ item.sku }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Student</p>
|
<p className={'text-xs text-gray-500 '}>Name</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.usersOneListFormatter(item.student) }</p>
|
<p className={'line-clamp-2'}>{ item.name }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Course</p>
|
<p className={'text-xs text-gray-500 '}>Description</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.coursesOneListFormatter(item.course) }</p>
|
<p className={'line-clamp-2'}>{ item.description }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>EnrolledAt</p>
|
<p className={'text-xs text-gray-500 '}>Price</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.enrolled_at) }</p>
|
<p className={'line-clamp-2'}>{ item.price }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Stock</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.stock }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Images</p>
|
||||||
|
<ImageField
|
||||||
|
name={'Avatar'}
|
||||||
|
image={item.images}
|
||||||
|
className='mx-auto w-8 h-8'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Category</p>
|
||||||
|
<p className={'line-clamp-2'}>{ dataFormatter.categoriesOneListFormatter(item.category) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -86,35 +121,12 @@ const ListEnrollments = ({ enrollments, loading, onDelete, currentPage, numPages
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>Progress(%)</p>
|
|
||||||
<p className={'line-clamp-2'}>{ item.progress_percent }</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
|
||||||
<p className={'text-xs text-gray-500 '}>CertificateFile</p>
|
|
||||||
{dataFormatter.filesFormatter(item.certificate).map(link => (
|
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Link>
|
</Link>
|
||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/enrollments/enrollments-edit/?id=${item.id}`}
|
pathEdit={`/products/products-edit/?id=${item.id}`}
|
||||||
pathView={`/enrollments/enrollments-view/?id=${item.id}`}
|
pathView={`/products/products-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -123,7 +135,7 @@ const ListEnrollments = ({ enrollments, loading, onDelete, currentPage, numPages
|
|||||||
</CardBox>
|
</CardBox>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!loading && enrollments.length === 0 && (
|
{!loading && products.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -140,4 +152,4 @@ const ListEnrollments = ({ enrollments, loading, onDelete, currentPage, numPages
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListEnrollments
|
export default ListProducts
|
||||||
463
frontend/src/components/Products/TableProducts.tsx
Normal file
463
frontend/src/components/Products/TableProducts.tsx
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
import React, { useEffect, useState, useMemo } from 'react'
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import { ToastContainer, toast } from 'react-toastify';
|
||||||
|
import BaseButton from '../BaseButton'
|
||||||
|
import CardBoxModal from '../CardBoxModal'
|
||||||
|
import CardBox from "../CardBox";
|
||||||
|
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/products/productsSlice'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Field, Form, Formik } from "formik";
|
||||||
|
import {
|
||||||
|
DataGrid,
|
||||||
|
GridColDef,
|
||||||
|
} from '@mui/x-data-grid';
|
||||||
|
import {loadColumns} from "./configureProductsCols";
|
||||||
|
import _ from 'lodash';
|
||||||
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
|
import {dataGridStyles} from "../../styles";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const perPage = 10
|
||||||
|
|
||||||
|
const TableSampleProducts = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const pagesList = [];
|
||||||
|
const [id, setId] = useState(null);
|
||||||
|
const [currentPage, setCurrentPage] = useState(0);
|
||||||
|
const [filterRequest, setFilterRequest] = React.useState('');
|
||||||
|
const [columns, setColumns] = useState<GridColDef[]>([]);
|
||||||
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
|
const [sortModel, setSortModel] = useState([
|
||||||
|
{
|
||||||
|
field: '',
|
||||||
|
sort: 'desc',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { products, loading, count, notify: productsNotify, refetch } = useAppSelector((state) => state.products)
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
|
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
|
||||||
|
for (let i = 0; i < numPages; i++) {
|
||||||
|
pagesList.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadData = async (page = currentPage, request = filterRequest) => {
|
||||||
|
if (page !== currentPage) setCurrentPage(page);
|
||||||
|
if (request !== filterRequest) setFilterRequest(request);
|
||||||
|
const { sort, field } = sortModel[0];
|
||||||
|
|
||||||
|
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
|
||||||
|
dispatch(fetch({ limit: perPage, page, query }));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (productsNotify.showNotification) {
|
||||||
|
notify(productsNotify.typeNotification, productsNotify.textNotification);
|
||||||
|
}
|
||||||
|
}, [productsNotify.showNotification]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!currentUser) return;
|
||||||
|
loadData();
|
||||||
|
}, [sortModel, currentUser]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (refetch) {
|
||||||
|
loadData(0);
|
||||||
|
dispatch(setRefetch(false));
|
||||||
|
}
|
||||||
|
}, [refetch, dispatch]);
|
||||||
|
|
||||||
|
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
|
||||||
|
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
|
||||||
|
|
||||||
|
const handleModalAction = () => {
|
||||||
|
setIsModalInfoActive(false)
|
||||||
|
setIsModalTrashActive(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleDeleteModalAction = (id: string) => {
|
||||||
|
setId(id)
|
||||||
|
setIsModalTrashActive(true)
|
||||||
|
}
|
||||||
|
const handleDeleteAction = async () => {
|
||||||
|
if (id) {
|
||||||
|
await dispatch(deleteItem(id));
|
||||||
|
await loadData(0);
|
||||||
|
setIsModalTrashActive(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateFilterRequests = useMemo(() => {
|
||||||
|
let request = '&';
|
||||||
|
filterItems.forEach((item) => {
|
||||||
|
const isRangeFilter = filters.find(
|
||||||
|
(filter) =>
|
||||||
|
filter.title === item.fields.selectedField &&
|
||||||
|
(filter.number || filter.date),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isRangeFilter) {
|
||||||
|
const from = item.fields.filterValueFrom;
|
||||||
|
const to = item.fields.filterValueTo;
|
||||||
|
if (from) {
|
||||||
|
request += `${item.fields.selectedField}Range=${from}&`;
|
||||||
|
}
|
||||||
|
if (to) {
|
||||||
|
request += `${item.fields.selectedField}Range=${to}&`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const value = item.fields.filterValue;
|
||||||
|
if (value) {
|
||||||
|
request += `${item.fields.selectedField}=${value}&`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return request;
|
||||||
|
}, [filterItems, filters]);
|
||||||
|
|
||||||
|
const deleteFilter = (value) => {
|
||||||
|
const newItems = filterItems.filter((item) => item.id !== value);
|
||||||
|
|
||||||
|
if (newItems.length) {
|
||||||
|
setFilterItems(newItems);
|
||||||
|
} else {
|
||||||
|
loadData(0, '');
|
||||||
|
|
||||||
|
setFilterItems(newItems);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
loadData(0, generateFilterRequests);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (id) => (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
const name = e.target.name;
|
||||||
|
|
||||||
|
setFilterItems(
|
||||||
|
filterItems.map((item) => {
|
||||||
|
if (item.id !== id) return item;
|
||||||
|
if (name === 'selectedField') return { id, fields: { [name]: value } };
|
||||||
|
|
||||||
|
return { id, fields: { ...item.fields, [name]: value } }
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setFilterItems([]);
|
||||||
|
loadData(0, '');
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPageChange = (page: number) => {
|
||||||
|
loadData(page);
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!currentUser) return;
|
||||||
|
|
||||||
|
loadColumns(
|
||||||
|
handleDeleteModalAction,
|
||||||
|
`products`,
|
||||||
|
currentUser,
|
||||||
|
).then((newCols) => setColumns(newCols));
|
||||||
|
}, [currentUser]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleTableSubmit = async (id: string, data) => {
|
||||||
|
|
||||||
|
if (!_.isEmpty(data)) {
|
||||||
|
await dispatch(update({ id, data }))
|
||||||
|
.unwrap()
|
||||||
|
.then((res) => res)
|
||||||
|
.catch((err) => {
|
||||||
|
throw new Error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeleteRows = async (selectedRows) => {
|
||||||
|
await dispatch(deleteItemsByIds(selectedRows));
|
||||||
|
await loadData(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const controlClasses =
|
||||||
|
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
|
||||||
|
` ${bgColor} ${focusRing} ${corners} ` +
|
||||||
|
'dark:bg-slate-800 border';
|
||||||
|
|
||||||
|
|
||||||
|
const dataGrid = (
|
||||||
|
<div className='relative overflow-x-auto'>
|
||||||
|
<DataGrid
|
||||||
|
autoHeight
|
||||||
|
rowHeight={64}
|
||||||
|
sx={dataGridStyles}
|
||||||
|
className={'datagrid--table'}
|
||||||
|
getRowClassName={() => `datagrid--row`}
|
||||||
|
rows={products ?? []}
|
||||||
|
columns={columns}
|
||||||
|
initialState={{
|
||||||
|
pagination: {
|
||||||
|
paginationModel: {
|
||||||
|
pageSize: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
disableRowSelectionOnClick
|
||||||
|
onProcessRowUpdateError={(params) => {
|
||||||
|
console.log('Error', params);
|
||||||
|
}}
|
||||||
|
processRowUpdate={async (newRow, oldRow) => {
|
||||||
|
const data = dataFormatter.dataGridEditFormatter(newRow);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await handleTableSubmit(newRow.id, data);
|
||||||
|
return newRow;
|
||||||
|
} catch {
|
||||||
|
return oldRow;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
sortingMode={'server'}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(ids) => {
|
||||||
|
setSelectedRows(ids)
|
||||||
|
}}
|
||||||
|
onSortModelChange={(params) => {
|
||||||
|
params.length
|
||||||
|
? setSortModel(params)
|
||||||
|
: setSortModel([{ field: '', sort: 'desc' }]);
|
||||||
|
}}
|
||||||
|
rowCount={count}
|
||||||
|
pageSizeOptions={[10]}
|
||||||
|
paginationMode={'server'}
|
||||||
|
loading={loading}
|
||||||
|
onPaginationModelChange={(params) => {
|
||||||
|
onPageChange(params.page);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
||||||
|
<CardBox>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
checkboxes: ['lorem'],
|
||||||
|
switches: ['lorem'],
|
||||||
|
radio: 'lorem',
|
||||||
|
}}
|
||||||
|
onSubmit={() => null}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<>
|
||||||
|
{filterItems && filterItems.map((filterItem) => {
|
||||||
|
return (
|
||||||
|
<div key={filterItem.id} className="flex mb-4">
|
||||||
|
<div className="flex flex-col w-full mr-3">
|
||||||
|
<div className=" text-gray-500 font-bold">Filter</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='selectedField'
|
||||||
|
id='selectedField'
|
||||||
|
component='select'
|
||||||
|
value={filterItem?.fields?.selectedField || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
>
|
||||||
|
{filters.map((selectOption) => (
|
||||||
|
<option
|
||||||
|
key={selectOption.title}
|
||||||
|
value={`${selectOption.title}`}
|
||||||
|
>
|
||||||
|
{selectOption.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Field>
|
||||||
|
</div>
|
||||||
|
{filters.find((filter) =>
|
||||||
|
filter.title === filterItem?.fields?.selectedField
|
||||||
|
)?.type === 'enum' ? (
|
||||||
|
<div className="flex flex-col w-full mr-3">
|
||||||
|
<div className="text-gray-500 font-bold">
|
||||||
|
Value
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name="filterValue"
|
||||||
|
id='filterValue'
|
||||||
|
component="select"
|
||||||
|
value={filterItem?.fields?.filterValue || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
>
|
||||||
|
<option value="">Select Value</option>
|
||||||
|
{filters.find((filter) =>
|
||||||
|
filter.title === filterItem?.fields?.selectedField
|
||||||
|
)?.options?.map((option) => (
|
||||||
|
<option key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Field>
|
||||||
|
</div>
|
||||||
|
) : filters.find((filter) =>
|
||||||
|
filter.title === filterItem?.fields?.selectedField
|
||||||
|
)?.number ? (
|
||||||
|
<div className="flex flex-row w-full mr-3">
|
||||||
|
<div className="flex flex-col w-full mr-3">
|
||||||
|
<div className=" text-gray-500 font-bold">From</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValueFrom'
|
||||||
|
placeholder='From'
|
||||||
|
id='filterValueFrom'
|
||||||
|
value={filterItem?.fields?.filterValueFrom || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col w-full">
|
||||||
|
<div className=" text-gray-500 font-bold">To</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValueTo'
|
||||||
|
placeholder='to'
|
||||||
|
id='filterValueTo'
|
||||||
|
value={filterItem?.fields?.filterValueTo || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : filters.find(
|
||||||
|
(filter) =>
|
||||||
|
filter.title ===
|
||||||
|
filterItem?.fields?.selectedField
|
||||||
|
)?.date ? (
|
||||||
|
<div className='flex flex-row w-full mr-3'>
|
||||||
|
<div className='flex flex-col w-full mr-3'>
|
||||||
|
<div className=' text-gray-500 font-bold'>
|
||||||
|
From
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValueFrom'
|
||||||
|
placeholder='From'
|
||||||
|
id='filterValueFrom'
|
||||||
|
type='datetime-local'
|
||||||
|
value={filterItem?.fields?.filterValueFrom || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col w-full'>
|
||||||
|
<div className=' text-gray-500 font-bold'>To</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValueTo'
|
||||||
|
placeholder='to'
|
||||||
|
id='filterValueTo'
|
||||||
|
type='datetime-local'
|
||||||
|
value={filterItem?.fields?.filterValueTo || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col w-full mr-3">
|
||||||
|
<div className=" text-gray-500 font-bold">Contains</div>
|
||||||
|
<Field
|
||||||
|
className={controlClasses}
|
||||||
|
name='filterValue'
|
||||||
|
placeholder='Contained'
|
||||||
|
id='filterValue'
|
||||||
|
value={filterItem?.fields?.filterValue || ''}
|
||||||
|
onChange={handleChange(filterItem.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className=" text-gray-500 font-bold">Action</div>
|
||||||
|
<BaseButton
|
||||||
|
className="my-2"
|
||||||
|
type='reset'
|
||||||
|
color='danger'
|
||||||
|
label='Delete'
|
||||||
|
onClick={() => {
|
||||||
|
deleteFilter(filterItem.id)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<div className="flex">
|
||||||
|
<BaseButton
|
||||||
|
className="my-2 mr-3"
|
||||||
|
color="success"
|
||||||
|
label='Apply'
|
||||||
|
onClick={handleSubmit}
|
||||||
|
/>
|
||||||
|
<BaseButton
|
||||||
|
className="my-2"
|
||||||
|
color='info'
|
||||||
|
label='Cancel'
|
||||||
|
onClick={handleReset}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox> : null
|
||||||
|
}
|
||||||
|
<CardBoxModal
|
||||||
|
title="Please confirm"
|
||||||
|
buttonColor="info"
|
||||||
|
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
||||||
|
isActive={isModalTrashActive}
|
||||||
|
onConfirm={handleDeleteAction}
|
||||||
|
onCancel={handleModalAction}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete this item?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
|
||||||
|
|
||||||
|
{dataGrid}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{selectedRows.length > 0 &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
className='me-4'
|
||||||
|
color='danger'
|
||||||
|
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
||||||
|
onClick={() => onDeleteRows(selectedRows)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button'),
|
||||||
|
)}
|
||||||
|
<ToastContainer />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TableSampleProducts
|
||||||
@ -37,13 +37,13 @@ export const loadColumns = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_ENROLLMENTS')
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_PRODUCTS')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'enrollment_code',
|
field: 'sku',
|
||||||
headerName: 'EnrollmentCode',
|
headerName: 'SKU',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -57,8 +57,91 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'student',
|
field: 'name',
|
||||||
headerName: 'Student',
|
headerName: 'Name',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'description',
|
||||||
|
headerName: 'Description',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'price',
|
||||||
|
headerName: 'Price',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'number',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'stock',
|
||||||
|
headerName: 'Stock',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
type: 'number',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'images',
|
||||||
|
headerName: 'Images',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
editable: false,
|
||||||
|
sortable: false,
|
||||||
|
renderCell: (params: GridValueGetterParams) => (
|
||||||
|
<ImageField
|
||||||
|
name={'Avatar'}
|
||||||
|
image={params?.row?.images}
|
||||||
|
className='w-24 h-24 mx-auto lg:w-6 lg:h-6'
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'category',
|
||||||
|
headerName: 'Category',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -72,52 +155,12 @@ export const loadColumns = async (
|
|||||||
type: 'singleSelect',
|
type: 'singleSelect',
|
||||||
getOptionValue: (value: any) => value?.id,
|
getOptionValue: (value: any) => value?.id,
|
||||||
getOptionLabel: (value: any) => value?.label,
|
getOptionLabel: (value: any) => value?.label,
|
||||||
valueOptions: await callOptionsApi('users'),
|
valueOptions: await callOptionsApi('categories'),
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
params?.value?.id ?? params?.value,
|
params?.value?.id ?? params?.value,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
field: 'course',
|
|
||||||
headerName: 'Course',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
sortable: false,
|
|
||||||
type: 'singleSelect',
|
|
||||||
getOptionValue: (value: any) => value?.id,
|
|
||||||
getOptionLabel: (value: any) => value?.label,
|
|
||||||
valueOptions: await callOptionsApi('courses'),
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
params?.value?.id ?? params?.value,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'enrolled_at',
|
|
||||||
headerName: 'EnrolledAt',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'dateTime',
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
new Date(params.row.enrolled_at),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'status',
|
||||||
headerName: 'Status',
|
headerName: 'Status',
|
||||||
@ -133,48 +176,6 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
field: 'progress_percent',
|
|
||||||
headerName: 'Progress(%)',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'certificate',
|
|
||||||
headerName: 'CertificateFile',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
editable: false,
|
|
||||||
sortable: false,
|
|
||||||
renderCell: (params: GridValueGetterParams) => (
|
|
||||||
<>
|
|
||||||
{dataFormatter.filesFormatter(params.row.certificate).map(link => (
|
|
||||||
<button
|
|
||||||
key={link.publicUrl}
|
|
||||||
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
|
|
||||||
>
|
|
||||||
{link.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
@ -188,8 +189,8 @@ export const loadColumns = async (
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={params?.row?.id}
|
itemId={params?.row?.id}
|
||||||
pathEdit={`/enrollments/enrollments-edit/?id=${params?.row?.id}`}
|
pathEdit={`/products/products-edit/?id=${params?.row?.id}`}
|
||||||
pathView={`/enrollments/enrollments-view/?id=${params?.row?.id}`}
|
pathView={`/products/products-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -1,476 +0,0 @@
|
|||||||
import React, { useEffect, useState, useMemo } from 'react'
|
|
||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
|
||||||
import BaseButton from '../BaseButton'
|
|
||||||
import CardBoxModal from '../CardBoxModal'
|
|
||||||
import CardBox from "../CardBox";
|
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/quizzes/quizzesSlice'
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Field, Form, Formik } from "formik";
|
|
||||||
import {
|
|
||||||
DataGrid,
|
|
||||||
GridColDef,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import {loadColumns} from "./configureQuizzesCols";
|
|
||||||
import _ from 'lodash';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import {dataGridStyles} from "../../styles";
|
|
||||||
|
|
||||||
|
|
||||||
import ListQuizzes from './ListQuizzes';
|
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
|
||||||
|
|
||||||
const TableSampleQuizzes = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const pagesList = [];
|
|
||||||
const [id, setId] = useState(null);
|
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
|
||||||
const [filterRequest, setFilterRequest] = React.useState('');
|
|
||||||
const [columns, setColumns] = useState<GridColDef[]>([]);
|
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
|
||||||
const [sortModel, setSortModel] = useState([
|
|
||||||
{
|
|
||||||
field: '',
|
|
||||||
sort: 'desc',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { quizzes, loading, count, notify: quizzesNotify, refetch } = useAppSelector((state) => state.quizzes)
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
|
|
||||||
for (let i = 0; i < numPages; i++) {
|
|
||||||
pagesList.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadData = async (page = currentPage, request = filterRequest) => {
|
|
||||||
if (page !== currentPage) setCurrentPage(page);
|
|
||||||
if (request !== filterRequest) setFilterRequest(request);
|
|
||||||
const { sort, field } = sortModel[0];
|
|
||||||
|
|
||||||
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
|
|
||||||
dispatch(fetch({ limit: perPage, page, query }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (quizzesNotify.showNotification) {
|
|
||||||
notify(quizzesNotify.typeNotification, quizzesNotify.textNotification);
|
|
||||||
}
|
|
||||||
}, [quizzesNotify.showNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
loadData();
|
|
||||||
}, [sortModel, currentUser]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (refetch) {
|
|
||||||
loadData(0);
|
|
||||||
dispatch(setRefetch(false));
|
|
||||||
}
|
|
||||||
}, [refetch, dispatch]);
|
|
||||||
|
|
||||||
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
|
|
||||||
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
|
|
||||||
|
|
||||||
const handleModalAction = () => {
|
|
||||||
setIsModalInfoActive(false)
|
|
||||||
setIsModalTrashActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteModalAction = (id: string) => {
|
|
||||||
setId(id)
|
|
||||||
setIsModalTrashActive(true)
|
|
||||||
}
|
|
||||||
const handleDeleteAction = async () => {
|
|
||||||
if (id) {
|
|
||||||
await dispatch(deleteItem(id));
|
|
||||||
await loadData(0);
|
|
||||||
setIsModalTrashActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateFilterRequests = useMemo(() => {
|
|
||||||
let request = '&';
|
|
||||||
filterItems.forEach((item) => {
|
|
||||||
const isRangeFilter = filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title === item.fields.selectedField &&
|
|
||||||
(filter.number || filter.date),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRangeFilter) {
|
|
||||||
const from = item.fields.filterValueFrom;
|
|
||||||
const to = item.fields.filterValueTo;
|
|
||||||
if (from) {
|
|
||||||
request += `${item.fields.selectedField}Range=${from}&`;
|
|
||||||
}
|
|
||||||
if (to) {
|
|
||||||
request += `${item.fields.selectedField}Range=${to}&`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const value = item.fields.filterValue;
|
|
||||||
if (value) {
|
|
||||||
request += `${item.fields.selectedField}=${value}&`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return request;
|
|
||||||
}, [filterItems, filters]);
|
|
||||||
|
|
||||||
const deleteFilter = (value) => {
|
|
||||||
const newItems = filterItems.filter((item) => item.id !== value);
|
|
||||||
|
|
||||||
if (newItems.length) {
|
|
||||||
setFilterItems(newItems);
|
|
||||||
} else {
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
setFilterItems(newItems);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
loadData(0, generateFilterRequests);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (id) => (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
const name = e.target.name;
|
|
||||||
|
|
||||||
setFilterItems(
|
|
||||||
filterItems.map((item) => {
|
|
||||||
if (item.id !== id) return item;
|
|
||||||
if (name === 'selectedField') return { id, fields: { [name]: value } };
|
|
||||||
|
|
||||||
return { id, fields: { ...item.fields, [name]: value } }
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setFilterItems([]);
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
|
||||||
loadData(page);
|
|
||||||
setCurrentPage(page);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
|
|
||||||
loadColumns(
|
|
||||||
handleDeleteModalAction,
|
|
||||||
`quizzes`,
|
|
||||||
currentUser,
|
|
||||||
).then((newCols) => setColumns(newCols));
|
|
||||||
}, [currentUser]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleTableSubmit = async (id: string, data) => {
|
|
||||||
|
|
||||||
if (!_.isEmpty(data)) {
|
|
||||||
await dispatch(update({ id, data }))
|
|
||||||
.unwrap()
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteRows = async (selectedRows) => {
|
|
||||||
await dispatch(deleteItemsByIds(selectedRows));
|
|
||||||
await loadData(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
const controlClasses =
|
|
||||||
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
|
|
||||||
` ${bgColor} ${focusRing} ${corners} ` +
|
|
||||||
'dark:bg-slate-800 border';
|
|
||||||
|
|
||||||
|
|
||||||
const dataGrid = (
|
|
||||||
<div className='relative overflow-x-auto'>
|
|
||||||
<DataGrid
|
|
||||||
autoHeight
|
|
||||||
rowHeight={64}
|
|
||||||
sx={dataGridStyles}
|
|
||||||
className={'datagrid--table'}
|
|
||||||
getRowClassName={() => `datagrid--row`}
|
|
||||||
rows={quizzes ?? []}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{
|
|
||||||
pagination: {
|
|
||||||
paginationModel: {
|
|
||||||
pageSize: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
disableRowSelectionOnClick
|
|
||||||
onProcessRowUpdateError={(params) => {
|
|
||||||
console.log('Error', params);
|
|
||||||
}}
|
|
||||||
processRowUpdate={async (newRow, oldRow) => {
|
|
||||||
const data = dataFormatter.dataGridEditFormatter(newRow);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await handleTableSubmit(newRow.id, data);
|
|
||||||
return newRow;
|
|
||||||
} catch {
|
|
||||||
return oldRow;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
sortingMode={'server'}
|
|
||||||
checkboxSelection
|
|
||||||
onRowSelectionModelChange={(ids) => {
|
|
||||||
setSelectedRows(ids)
|
|
||||||
}}
|
|
||||||
onSortModelChange={(params) => {
|
|
||||||
params.length
|
|
||||||
? setSortModel(params)
|
|
||||||
: setSortModel([{ field: '', sort: 'desc' }]);
|
|
||||||
}}
|
|
||||||
rowCount={count}
|
|
||||||
pageSizeOptions={[10]}
|
|
||||||
paginationMode={'server'}
|
|
||||||
loading={loading}
|
|
||||||
onPaginationModelChange={(params) => {
|
|
||||||
onPageChange(params.page);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
|
||||||
<CardBox>
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
checkboxes: ['lorem'],
|
|
||||||
switches: ['lorem'],
|
|
||||||
radio: 'lorem',
|
|
||||||
}}
|
|
||||||
onSubmit={() => null}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<>
|
|
||||||
{filterItems && filterItems.map((filterItem) => {
|
|
||||||
return (
|
|
||||||
<div key={filterItem.id} className="flex mb-4">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Filter</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='selectedField'
|
|
||||||
id='selectedField'
|
|
||||||
component='select'
|
|
||||||
value={filterItem?.fields?.selectedField || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
{filters.map((selectOption) => (
|
|
||||||
<option
|
|
||||||
key={selectOption.title}
|
|
||||||
value={`${selectOption.title}`}
|
|
||||||
>
|
|
||||||
{selectOption.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.type === 'enum' ? (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className="text-gray-500 font-bold">
|
|
||||||
Value
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name="filterValue"
|
|
||||||
id='filterValue'
|
|
||||||
component="select"
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
<option value="">Select Value</option>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.options?.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
) : filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.number ? (
|
|
||||||
<div className="flex flex-row w-full mr-3">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">From</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className=" text-gray-500 font-bold">To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title ===
|
|
||||||
filterItem?.fields?.selectedField
|
|
||||||
)?.date ? (
|
|
||||||
<div className='flex flex-row w-full mr-3'>
|
|
||||||
<div className='flex flex-col w-full mr-3'>
|
|
||||||
<div className=' text-gray-500 font-bold'>
|
|
||||||
From
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col w-full'>
|
|
||||||
<div className=' text-gray-500 font-bold'>To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Contains</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValue'
|
|
||||||
placeholder='Contained'
|
|
||||||
id='filterValue'
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className=" text-gray-500 font-bold">Action</div>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
type='reset'
|
|
||||||
color='danger'
|
|
||||||
label='Delete'
|
|
||||||
onClick={() => {
|
|
||||||
deleteFilter(filterItem.id)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="flex">
|
|
||||||
<BaseButton
|
|
||||||
className="my-2 mr-3"
|
|
||||||
color="success"
|
|
||||||
label='Apply'
|
|
||||||
onClick={handleSubmit}
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
color='info'
|
|
||||||
label='Cancel'
|
|
||||||
onClick={handleReset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</CardBox> : null
|
|
||||||
}
|
|
||||||
<CardBoxModal
|
|
||||||
title="Please confirm"
|
|
||||||
buttonColor="info"
|
|
||||||
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
|
||||||
isActive={isModalTrashActive}
|
|
||||||
onConfirm={handleDeleteAction}
|
|
||||||
onCancel={handleModalAction}
|
|
||||||
>
|
|
||||||
<p>Are you sure you want to delete this item?</p>
|
|
||||||
</CardBoxModal>
|
|
||||||
|
|
||||||
|
|
||||||
{quizzes && Array.isArray(quizzes) && !showGrid && (
|
|
||||||
<ListQuizzes
|
|
||||||
quizzes={quizzes}
|
|
||||||
loading={loading}
|
|
||||||
onDelete={handleDeleteModalAction}
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
onPageChange={onPageChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{showGrid && dataGrid}
|
|
||||||
|
|
||||||
|
|
||||||
{selectedRows.length > 0 &&
|
|
||||||
createPortal(
|
|
||||||
<BaseButton
|
|
||||||
className='me-4'
|
|
||||||
color='danger'
|
|
||||||
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
|
||||||
onClick={() => onDeleteRows(selectedRows)}
|
|
||||||
/>,
|
|
||||||
document.getElementById('delete-rows-button'),
|
|
||||||
)}
|
|
||||||
<ToastContainer />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TableSampleQuizzes
|
|
||||||
@ -12,7 +12,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
announcements: any[];
|
shipments: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -20,8 +20,8 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardAnnouncements = ({
|
const CardShipments = ({
|
||||||
announcements,
|
shipments,
|
||||||
loading,
|
loading,
|
||||||
onDelete,
|
onDelete,
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -37,7 +37,7 @@ const CardAnnouncements = ({
|
|||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ANNOUNCEMENTS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_SHIPMENTS')
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,7 +47,7 @@ const CardAnnouncements = ({
|
|||||||
role='list'
|
role='list'
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||||
>
|
>
|
||||||
{!loading && announcements.map((item, index) => (
|
{!loading && shipments.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
||||||
@ -57,8 +57,8 @@ const CardAnnouncements = ({
|
|||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
||||||
|
|
||||||
<Link href={`/announcements/announcements-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
<Link href={`/shipments/shipments-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
||||||
{item.title}
|
{item.tracking_number}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ const CardAnnouncements = ({
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/announcements/announcements-edit/?id=${item.id}`}
|
pathEdit={`/shipments/shipments-edit/?id=${item.id}`}
|
||||||
pathView={`/announcements/announcements-view/?id=${item.id}`}
|
pathView={`/shipments/shipments-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -78,10 +78,10 @@ const CardAnnouncements = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Course</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Order</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.coursesOneListFormatter(item.course) }
|
{ dataFormatter.ordersOneListFormatter(item.order) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -90,10 +90,10 @@ const CardAnnouncements = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Title</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>Carrier</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.title }
|
{ item.carrier }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -102,10 +102,10 @@ const CardAnnouncements = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Content</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>TrackingNumber</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ item.content }
|
{ item.tracking_number }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -114,10 +114,10 @@ const CardAnnouncements = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>PublishedAt</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>ShippedAt</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.dateTimeFormatter(item.published_at) }
|
{ dataFormatter.dateTimeFormatter(item.shipped_at) }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -126,10 +126,22 @@ const CardAnnouncements = ({
|
|||||||
|
|
||||||
|
|
||||||
<div className='flex justify-between gap-x-4 py-3'>
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
<dt className=' text-gray-500 dark:text-dark-600'>Active</dt>
|
<dt className=' text-gray-500 dark:text-dark-600'>DeliveredAt</dt>
|
||||||
<dd className='flex items-start gap-x-2'>
|
<dd className='flex items-start gap-x-2'>
|
||||||
<div className='font-medium line-clamp-4'>
|
<div className='font-medium line-clamp-4'>
|
||||||
{ dataFormatter.booleanFormatter(item.is_active) }
|
{ dataFormatter.dateTimeFormatter(item.delivered_at) }
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='flex justify-between gap-x-4 py-3'>
|
||||||
|
<dt className=' text-gray-500 dark:text-dark-600'>Status</dt>
|
||||||
|
<dd className='flex items-start gap-x-2'>
|
||||||
|
<div className='font-medium line-clamp-4'>
|
||||||
|
{ item.status }
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -139,7 +151,7 @@ const CardAnnouncements = ({
|
|||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{!loading && announcements.length === 0 && (
|
{!loading && shipments.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -156,4 +168,4 @@ const CardAnnouncements = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardAnnouncements;
|
export default CardShipments;
|
||||||
@ -13,7 +13,7 @@ import {hasPermission} from "../../helpers/userPermissions";
|
|||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
quiz_questions: any[];
|
shipments: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@ -21,10 +21,10 @@ type Props = {
|
|||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListQuiz_questions = ({ quiz_questions, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
const ListShipments = ({ shipments, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_QUIZ_QUESTIONS')
|
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_SHIPMENTS')
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
||||||
@ -34,13 +34,13 @@ const ListQuiz_questions = ({ quiz_questions, loading, onDelete, currentPage, nu
|
|||||||
<>
|
<>
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||||
{loading && <LoadingSpinner />}
|
{loading && <LoadingSpinner />}
|
||||||
{!loading && quiz_questions.map((item) => (
|
{!loading && shipments.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/quiz_questions/quiz_questions-view/?id=${item.id}`}
|
href={`/shipments/shipments-view/?id=${item.id}`}
|
||||||
className={
|
className={
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
||||||
}
|
}
|
||||||
@ -48,40 +48,48 @@ const ListQuiz_questions = ({ quiz_questions, loading, onDelete, currentPage, nu
|
|||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Quiz</p>
|
<p className={'text-xs text-gray-500 '}>Order</p>
|
||||||
<p className={'line-clamp-2'}>{ dataFormatter.quizzesOneListFormatter(item.quiz) }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.ordersOneListFormatter(item.order) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Question</p>
|
<p className={'text-xs text-gray-500 '}>Carrier</p>
|
||||||
<p className={'line-clamp-2'}>{ item.question }</p>
|
<p className={'line-clamp-2'}>{ item.carrier }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>QuestionType</p>
|
<p className={'text-xs text-gray-500 '}>TrackingNumber</p>
|
||||||
<p className={'line-clamp-2'}>{ item.question_type }</p>
|
<p className={'line-clamp-2'}>{ item.tracking_number }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Choices</p>
|
<p className={'text-xs text-gray-500 '}>ShippedAt</p>
|
||||||
<p className={'line-clamp-2'}>{ item.choices }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.shipped_at) }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={'flex-1 px-3'}>
|
<div className={'flex-1 px-3'}>
|
||||||
<p className={'text-xs text-gray-500 '}>Answer</p>
|
<p className={'text-xs text-gray-500 '}>DeliveredAt</p>
|
||||||
<p className={'line-clamp-2'}>{ item.answer }</p>
|
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.delivered_at) }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className={'flex-1 px-3'}>
|
||||||
|
<p className={'text-xs text-gray-500 '}>Status</p>
|
||||||
|
<p className={'line-clamp-2'}>{ item.status }</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -90,8 +98,8 @@ const ListQuiz_questions = ({ quiz_questions, loading, onDelete, currentPage, nu
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={item.id}
|
itemId={item.id}
|
||||||
pathEdit={`/quiz_questions/quiz_questions-edit/?id=${item.id}`}
|
pathEdit={`/shipments/shipments-edit/?id=${item.id}`}
|
||||||
pathView={`/quiz_questions/quiz_questions-view/?id=${item.id}`}
|
pathView={`/shipments/shipments-view/?id=${item.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
@ -100,7 +108,7 @@ const ListQuiz_questions = ({ quiz_questions, loading, onDelete, currentPage, nu
|
|||||||
</CardBox>
|
</CardBox>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!loading && quiz_questions.length === 0 && (
|
{!loading && shipments.length === 0 && (
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
<div className='col-span-full flex items-center justify-center h-40'>
|
||||||
<p className=''>No data to display</p>
|
<p className=''>No data to display</p>
|
||||||
</div>
|
</div>
|
||||||
@ -117,4 +125,4 @@ const ListQuiz_questions = ({ quiz_questions, loading, onDelete, currentPage, nu
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListQuiz_questions
|
export default ListShipments
|
||||||
@ -4,7 +4,7 @@ import { ToastContainer, toast } from 'react-toastify';
|
|||||||
import BaseButton from '../BaseButton'
|
import BaseButton from '../BaseButton'
|
||||||
import CardBoxModal from '../CardBoxModal'
|
import CardBoxModal from '../CardBoxModal'
|
||||||
import CardBox from "../CardBox";
|
import CardBox from "../CardBox";
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/lessons/lessonsSlice'
|
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/shipments/shipmentsSlice'
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { Field, Form, Formik } from "formik";
|
import { Field, Form, Formik } from "formik";
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
DataGrid,
|
DataGrid,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import {loadColumns} from "./configureLessonsCols";
|
import {loadColumns} from "./configureShipmentsCols";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
import {dataGridStyles} from "../../styles";
|
import {dataGridStyles} from "../../styles";
|
||||||
@ -24,7 +24,7 @@ import { SlotInfo } from 'react-big-calendar';
|
|||||||
|
|
||||||
const perPage = 100
|
const perPage = 100
|
||||||
|
|
||||||
const TableSampleLessons = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
const TableSampleShipments = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -43,7 +43,7 @@ const TableSampleLessons = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { lessons, loading, count, notify: lessonsNotify, refetch } = useAppSelector((state) => state.lessons)
|
const { shipments, loading, count, notify: shipmentsNotify, refetch } = useAppSelector((state) => state.shipments)
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
@ -63,10 +63,10 @@ const TableSampleLessons = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lessonsNotify.showNotification) {
|
if (shipmentsNotify.showNotification) {
|
||||||
notify(lessonsNotify.typeNotification, lessonsNotify.textNotification);
|
notify(shipmentsNotify.typeNotification, shipmentsNotify.textNotification);
|
||||||
}
|
}
|
||||||
}, [lessonsNotify.showNotification]);
|
}, [shipmentsNotify.showNotification]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
@ -93,7 +93,7 @@ const TableSampleLessons = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
|
|
||||||
const handleCreateEventAction = ({ start, end }: SlotInfo) => {
|
const handleCreateEventAction = ({ start, end }: SlotInfo) => {
|
||||||
router.push(
|
router.push(
|
||||||
`/lessons/lessons-new?dateRangeStart=${start.toISOString()}&dateRangeEnd=${end.toISOString()}`,
|
`/shipments/shipments-new?dateRangeStart=${start.toISOString()}&dateRangeEnd=${end.toISOString()}`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ const TableSampleLessons = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
|
|
||||||
loadColumns(
|
loadColumns(
|
||||||
handleDeleteModalAction,
|
handleDeleteModalAction,
|
||||||
`lessons`,
|
`shipments`,
|
||||||
currentUser,
|
currentUser,
|
||||||
).then((newCols) => setColumns(newCols));
|
).then((newCols) => setColumns(newCols));
|
||||||
}, [currentUser]);
|
}, [currentUser]);
|
||||||
@ -224,7 +224,7 @@ const TableSampleLessons = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
sx={dataGridStyles}
|
sx={dataGridStyles}
|
||||||
className={'datagrid--table'}
|
className={'datagrid--table'}
|
||||||
getRowClassName={() => `datagrid--row`}
|
getRowClassName={() => `datagrid--row`}
|
||||||
rows={lessons ?? []}
|
rows={shipments ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -451,18 +451,18 @@ const TableSampleLessons = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
|
|
||||||
{!showGrid && (
|
{!showGrid && (
|
||||||
<BigCalendar
|
<BigCalendar
|
||||||
events={lessons}
|
events={shipments}
|
||||||
showField={'title'}
|
showField={'tracking_number'}
|
||||||
start-data-key={'start_at'}
|
start-data-key={'shipped_at'}
|
||||||
end-data-key={'end_at'}
|
end-data-key={'delivered_at'}
|
||||||
handleDeleteAction={handleDeleteModalAction}
|
handleDeleteAction={handleDeleteModalAction}
|
||||||
pathEdit={`/lessons/lessons-edit/?id=`}
|
pathEdit={`/shipments/shipments-edit/?id=`}
|
||||||
pathView={`/lessons/lessons-view/?id=`}
|
pathView={`/shipments/shipments-view/?id=`}
|
||||||
handleCreateEventAction={handleCreateEventAction}
|
handleCreateEventAction={handleCreateEventAction}
|
||||||
onDateRangeChange={(range) => {
|
onDateRangeChange={(range) => {
|
||||||
loadData(0,`&calendarStart=${range.start}&calendarEnd=${range.end}`);
|
loadData(0,`&calendarStart=${range.start}&calendarEnd=${range.end}`);
|
||||||
}}
|
}}
|
||||||
entityName={'lessons'}
|
entityName={'shipments'}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -486,4 +486,4 @@ const TableSampleLessons = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TableSampleLessons
|
export default TableSampleShipments
|
||||||
@ -37,13 +37,13 @@ export const loadColumns = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_ANNOUNCEMENTS')
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_SHIPMENTS')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'course',
|
field: 'order',
|
||||||
headerName: 'Course',
|
headerName: 'Order',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -57,15 +57,15 @@ export const loadColumns = async (
|
|||||||
type: 'singleSelect',
|
type: 'singleSelect',
|
||||||
getOptionValue: (value: any) => value?.id,
|
getOptionValue: (value: any) => value?.id,
|
||||||
getOptionLabel: (value: any) => value?.label,
|
getOptionLabel: (value: any) => value?.label,
|
||||||
valueOptions: await callOptionsApi('courses'),
|
valueOptions: await callOptionsApi('orders'),
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
params?.value?.id ?? params?.value,
|
params?.value?.id ?? params?.value,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'title',
|
field: 'carrier',
|
||||||
headerName: 'Title',
|
headerName: 'Carrier',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -79,8 +79,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'content',
|
field: 'tracking_number',
|
||||||
headerName: 'Content',
|
headerName: 'TrackingNumber',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -94,8 +94,8 @@ export const loadColumns = async (
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'published_at',
|
field: 'shipped_at',
|
||||||
headerName: 'PublishedAt',
|
headerName: 'ShippedAt',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -107,13 +107,13 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
new Date(params.row.published_at),
|
new Date(params.row.shipped_at),
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'is_active',
|
field: 'delivered_at',
|
||||||
headerName: 'Active',
|
headerName: 'DeliveredAt',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
@ -123,10 +123,27 @@ export const loadColumns = async (
|
|||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
type: 'boolean',
|
type: 'dateTime',
|
||||||
|
valueGetter: (params: GridValueGetterParams) =>
|
||||||
|
new Date(params.row.delivered_at),
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
headerName: 'Status',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 120,
|
||||||
|
filterable: false,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
@ -140,8 +157,8 @@ export const loadColumns = async (
|
|||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
itemId={params?.row?.id}
|
itemId={params?.row?.id}
|
||||||
pathEdit={`/announcements/announcements-edit/?id=${params?.row?.id}`}
|
pathEdit={`/shipments/shipments-edit/?id=${params?.row?.id}`}
|
||||||
pathView={`/announcements/announcements-view/?id=${params?.row?.id}`}
|
pathView={`/shipments/shipments-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user