From df7bd1416a6befca497d3324130a6a373636f7d4 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 14 Apr 2025 00:44:22 +0000 Subject: [PATCH] Revert to version 14891ad --- .gitignore | 5 - app-shell/src/_schema.json | 7 +- backend/src/db/api/likes.js | 235 --------- backend/src/db/migrations/1744591367307.js | 72 --- backend/src/db/models/likes.js | 45 -- .../db/seeders/20200430130760-user-roles.js | 26 - .../db/seeders/20231127130745-sample-data.js | 24 +- backend/src/db/seeders/20250414004247.js | 87 ---- backend/src/index.js | 8 - backend/src/routes/likes.js | 429 ---------------- backend/src/services/likes.js | 114 ----- frontend/json/runtimeError.json | 1 - frontend/src/components/Likes/CardLikes.tsx | 98 ---- frontend/src/components/Likes/ListLikes.tsx | 85 ---- frontend/src/components/Likes/TableLikes.tsx | 481 ------------------ .../components/Likes/configureLikesCols.tsx | 61 --- .../components/WebPageComponents/Footer.tsx | 2 +- frontend/src/menuAside.ts | 8 - frontend/src/pages/dashboard.tsx | 35 -- frontend/src/pages/likes/[likesId].tsx | 118 ----- frontend/src/pages/likes/likes-edit.tsx | 116 ----- frontend/src/pages/likes/likes-list.tsx | 160 ------ frontend/src/pages/likes/likes-new.tsx | 92 ---- frontend/src/pages/likes/likes-table.tsx | 159 ------ frontend/src/pages/likes/likes-view.tsx | 76 --- frontend/src/pages/web_pages/home.tsx | 2 +- frontend/src/stores/likes/likesSlice.ts | 236 --------- frontend/src/stores/store.ts | 2 - 28 files changed, 14 insertions(+), 2770 deletions(-) delete mode 100644 backend/src/db/api/likes.js delete mode 100644 backend/src/db/migrations/1744591367307.js delete mode 100644 backend/src/db/models/likes.js delete mode 100644 backend/src/db/seeders/20250414004247.js delete mode 100644 backend/src/routes/likes.js delete mode 100644 backend/src/services/likes.js delete mode 100644 frontend/json/runtimeError.json delete mode 100644 frontend/src/components/Likes/CardLikes.tsx delete mode 100644 frontend/src/components/Likes/ListLikes.tsx delete mode 100644 frontend/src/components/Likes/TableLikes.tsx delete mode 100644 frontend/src/components/Likes/configureLikesCols.tsx delete mode 100644 frontend/src/pages/likes/[likesId].tsx delete mode 100644 frontend/src/pages/likes/likes-edit.tsx delete mode 100644 frontend/src/pages/likes/likes-list.tsx delete mode 100644 frontend/src/pages/likes/likes-new.tsx delete mode 100644 frontend/src/pages/likes/likes-table.tsx delete mode 100644 frontend/src/pages/likes/likes-view.tsx delete mode 100644 frontend/src/stores/likes/likesSlice.ts diff --git a/.gitignore b/.gitignore index d0eb167..e427ff3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,3 @@ node_modules/ */node_modules/ */build/ - -**/node_modules/ -**/build/ -.DS_Store -.env \ No newline at end of file diff --git a/app-shell/src/_schema.json b/app-shell/src/_schema.json index be4333a..f7ee5bd 100644 --- a/app-shell/src/_schema.json +++ b/app-shell/src/_schema.json @@ -1,4 +1,5 @@ + + { - "Initial version": "{\"iv\":\"ZCa/jm0ca5ufZFul\",\"encryptedData\":\"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\"}", - "likes": "{\"iv\":\"UqrfEPv7BVatWn/8\",\"encryptedData\":\"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\"}" -} \ No newline at end of file + "Initial version": "{\"iv\":\"ZCa/jm0ca5ufZFul\",\"encryptedData\":\"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\"}" +} diff --git a/backend/src/db/api/likes.js b/backend/src/db/api/likes.js deleted file mode 100644 index 6705688..0000000 --- a/backend/src/db/api/likes.js +++ /dev/null @@ -1,235 +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 LikesDBApi { - static async create(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const likes = await db.likes.create( - { - id: data.id || undefined, - - importHash: data.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - }, - { transaction }, - ); - - return likes; - } - - 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 likesData = data.map((item, index) => ({ - id: item.id || undefined, - - importHash: item.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - createdAt: new Date(Date.now() + index * 1000), - })); - - // Bulk create items - const likes = await db.likes.bulkCreate(likesData, { transaction }); - - // For each item created, replace relation files - - return likes; - } - - static async update(id, data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const likes = await db.likes.findByPk(id, {}, { transaction }); - - const updatePayload = {}; - - updatePayload.updatedById = currentUser.id; - - await likes.update(updatePayload, { transaction }); - - return likes; - } - - static async deleteByIds(ids, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const likes = await db.likes.findAll({ - where: { - id: { - [Op.in]: ids, - }, - }, - transaction, - }); - - await db.sequelize.transaction(async (transaction) => { - for (const record of likes) { - await record.update({ deletedBy: currentUser.id }, { transaction }); - } - for (const record of likes) { - await record.destroy({ transaction }); - } - }); - - return likes; - } - - static async remove(id, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const likes = await db.likes.findByPk(id, options); - - await likes.update( - { - deletedBy: currentUser.id, - }, - { - transaction, - }, - ); - - await likes.destroy({ - transaction, - }); - - return likes; - } - - static async findBy(where, options) { - const transaction = (options && options.transaction) || undefined; - - const likes = await db.likes.findOne({ where }, { transaction }); - - if (!likes) { - return likes; - } - - const output = likes.get({ plain: true }); - - 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 = []; - - if (filter) { - if (filter.id) { - where = { - ...where, - ['id']: Utils.uuid(filter.id), - }; - } - - 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.likes.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('likes', 'id', query), - ], - }; - } - - const records = await db.likes.findAll({ - attributes: ['id', 'id'], - where, - limit: limit ? Number(limit) : undefined, - offset: offset ? Number(offset) : undefined, - orderBy: [['id', 'ASC']], - }); - - return records.map((record) => ({ - id: record.id, - label: record.id, - })); - } -}; diff --git a/backend/src/db/migrations/1744591367307.js b/backend/src/db/migrations/1744591367307.js deleted file mode 100644 index 3ab60dc..0000000 --- a/backend/src/db/migrations/1744591367307.js +++ /dev/null @@ -1,72 +0,0 @@ -module.exports = { - /** - * @param {QueryInterface} queryInterface - * @param {Sequelize} Sequelize - * @returns {Promise} - */ - async up(queryInterface, Sequelize) { - /** - * @type {Transaction} - */ - const transaction = await queryInterface.sequelize.transaction(); - try { - await queryInterface.createTable( - 'likes', - { - id: { - type: Sequelize.DataTypes.UUID, - defaultValue: Sequelize.DataTypes.UUIDV4, - primaryKey: true, - }, - createdById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - updatedById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - createdAt: { type: Sequelize.DataTypes.DATE }, - updatedAt: { type: Sequelize.DataTypes.DATE }, - deletedAt: { type: Sequelize.DataTypes.DATE }, - importHash: { - type: Sequelize.DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, - { transaction }, - ); - - await transaction.commit(); - } catch (err) { - await transaction.rollback(); - throw err; - } - }, - /** - * @param {QueryInterface} queryInterface - * @param {Sequelize} Sequelize - * @returns {Promise} - */ - async down(queryInterface, Sequelize) { - /** - * @type {Transaction} - */ - const transaction = await queryInterface.sequelize.transaction(); - try { - await queryInterface.dropTable('likes', { transaction }); - - await transaction.commit(); - } catch (err) { - await transaction.rollback(); - throw err; - } - }, -}; diff --git a/backend/src/db/models/likes.js b/backend/src/db/models/likes.js deleted file mode 100644 index 884d1ba..0000000 --- a/backend/src/db/models/likes.js +++ /dev/null @@ -1,45 +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 likes = sequelize.define( - 'likes', - { - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true, - }, - - importHash: { - type: DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, - { - timestamps: true, - paranoid: true, - freezeTableName: true, - }, - ); - - likes.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.likes.belongsTo(db.users, { - as: 'createdBy', - }); - - db.likes.belongsTo(db.users, { - as: 'updatedBy', - }); - }; - - return likes; -}; diff --git a/backend/src/db/seeders/20200430130760-user-roles.js b/backend/src/db/seeders/20200430130760-user-roles.js index 96f97b6..54b0cb5 100644 --- a/backend/src/db/seeders/20200430130760-user-roles.js +++ b/backend/src/db/seeders/20200430130760-user-roles.js @@ -101,7 +101,6 @@ module.exports = { 'students', 'roles', 'permissions', - 'likes', , ]; await queryInterface.bulkInsert( @@ -949,31 +948,6 @@ primary key ("roles_permissionsId", "permissionId") permissionId: getId('DELETE_PERMISSIONS'), }, - { - createdAt, - updatedAt, - roles_permissionsId: getId('Administrator'), - permissionId: getId('CREATE_LIKES'), - }, - { - createdAt, - updatedAt, - roles_permissionsId: getId('Administrator'), - permissionId: getId('READ_LIKES'), - }, - { - createdAt, - updatedAt, - roles_permissionsId: getId('Administrator'), - permissionId: getId('UPDATE_LIKES'), - }, - { - createdAt, - updatedAt, - roles_permissionsId: getId('Administrator'), - permissionId: getId('DELETE_LIKES'), - }, - { createdAt, updatedAt, diff --git a/backend/src/db/seeders/20231127130745-sample-data.js b/backend/src/db/seeders/20231127130745-sample-data.js index e9630ab..6b28e66 100644 --- a/backend/src/db/seeders/20231127130745-sample-data.js +++ b/backend/src/db/seeders/20231127130745-sample-data.js @@ -13,8 +13,6 @@ const Instructors = db.instructors; const Students = db.students; -const Likes = db.likes; - const AnalyticsData = [ { // type code here for "relation_many" field @@ -94,6 +92,14 @@ const DiscussionBoardsData = [ ]; const EnrollmentsData = [ + { + // type code here for "relation_one" field + + // type code here for "relation_one" field + + payment_status: 'overdue', + }, + { // type code here for "relation_one" field @@ -109,14 +115,6 @@ const EnrollmentsData = [ payment_status: 'pending', }, - - { - // type code here for "relation_one" field - - // type code here for "relation_one" field - - payment_status: 'overdue', - }, ]; const InstructorsData = [ @@ -177,8 +175,6 @@ const StudentsData = [ }, ]; -const LikesData = [{}, {}, {}]; - // Similar logic for "relation_many" // Similar logic for "relation_many" @@ -316,8 +312,6 @@ module.exports = { await Students.bulkCreate(StudentsData); - await Likes.bulkCreate(LikesData); - await Promise.all([ // Similar logic for "relation_many" @@ -357,7 +351,5 @@ module.exports = { await queryInterface.bulkDelete('instructors', null, {}); await queryInterface.bulkDelete('students', null, {}); - - await queryInterface.bulkDelete('likes', null, {}); }, }; diff --git a/backend/src/db/seeders/20250414004247.js b/backend/src/db/seeders/20250414004247.js deleted file mode 100644 index ddd1a6e..0000000 --- a/backend/src/db/seeders/20250414004247.js +++ /dev/null @@ -1,87 +0,0 @@ -const { v4: uuid } = require('uuid'); -const db = require('../models'); -const Sequelize = require('sequelize'); -const config = require('../../config'); - -module.exports = { - /** - * @param{import("sequelize").QueryInterface} queryInterface - * @return {Promise} - */ - async up(queryInterface) { - const createdAt = new Date(); - const updatedAt = new Date(); - - /** @type {Map} */ - const idMap = new Map(); - - /** - * @param {string} key - * @return {string} - */ - function getId(key) { - if (idMap.has(key)) { - return idMap.get(key); - } - const id = uuid(); - idMap.set(key, id); - return id; - } - - /** - * @param {string} name - */ - function createPermissions(name) { - return [ - { - id: getId(`CREATE_${name.toUpperCase()}`), - createdAt, - updatedAt, - name: `CREATE_${name.toUpperCase()}`, - }, - { - id: getId(`READ_${name.toUpperCase()}`), - createdAt, - updatedAt, - name: `READ_${name.toUpperCase()}`, - }, - { - id: getId(`UPDATE_${name.toUpperCase()}`), - createdAt, - updatedAt, - name: `UPDATE_${name.toUpperCase()}`, - }, - { - id: getId(`DELETE_${name.toUpperCase()}`), - createdAt, - updatedAt, - name: `DELETE_${name.toUpperCase()}`, - }, - ]; - } - - const entities = ['likes']; - - const createdPermissions = entities.flatMap(createPermissions); - - // Add permissions to database - await queryInterface.bulkInsert('permissions', createdPermissions); - // Get permissions ids - const permissionsIds = createdPermissions.map((p) => p.id); - // Get admin role - const adminRole = await db.roles.findOne({ - where: { name: config.roles.admin }, - }); - - if (adminRole) { - // Add permissions to admin role if it exists - await adminRole.addPermissions(permissionsIds); - } - }, - down: async (queryInterface, Sequelize) => { - await queryInterface.bulkDelete( - 'permissions', - entities.flatMap(createPermissions), - ); - }, -}; diff --git a/backend/src/index.js b/backend/src/index.js index 48a50a6..c553b7e 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -37,8 +37,6 @@ const rolesRoutes = require('./routes/roles'); const permissionsRoutes = require('./routes/permissions'); -const likesRoutes = require('./routes/likes'); - const getBaseUrl = (url) => { if (!url) return ''; return url.endsWith('/api') ? url.slice(0, -4) : url; @@ -158,12 +156,6 @@ app.use( permissionsRoutes, ); -app.use( - '/api/likes', - passport.authenticate('jwt', { session: false }), - likesRoutes, -); - app.use( '/api/openai', passport.authenticate('jwt', { session: false }), diff --git a/backend/src/routes/likes.js b/backend/src/routes/likes.js deleted file mode 100644 index ed6c9b6..0000000 --- a/backend/src/routes/likes.js +++ /dev/null @@ -1,429 +0,0 @@ -const express = require('express'); - -const LikesService = require('../services/likes'); -const LikesDBApi = require('../db/api/likes'); -const wrapAsync = require('../helpers').wrapAsync; - -const router = express.Router(); - -const { parse } = require('json2csv'); - -const { checkCrudPermissions } = require('../middlewares/check-permissions'); - -router.use(checkCrudPermissions('likes')); - -/** - * @swagger - * components: - * schemas: - * Likes: - * type: object - * properties: - - */ - -/** - * @swagger - * tags: - * name: Likes - * description: The Likes managing API - */ - -/** - * @swagger - * /api/likes: - * post: - * security: - * - bearerAuth: [] - * tags: [Likes] - * 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/Likes" - * responses: - * 200: - * description: The item was successfully added - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Likes" - * 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 LikesService.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: [Likes] - * 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/Likes" - * responses: - * 200: - * description: The items were successfully imported - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Likes" - * 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 LikesService.bulkImport(req, res, true, link.host); - const payload = true; - res.status(200).send(payload); - }), -); - -/** - * @swagger - * /api/likes/{id}: - * put: - * security: - * - bearerAuth: [] - * tags: [Likes] - * 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/Likes" - * required: - * - id - * responses: - * 200: - * description: The item data was successfully updated - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Likes" - * 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 LikesService.update(req.body.data, req.body.id, req.currentUser); - const payload = true; - res.status(200).send(payload); - }), -); - -/** - * @swagger - * /api/likes/{id}: - * delete: - * security: - * - bearerAuth: [] - * tags: [Likes] - * 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/Likes" - * 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 LikesService.remove(req.params.id, req.currentUser); - const payload = true; - res.status(200).send(payload); - }), -); - -/** - * @swagger - * /api/likes/deleteByIds: - * post: - * security: - * - bearerAuth: [] - * tags: [Likes] - * 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/Likes" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Items not found - * 500: - * description: Some server error - */ -router.post( - '/deleteByIds', - wrapAsync(async (req, res) => { - await LikesService.deleteByIds(req.body.data, req.currentUser); - const payload = true; - res.status(200).send(payload); - }), -); - -/** - * @swagger - * /api/likes: - * get: - * security: - * - bearerAuth: [] - * tags: [Likes] - * summary: Get all likes - * description: Get all likes - * responses: - * 200: - * description: Likes list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Likes" - * 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 LikesDBApi.findAll(req.query, { currentUser }); - if (filetype && filetype === 'csv') { - const fields = ['id']; - 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/likes/count: - * get: - * security: - * - bearerAuth: [] - * tags: [Likes] - * summary: Count all likes - * description: Count all likes - * responses: - * 200: - * description: Likes count successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Likes" - * 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 LikesDBApi.findAll(req.query, null, { - countOnly: true, - currentUser, - }); - - res.status(200).send(payload); - }), -); - -/** - * @swagger - * /api/likes/autocomplete: - * get: - * security: - * - bearerAuth: [] - * tags: [Likes] - * summary: Find all likes that match search criteria - * description: Find all likes that match search criteria - * responses: - * 200: - * description: Likes list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Likes" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/autocomplete', async (req, res) => { - const payload = await LikesDBApi.findAllAutocomplete( - req.query.query, - req.query.limit, - req.query.offset, - ); - - res.status(200).send(payload); -}); - -/** - * @swagger - * /api/likes/{id}: - * get: - * security: - * - bearerAuth: [] - * tags: [Likes] - * 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/Likes" - * 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 LikesDBApi.findBy({ id: req.params.id }); - - res.status(200).send(payload); - }), -); - -router.use('/', require('../helpers').commonErrorHandler); - -module.exports = router; diff --git a/backend/src/services/likes.js b/backend/src/services/likes.js deleted file mode 100644 index d8f6f3e..0000000 --- a/backend/src/services/likes.js +++ /dev/null @@ -1,114 +0,0 @@ -const db = require('../db/models'); -const LikesDBApi = require('../db/api/likes'); -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 LikesService { - static async create(data, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - await LikesDBApi.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 LikesDBApi.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 likes = await LikesDBApi.findBy({ id }, { transaction }); - - if (!likes) { - throw new ValidationError('likesNotFound'); - } - - const updatedLikes = await LikesDBApi.update(id, data, { - currentUser, - transaction, - }); - - await transaction.commit(); - return updatedLikes; - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async deleteByIds(ids, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await LikesDBApi.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 LikesDBApi.remove(id, { - currentUser, - transaction, - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } -}; diff --git a/frontend/json/runtimeError.json b/frontend/json/runtimeError.json deleted file mode 100644 index 9e26dfe..0000000 --- a/frontend/json/runtimeError.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/frontend/src/components/Likes/CardLikes.tsx b/frontend/src/components/Likes/CardLikes.tsx deleted file mode 100644 index 92fbddc..0000000 --- a/frontend/src/components/Likes/CardLikes.tsx +++ /dev/null @@ -1,98 +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 = { - likes: any[]; - loading: boolean; - onDelete: (id: string) => void; - currentPage: number; - numPages: number; - onPageChange: (page: number) => void; -}; - -const CardLikes = ({ - likes, - 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_LIKES'); - - return ( -
- {loading && } -
    - {!loading && - likes.map((item, index) => ( -
  • -
    - - {item.id} - - -
    - -
    -
    -
    -
  • - ))} - {!loading && likes.length === 0 && ( -
    -

    No data to display

    -
    - )} -
-
- -
-
- ); -}; - -export default CardLikes; diff --git a/frontend/src/components/Likes/ListLikes.tsx b/frontend/src/components/Likes/ListLikes.tsx deleted file mode 100644 index 3c31b6b..0000000 --- a/frontend/src/components/Likes/ListLikes.tsx +++ /dev/null @@ -1,85 +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 = { - likes: any[]; - loading: boolean; - onDelete: (id: string) => void; - currentPage: number; - numPages: number; - onPageChange: (page: number) => void; -}; - -const ListLikes = ({ - likes, - loading, - onDelete, - currentPage, - numPages, - onPageChange, -}: Props) => { - const currentUser = useAppSelector((state) => state.auth.currentUser); - const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_LIKES'); - - const corners = useAppSelector((state) => state.style.corners); - const bgColor = useAppSelector((state) => state.style.cardsColor); - - return ( - <> -
- {loading && } - {!loading && - likes.map((item) => ( - -
- dark:divide-dark-700 overflow-x-auto' - } - > - -
-
- ))} - {!loading && likes.length === 0 && ( -
-

No data to display

-
- )} -
-
- -
- - ); -}; - -export default ListLikes; diff --git a/frontend/src/components/Likes/TableLikes.tsx b/frontend/src/components/Likes/TableLikes.tsx deleted file mode 100644 index e544926..0000000 --- a/frontend/src/components/Likes/TableLikes.tsx +++ /dev/null @@ -1,481 +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/likes/likesSlice'; -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 './configureLikesCols'; -import _ from 'lodash'; -import dataFormatter from '../../helpers/dataFormatter'; -import { dataGridStyles } from '../../styles'; - -const perPage = 10; - -const TableSampleLikes = ({ - 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([]); - const [selectedRows, setSelectedRows] = useState([]); - const [sortModel, setSortModel] = useState([ - { - field: '', - sort: 'desc', - }, - ]); - - const { - likes, - loading, - count, - notify: likesNotify, - refetch, - } = useAppSelector((state) => state.likes); - 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 (likesNotify.showNotification) { - notify(likesNotify.typeNotification, likesNotify.textNotification); - } - }, [likesNotify.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, `likes`, 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 = ( -
- `datagrid--row`} - rows={likes ?? []} - 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); - }} - /> -
- ); - - return ( - <> - {filterItems && Array.isArray(filterItems) && filterItems.length ? ( - - null} - > -
- <> - {filterItems && - filterItems.map((filterItem) => { - return ( -
-
-
- Filter -
- - {filters.map((selectOption) => ( - - ))} - -
- {filters.find( - (filter) => - filter.title === filterItem?.fields?.selectedField, - )?.type === 'enum' ? ( -
-
Value
- - - {filters - .find( - (filter) => - filter.title === - filterItem?.fields?.selectedField, - ) - ?.options?.map((option) => ( - - ))} - -
- ) : filters.find( - (filter) => - filter.title === - filterItem?.fields?.selectedField, - )?.number ? ( -
-
-
- From -
- -
-
-
- To -
- -
-
- ) : filters.find( - (filter) => - filter.title === - filterItem?.fields?.selectedField, - )?.date ? ( -
-
-
- From -
- -
-
-
- To -
- -
-
- ) : ( -
-
- Contains -
- -
- )} -
-
- Action -
- { - deleteFilter(filterItem.id); - }} - /> -
-
- ); - })} -
- - -
- -
-
-
- ) : null} - -

Are you sure you want to delete this item?

-
- - {dataGrid} - - {selectedRows.length > 0 && - createPortal( - onDeleteRows(selectedRows)} - />, - document.getElementById('delete-rows-button'), - )} - - - ); -}; - -export default TableSampleLikes; diff --git a/frontend/src/components/Likes/configureLikesCols.tsx b/frontend/src/components/Likes/configureLikesCols.tsx deleted file mode 100644 index 1e16b07..0000000 --- a/frontend/src/components/Likes/configureLikesCols.tsx +++ /dev/null @@ -1,61 +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_LIKES'); - - return [ - { - field: 'actions', - type: 'actions', - minWidth: 30, - headerClassName: 'datagrid--header', - cellClassName: 'datagrid--cell', - getActions: (params: GridRowParams) => { - return [ - , - ]; - }, - }, - ]; -}; diff --git a/frontend/src/components/WebPageComponents/Footer.tsx b/frontend/src/components/WebPageComponents/Footer.tsx index 3833078..081158b 100644 --- a/frontend/src/components/WebPageComponents/Footer.tsx +++ b/frontend/src/components/WebPageComponents/Footer.tsx @@ -20,7 +20,7 @@ export default function WebSiteFooter({ const style = FooterStyle.WITH_PROJECT_NAME; - const design = FooterDesigns.DEFAULT_DESIGN; + const design = FooterDesigns.DESIGN_DIVERSITY; return (
{ const [students, setStudents] = React.useState('Loading...'); const [roles, setRoles] = React.useState('Loading...'); const [permissions, setPermissions] = React.useState('Loading...'); - const [likes, setLikes] = React.useState('Loading...'); const [widgetsRole, setWidgetsRole] = React.useState({ role: { value: '', label: '' }, @@ -53,7 +52,6 @@ const Dashboard = () => { 'students', 'roles', 'permissions', - 'likes', ]; const fns = [ setUsers, @@ -65,7 +63,6 @@ const Dashboard = () => { setStudents, setRoles, setPermissions, - setLikes, ]; const requests = entities.map((entity, index) => { @@ -461,38 +458,6 @@ const Dashboard = () => {
)} - - {hasPermission(currentUser, 'READ_LIKES') && ( - -
-
-
-
- Likes -
-
- {likes} -
-
-
- -
-
-
- - )} diff --git a/frontend/src/pages/likes/[likesId].tsx b/frontend/src/pages/likes/[likesId].tsx deleted file mode 100644 index acc8b15..0000000 --- a/frontend/src/pages/likes/[likesId].tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { mdiChartTimelineVariant, mdiUpload } from '@mdi/js'; -import Head from 'next/head'; -import React, { ReactElement, useEffect, useState } from 'react'; -import DatePicker from 'react-datepicker'; -import 'react-datepicker/dist/react-datepicker.css'; -import dayjs from 'dayjs'; - -import CardBox from '../../components/CardBox'; -import LayoutAuthenticated from '../../layouts/Authenticated'; -import SectionMain from '../../components/SectionMain'; -import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'; -import { getPageTitle } from '../../config'; - -import { Field, Form, Formik } from 'formik'; -import FormField from '../../components/FormField'; -import BaseDivider from '../../components/BaseDivider'; -import BaseButtons from '../../components/BaseButtons'; -import BaseButton from '../../components/BaseButton'; -import FormCheckRadio from '../../components/FormCheckRadio'; -import FormCheckRadioGroup from '../../components/FormCheckRadioGroup'; -import FormFilePicker from '../../components/FormFilePicker'; -import FormImagePicker from '../../components/FormImagePicker'; -import { SelectField } from '../../components/SelectField'; -import { SelectFieldMany } from '../../components/SelectFieldMany'; -import { SwitchField } from '../../components/SwitchField'; -import { RichTextField } from '../../components/RichTextField'; - -import { update, fetch } from '../../stores/likes/likesSlice'; -import { useAppDispatch, useAppSelector } from '../../stores/hooks'; -import { useRouter } from 'next/router'; -import { saveFile } from '../../helpers/fileSaver'; -import dataFormatter from '../../helpers/dataFormatter'; -import ImageField from '../../components/ImageField'; - -const EditLikes = () => { - const router = useRouter(); - const dispatch = useAppDispatch(); - const initVals = {}; - const [initialValues, setInitialValues] = useState(initVals); - - const { likes } = useAppSelector((state) => state.likes); - - const { likesId } = router.query; - - useEffect(() => { - dispatch(fetch({ id: likesId })); - }, [likesId]); - - useEffect(() => { - if (typeof likes === 'object') { - setInitialValues(likes); - } - }, [likes]); - - useEffect(() => { - if (typeof likes === 'object') { - const newInitialVal = { ...initVals }; - - Object.keys(initVals).forEach((el) => (newInitialVal[el] = likes[el])); - - setInitialValues(newInitialVal); - } - }, [likes]); - - const handleSubmit = async (data) => { - await dispatch(update({ id: likesId, data })); - await router.push('/likes/likes-list'); - }; - - return ( - <> - - {getPageTitle('Edit likes')} - - - - {''} - - - handleSubmit(values)} - > -
- - - - - router.push('/likes/likes-list')} - /> - - -
-
-
- - ); -}; - -EditLikes.getLayout = function getLayout(page: ReactElement) { - return ( - - {page} - - ); -}; - -export default EditLikes; diff --git a/frontend/src/pages/likes/likes-edit.tsx b/frontend/src/pages/likes/likes-edit.tsx deleted file mode 100644 index c92db37..0000000 --- a/frontend/src/pages/likes/likes-edit.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { mdiChartTimelineVariant, mdiUpload } from '@mdi/js'; -import Head from 'next/head'; -import React, { ReactElement, useEffect, useState } from 'react'; -import DatePicker from 'react-datepicker'; -import 'react-datepicker/dist/react-datepicker.css'; -import dayjs from 'dayjs'; - -import CardBox from '../../components/CardBox'; -import LayoutAuthenticated from '../../layouts/Authenticated'; -import SectionMain from '../../components/SectionMain'; -import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'; -import { getPageTitle } from '../../config'; - -import { Field, Form, Formik } from 'formik'; -import FormField from '../../components/FormField'; -import BaseDivider from '../../components/BaseDivider'; -import BaseButtons from '../../components/BaseButtons'; -import BaseButton from '../../components/BaseButton'; -import FormCheckRadio from '../../components/FormCheckRadio'; -import FormCheckRadioGroup from '../../components/FormCheckRadioGroup'; -import FormFilePicker from '../../components/FormFilePicker'; -import FormImagePicker from '../../components/FormImagePicker'; -import { SelectField } from '../../components/SelectField'; -import { SelectFieldMany } from '../../components/SelectFieldMany'; -import { SwitchField } from '../../components/SwitchField'; -import { RichTextField } from '../../components/RichTextField'; - -import { update, fetch } from '../../stores/likes/likesSlice'; -import { useAppDispatch, useAppSelector } from '../../stores/hooks'; -import { useRouter } from 'next/router'; -import { saveFile } from '../../helpers/fileSaver'; -import dataFormatter from '../../helpers/dataFormatter'; -import ImageField from '../../components/ImageField'; - -const EditLikesPage = () => { - const router = useRouter(); - const dispatch = useAppDispatch(); - const initVals = {}; - const [initialValues, setInitialValues] = useState(initVals); - - const { likes } = useAppSelector((state) => state.likes); - - const { id } = router.query; - - useEffect(() => { - dispatch(fetch({ id: id })); - }, [id]); - - useEffect(() => { - if (typeof likes === 'object') { - setInitialValues(likes); - } - }, [likes]); - - useEffect(() => { - if (typeof likes === 'object') { - const newInitialVal = { ...initVals }; - Object.keys(initVals).forEach((el) => (newInitialVal[el] = likes[el])); - setInitialValues(newInitialVal); - } - }, [likes]); - - const handleSubmit = async (data) => { - await dispatch(update({ id: id, data })); - await router.push('/likes/likes-list'); - }; - - return ( - <> - - {getPageTitle('Edit likes')} - - - - {''} - - - handleSubmit(values)} - > -
- - - - - router.push('/likes/likes-list')} - /> - - -
-
-
- - ); -}; - -EditLikesPage.getLayout = function getLayout(page: ReactElement) { - return ( - - {page} - - ); -}; - -export default EditLikesPage; diff --git a/frontend/src/pages/likes/likes-list.tsx b/frontend/src/pages/likes/likes-list.tsx deleted file mode 100644 index 841dfa7..0000000 --- a/frontend/src/pages/likes/likes-list.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { mdiChartTimelineVariant } from '@mdi/js'; -import Head from 'next/head'; -import { uniqueId } from 'lodash'; -import React, { ReactElement, useState } from 'react'; -import CardBox from '../../components/CardBox'; -import LayoutAuthenticated from '../../layouts/Authenticated'; -import SectionMain from '../../components/SectionMain'; -import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'; -import { getPageTitle } from '../../config'; -import TableLikes from '../../components/Likes/TableLikes'; -import BaseButton from '../../components/BaseButton'; -import axios from 'axios'; -import Link from 'next/link'; -import { useAppDispatch, useAppSelector } from '../../stores/hooks'; -import CardBoxModal from '../../components/CardBoxModal'; -import DragDropFilePicker from '../../components/DragDropFilePicker'; -import { setRefetch, uploadCsv } from '../../stores/likes/likesSlice'; - -import { hasPermission } from '../../helpers/userPermissions'; - -const LikesTablesPage = () => { - const [filterItems, setFilterItems] = useState([]); - const [csvFile, setCsvFile] = useState(null); - const [isModalActive, setIsModalActive] = useState(false); - const [showTableView, setShowTableView] = useState(false); - - const { currentUser } = useAppSelector((state) => state.auth); - - const dispatch = useAppDispatch(); - - const [filters] = useState([]); - - const hasCreatePermission = - currentUser && hasPermission(currentUser, 'CREATE_LIKES'); - - const addFilter = () => { - const newItem = { - id: uniqueId(), - fields: { - filterValue: '', - filterValueFrom: '', - filterValueTo: '', - selectedField: '', - }, - }; - newItem.fields.selectedField = filters[0].title; - setFilterItems([...filterItems, newItem]); - }; - - const getLikesCSV = async () => { - const response = await axios({ - url: '/likes?filetype=csv', - method: 'GET', - responseType: 'blob', - }); - const type = response.headers['content-type']; - const blob = new Blob([response.data], { type: type }); - const link = document.createElement('a'); - link.href = window.URL.createObjectURL(blob); - link.download = 'likesCSV.csv'; - link.click(); - }; - - const onModalConfirm = async () => { - if (!csvFile) return; - await dispatch(uploadCsv(csvFile)); - dispatch(setRefetch(true)); - setCsvFile(null); - setIsModalActive(false); - }; - - const onModalCancel = () => { - setCsvFile(null); - setIsModalActive(false); - }; - - return ( - <> - - {getPageTitle('Likes')} - - - - {''} - - - {hasCreatePermission && ( - - )} - - - - - {hasCreatePermission && ( - setIsModalActive(true)} - /> - )} - -
-
-
-
- - - - -
- - - - - ); -}; - -LikesTablesPage.getLayout = function getLayout(page: ReactElement) { - return ( - {page} - ); -}; - -export default LikesTablesPage; diff --git a/frontend/src/pages/likes/likes-new.tsx b/frontend/src/pages/likes/likes-new.tsx deleted file mode 100644 index ac0fdf4..0000000 --- a/frontend/src/pages/likes/likes-new.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { - mdiAccount, - mdiChartTimelineVariant, - mdiMail, - mdiUpload, -} from '@mdi/js'; -import Head from 'next/head'; -import React, { ReactElement } from 'react'; -import CardBox from '../../components/CardBox'; -import LayoutAuthenticated from '../../layouts/Authenticated'; -import SectionMain from '../../components/SectionMain'; -import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'; -import { getPageTitle } from '../../config'; - -import { Field, Form, Formik } from 'formik'; -import FormField from '../../components/FormField'; -import BaseDivider from '../../components/BaseDivider'; -import BaseButtons from '../../components/BaseButtons'; -import BaseButton from '../../components/BaseButton'; -import FormCheckRadio from '../../components/FormCheckRadio'; -import FormCheckRadioGroup from '../../components/FormCheckRadioGroup'; -import FormFilePicker from '../../components/FormFilePicker'; -import FormImagePicker from '../../components/FormImagePicker'; -import { SwitchField } from '../../components/SwitchField'; - -import { SelectField } from '../../components/SelectField'; -import { SelectFieldMany } from '../../components/SelectFieldMany'; -import { RichTextField } from '../../components/RichTextField'; - -import { create } from '../../stores/likes/likesSlice'; -import { useAppDispatch } from '../../stores/hooks'; -import { useRouter } from 'next/router'; -import moment from 'moment'; - -const initialValues = {}; - -const LikesNew = () => { - const router = useRouter(); - const dispatch = useAppDispatch(); - - const handleSubmit = async (data) => { - await dispatch(create(data)); - await router.push('/likes/likes-list'); - }; - return ( - <> - - {getPageTitle('New Item')} - - - - {''} - - - handleSubmit(values)} - > -
- - - - - router.push('/likes/likes-list')} - /> - - -
-
-
- - ); -}; - -LikesNew.getLayout = function getLayout(page: ReactElement) { - return ( - - {page} - - ); -}; - -export default LikesNew; diff --git a/frontend/src/pages/likes/likes-table.tsx b/frontend/src/pages/likes/likes-table.tsx deleted file mode 100644 index 9a15606..0000000 --- a/frontend/src/pages/likes/likes-table.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { mdiChartTimelineVariant } from '@mdi/js'; -import Head from 'next/head'; -import { uniqueId } from 'lodash'; -import React, { ReactElement, useState } from 'react'; -import CardBox from '../../components/CardBox'; -import LayoutAuthenticated from '../../layouts/Authenticated'; -import SectionMain from '../../components/SectionMain'; -import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'; -import { getPageTitle } from '../../config'; -import TableLikes from '../../components/Likes/TableLikes'; -import BaseButton from '../../components/BaseButton'; -import axios from 'axios'; -import Link from 'next/link'; -import { useAppDispatch, useAppSelector } from '../../stores/hooks'; -import CardBoxModal from '../../components/CardBoxModal'; -import DragDropFilePicker from '../../components/DragDropFilePicker'; -import { setRefetch, uploadCsv } from '../../stores/likes/likesSlice'; - -import { hasPermission } from '../../helpers/userPermissions'; - -const LikesTablesPage = () => { - const [filterItems, setFilterItems] = useState([]); - const [csvFile, setCsvFile] = useState(null); - const [isModalActive, setIsModalActive] = useState(false); - const [showTableView, setShowTableView] = useState(false); - - const { currentUser } = useAppSelector((state) => state.auth); - - const dispatch = useAppDispatch(); - - const [filters] = useState([]); - - const hasCreatePermission = - currentUser && hasPermission(currentUser, 'CREATE_LIKES'); - - const addFilter = () => { - const newItem = { - id: uniqueId(), - fields: { - filterValue: '', - filterValueFrom: '', - filterValueTo: '', - selectedField: '', - }, - }; - newItem.fields.selectedField = filters[0].title; - setFilterItems([...filterItems, newItem]); - }; - - const getLikesCSV = async () => { - const response = await axios({ - url: '/likes?filetype=csv', - method: 'GET', - responseType: 'blob', - }); - const type = response.headers['content-type']; - const blob = new Blob([response.data], { type: type }); - const link = document.createElement('a'); - link.href = window.URL.createObjectURL(blob); - link.download = 'likesCSV.csv'; - link.click(); - }; - - const onModalConfirm = async () => { - if (!csvFile) return; - await dispatch(uploadCsv(csvFile)); - dispatch(setRefetch(true)); - setCsvFile(null); - setIsModalActive(false); - }; - - const onModalCancel = () => { - setCsvFile(null); - setIsModalActive(false); - }; - - return ( - <> - - {getPageTitle('Likes')} - - - - {''} - - - {hasCreatePermission && ( - - )} - - - - - {hasCreatePermission && ( - setIsModalActive(true)} - /> - )} - -
-
-
-
- - - -
- - - - - ); -}; - -LikesTablesPage.getLayout = function getLayout(page: ReactElement) { - return ( - {page} - ); -}; - -export default LikesTablesPage; diff --git a/frontend/src/pages/likes/likes-view.tsx b/frontend/src/pages/likes/likes-view.tsx deleted file mode 100644 index cdfa75b..0000000 --- a/frontend/src/pages/likes/likes-view.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { ReactElement, useEffect } from 'react'; -import Head from 'next/head'; -import DatePicker from 'react-datepicker'; -import 'react-datepicker/dist/react-datepicker.css'; -import dayjs from 'dayjs'; -import { useAppDispatch, useAppSelector } from '../../stores/hooks'; -import { useRouter } from 'next/router'; -import { fetch } from '../../stores/likes/likesSlice'; -import { saveFile } from '../../helpers/fileSaver'; -import dataFormatter from '../../helpers/dataFormatter'; -import ImageField from '../../components/ImageField'; -import LayoutAuthenticated from '../../layouts/Authenticated'; -import { getPageTitle } from '../../config'; -import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'; -import SectionMain from '../../components/SectionMain'; -import CardBox from '../../components/CardBox'; -import BaseButton from '../../components/BaseButton'; -import BaseDivider from '../../components/BaseDivider'; -import { mdiChartTimelineVariant } from '@mdi/js'; -import { SwitchField } from '../../components/SwitchField'; -import FormField from '../../components/FormField'; - -const LikesView = () => { - const router = useRouter(); - const dispatch = useAppDispatch(); - const { likes } = useAppSelector((state) => state.likes); - - const { id } = router.query; - - function removeLastCharacter(str) { - console.log(str, `str`); - return str.slice(0, -1); - } - - useEffect(() => { - dispatch(fetch({ id })); - }, [dispatch, id]); - - return ( - <> - - {getPageTitle('View likes')} - - - - - - - - - router.push('/likes/likes-list')} - /> - - - - ); -}; - -LikesView.getLayout = function getLayout(page: ReactElement) { - return ( - {page} - ); -}; - -export default LikesView; diff --git a/frontend/src/pages/web_pages/home.tsx b/frontend/src/pages/web_pages/home.tsx index 54ac063..73f1376 100644 --- a/frontend/src/pages/web_pages/home.tsx +++ b/frontend/src/pages/web_pages/home.tsx @@ -139,7 +139,7 @@ export default function WebSite() { { - const { id, query } = data; - const result = await axios.get(`likes${query || (id ? `/${id}` : '')}`); - return id - ? result.data - : { rows: result.data.rows, count: result.data.count }; -}); - -export const deleteItemsByIds = createAsyncThunk( - 'likes/deleteByIds', - async (data: any, { rejectWithValue }) => { - try { - await axios.post('likes/deleteByIds', { data }); - } catch (error) { - if (!error.response) { - throw error; - } - - return rejectWithValue(error.response.data); - } - }, -); - -export const deleteItem = createAsyncThunk( - 'likes/deleteLikes', - async (id: string, { rejectWithValue }) => { - try { - await axios.delete(`likes/${id}`); - } catch (error) { - if (!error.response) { - throw error; - } - - return rejectWithValue(error.response.data); - } - }, -); - -export const create = createAsyncThunk( - 'likes/createLikes', - async (data: any, { rejectWithValue }) => { - try { - const result = await axios.post('likes', { data }); - return result.data; - } catch (error) { - if (!error.response) { - throw error; - } - - return rejectWithValue(error.response.data); - } - }, -); - -export const uploadCsv = createAsyncThunk( - 'likes/uploadCsv', - async (file: File, { rejectWithValue }) => { - try { - const data = new FormData(); - data.append('file', file); - data.append('filename', file.name); - - const result = await axios.post('likes/bulk-import', data, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); - - return result.data; - } catch (error) { - if (!error.response) { - throw error; - } - - return rejectWithValue(error.response.data); - } - }, -); - -export const update = createAsyncThunk( - 'likes/updateLikes', - async (payload: any, { rejectWithValue }) => { - try { - const result = await axios.put(`likes/${payload.id}`, { - id: payload.id, - data: payload.data, - }); - return result.data; - } catch (error) { - if (!error.response) { - throw error; - } - - return rejectWithValue(error.response.data); - } - }, -); - -export const likesSlice = createSlice({ - name: 'likes', - initialState, - reducers: { - setRefetch: (state, action: PayloadAction) => { - state.refetch = action.payload; - }, - }, - extraReducers: (builder) => { - builder.addCase(fetch.pending, (state) => { - state.loading = true; - resetNotify(state); - }); - builder.addCase(fetch.rejected, (state, action) => { - state.loading = false; - rejectNotify(state, action); - }); - - builder.addCase(fetch.fulfilled, (state, action) => { - if (action.payload.rows && action.payload.count >= 0) { - state.likes = action.payload.rows; - state.count = action.payload.count; - } else { - state.likes = action.payload; - } - state.loading = false; - }); - - builder.addCase(deleteItemsByIds.pending, (state) => { - state.loading = true; - resetNotify(state); - }); - - builder.addCase(deleteItemsByIds.fulfilled, (state) => { - state.loading = false; - fulfilledNotify(state, 'Likes has been deleted'); - }); - - builder.addCase(deleteItemsByIds.rejected, (state, action) => { - state.loading = false; - rejectNotify(state, action); - }); - - builder.addCase(deleteItem.pending, (state) => { - state.loading = true; - resetNotify(state); - }); - - builder.addCase(deleteItem.fulfilled, (state) => { - state.loading = false; - fulfilledNotify(state, `${'Likes'.slice(0, -1)} has been deleted`); - }); - - builder.addCase(deleteItem.rejected, (state, action) => { - state.loading = false; - rejectNotify(state, action); - }); - - builder.addCase(create.pending, (state) => { - state.loading = true; - resetNotify(state); - }); - builder.addCase(create.rejected, (state, action) => { - state.loading = false; - rejectNotify(state, action); - }); - - builder.addCase(create.fulfilled, (state) => { - state.loading = false; - fulfilledNotify(state, `${'Likes'.slice(0, -1)} has been created`); - }); - - builder.addCase(update.pending, (state) => { - state.loading = true; - resetNotify(state); - }); - builder.addCase(update.fulfilled, (state) => { - state.loading = false; - fulfilledNotify(state, `${'Likes'.slice(0, -1)} has been updated`); - }); - builder.addCase(update.rejected, (state, action) => { - state.loading = false; - rejectNotify(state, action); - }); - - builder.addCase(uploadCsv.pending, (state) => { - state.loading = true; - resetNotify(state); - }); - builder.addCase(uploadCsv.fulfilled, (state) => { - state.loading = false; - fulfilledNotify(state, 'Likes has been uploaded'); - }); - builder.addCase(uploadCsv.rejected, (state, action) => { - state.loading = false; - rejectNotify(state, action); - }); - }, -}); - -// Action creators are generated for each case reducer function -export const { setRefetch } = likesSlice.actions; - -export default likesSlice.reducer; diff --git a/frontend/src/stores/store.ts b/frontend/src/stores/store.ts index 3a71946..aaf78cc 100644 --- a/frontend/src/stores/store.ts +++ b/frontend/src/stores/store.ts @@ -13,7 +13,6 @@ import instructorsSlice from './instructors/instructorsSlice'; import studentsSlice from './students/studentsSlice'; import rolesSlice from './roles/rolesSlice'; import permissionsSlice from './permissions/permissionsSlice'; -import likesSlice from './likes/likesSlice'; export const store = configureStore({ reducer: { @@ -31,7 +30,6 @@ export const store = configureStore({ students: studentsSlice, roles: rolesSlice, permissions: permissionsSlice, - likes: likesSlice, }, });