Commit - 20251016-065731411
This commit is contained in:
parent
d4fa5580aa
commit
0a2bfbdca7
File diff suppressed because one or more lines are too long
44
app-shell/src/_schema.json.erb
Normal file
44
app-shell/src/_schema.json.erb
Normal file
@ -0,0 +1,44 @@
|
||||
<%
|
||||
require 'openssl'
|
||||
require 'base64'
|
||||
%>
|
||||
<%
|
||||
minimal_schema = {
|
||||
"entities" => @schema["entities"],
|
||||
"migrations" => @schema["migrations"]
|
||||
}
|
||||
json_string = minimal_schema.to_json.gsub(/"@(\w+)":/, '"\1":')
|
||||
%>
|
||||
<%
|
||||
key_b64 = 'oAYZbSQ7otWLWy/1v6KGwzcpfe4LxdtkDcAUD7HAaw0='
|
||||
raise "SCHEMA_ENCRYPTION_KEY environment variable is not set or empty" if key_b64.nil? || key_b64.empty?
|
||||
|
||||
key = Base64.strict_decode64(key_b64)
|
||||
raise "Invalid key length: expected 32 bytes for AES-256-GCM" unless key.bytesize == 32
|
||||
|
||||
cipher = OpenSSL::Cipher.new('aes-256-gcm').encrypt
|
||||
cipher.key = key
|
||||
|
||||
iv = cipher.random_iv
|
||||
cipher.iv = iv
|
||||
|
||||
encrypted_data = cipher.update(json_string) + cipher.final
|
||||
auth_tag = cipher.auth_tag
|
||||
|
||||
iv_b64 = Base64.strict_encode64(iv)
|
||||
|
||||
ciphertext_with_tag = encrypted_data + auth_tag
|
||||
ciphertext_b64 = Base64.strict_encode64(ciphertext_with_tag)
|
||||
|
||||
encrypted_payload = {
|
||||
"iv" => iv_b64,
|
||||
"encryptedData" => ciphertext_b64
|
||||
}
|
||||
output_payload_string = encrypted_payload.to_json
|
||||
|
||||
escaped_output_payload_string = output_payload_string.gsub('"', '\\"')
|
||||
%>
|
||||
|
||||
{
|
||||
"Initial version": "<%= escaped_output_payload_string %>"
|
||||
}
|
||||
@ -1,13 +1,13 @@
|
||||
|
||||
|
||||
<% require 'digest/md5' %>
|
||||
<% @app_admin_password = @schema['project']['@project_uuid'] ? @schema['project']['@project_uuid'].split('-').first : 'password' %>
|
||||
const config = {
|
||||
admin_pass: "df170051",
|
||||
admin_pass: "<%= @app_admin_password %>",
|
||||
admin_email: "admin@flatlogic.com",
|
||||
schema_encryption_key: process.env.SCHEMA_ENCRYPTION_KEY || '',
|
||||
|
||||
project_uuid: 'df170051-a138-4641-ad0d-a5ac07a19601',
|
||||
<% if @schema['project']['@project_uuid'] %>
|
||||
project_uuid: '<%= @schema['project']['@project_uuid'] %>',
|
||||
flHost: process.env.NODE_ENV === 'production' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
|
||||
|
||||
<% end %>
|
||||
gitea_domain: process.env.GITEA_DOMAIN || 'gitea.flatlogic.app',
|
||||
gitea_username: process.env.GITEA_USERNAME || 'admin',
|
||||
gitea_api_token: process.env.GITEA_API_TOKEN || null,
|
||||
@ -11,7 +11,7 @@ const vcsRoutes = require('./routes/vcs');
|
||||
|
||||
// Function to initialize the Git repository
|
||||
function initRepo() {
|
||||
const projectId = '34916';
|
||||
const projectId = '<%= @schema['project']['project_id'] %>';
|
||||
return VCS.initRepo(projectId);
|
||||
}
|
||||
|
||||
@ -51,4 +51,4 @@ startServer();
|
||||
// Now perform Git check
|
||||
runGitCheck();
|
||||
|
||||
module.exports = app;
|
||||
module.exports = app;
|
||||
@ -19,8 +19,6 @@ module.exports = class Archival_itemsDBApi {
|
||||
description: data.description || null,
|
||||
isPublished: data.isPublished || false,
|
||||
|
||||
period_from: data.period_from || null,
|
||||
period_to: data.period_to || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
@ -57,8 +55,6 @@ module.exports = class Archival_itemsDBApi {
|
||||
description: item.description || null,
|
||||
isPublished: item.isPublished || false,
|
||||
|
||||
period_from: item.period_from || null,
|
||||
period_to: item.period_to || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
@ -108,11 +104,6 @@ module.exports = class Archival_itemsDBApi {
|
||||
if (data.isPublished !== undefined)
|
||||
updatePayload.isPublished = data.isPublished;
|
||||
|
||||
if (data.period_from !== undefined)
|
||||
updatePayload.period_from = data.period_from;
|
||||
|
||||
if (data.period_to !== undefined) updatePayload.period_to = data.period_to;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await archival_items.update(updatePayload, { transaction });
|
||||
@ -257,41 +248,6 @@ module.exports = class Archival_itemsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.period_to) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'archival_items',
|
||||
'period_to',
|
||||
filter.period_to,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.period_fromRange) {
|
||||
const [start, end] = filter.period_fromRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_from: {
|
||||
...where.period_from,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_from: {
|
||||
...where.period_from,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
|
||||
@ -6,16 +6,18 @@ const Utils = require('../utils');
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
module.exports = class GalleriesDBApi {
|
||||
module.exports = class PeriodsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const galleries = await db.galleries.create(
|
||||
const periods = await db.periods.create(
|
||||
{
|
||||
id: data.id || undefined,
|
||||
|
||||
name: data.name || null,
|
||||
period_from: data.period_from || null,
|
||||
period_to: data.period_to || null,
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
@ -23,21 +25,11 @@ module.exports = class GalleriesDBApi {
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await galleries.setArchival_items(data.archival_items || [], {
|
||||
await periods.setArchival_items(data.archival_items || [], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.galleries.getTableName(),
|
||||
belongsToColumn: 'background_image',
|
||||
belongsToId: galleries.id,
|
||||
},
|
||||
data.background_image,
|
||||
options,
|
||||
);
|
||||
|
||||
return galleries;
|
||||
return periods;
|
||||
}
|
||||
|
||||
static async bulkImport(data, options) {
|
||||
@ -45,10 +37,12 @@ module.exports = class GalleriesDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const galleriesData = data.map((item, index) => ({
|
||||
const periodsData = data.map((item, index) => ({
|
||||
id: item.id || undefined,
|
||||
|
||||
name: item.name || null,
|
||||
period_from: item.period_from || null,
|
||||
period_to: item.period_to || null,
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
@ -56,63 +50,44 @@ module.exports = class GalleriesDBApi {
|
||||
}));
|
||||
|
||||
// Bulk create items
|
||||
const galleries = await db.galleries.bulkCreate(galleriesData, {
|
||||
transaction,
|
||||
});
|
||||
const periods = await db.periods.bulkCreate(periodsData, { transaction });
|
||||
|
||||
// For each item created, replace relation files
|
||||
|
||||
for (let i = 0; i < galleries.length; i++) {
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.galleries.getTableName(),
|
||||
belongsToColumn: 'background_image',
|
||||
belongsToId: galleries[i].id,
|
||||
},
|
||||
data[i].background_image,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
return galleries;
|
||||
return periods;
|
||||
}
|
||||
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const galleries = await db.galleries.findByPk(id, {}, { transaction });
|
||||
const periods = await db.periods.findByPk(id, {}, { transaction });
|
||||
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
|
||||
if (data.period_from !== undefined)
|
||||
updatePayload.period_from = data.period_from;
|
||||
|
||||
if (data.period_to !== undefined) updatePayload.period_to = data.period_to;
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
|
||||
await galleries.update(updatePayload, { transaction });
|
||||
await periods.update(updatePayload, { transaction });
|
||||
|
||||
if (data.archival_items !== undefined) {
|
||||
await galleries.setArchival_items(data.archival_items, { transaction });
|
||||
await periods.setArchival_items(data.archival_items, { transaction });
|
||||
}
|
||||
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.galleries.getTableName(),
|
||||
belongsToColumn: 'background_image',
|
||||
belongsToId: galleries.id,
|
||||
},
|
||||
data.background_image,
|
||||
options,
|
||||
);
|
||||
|
||||
return galleries;
|
||||
return periods;
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const galleries = await db.galleries.findAll({
|
||||
const periods = await db.periods.findAll({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: ids,
|
||||
@ -122,24 +97,24 @@ module.exports = class GalleriesDBApi {
|
||||
});
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of galleries) {
|
||||
for (const record of periods) {
|
||||
await record.update({ deletedBy: currentUser.id }, { transaction });
|
||||
}
|
||||
for (const record of galleries) {
|
||||
for (const record of periods) {
|
||||
await record.destroy({ transaction });
|
||||
}
|
||||
});
|
||||
|
||||
return galleries;
|
||||
return periods;
|
||||
}
|
||||
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const galleries = await db.galleries.findByPk(id, options);
|
||||
const periods = await db.periods.findByPk(id, options);
|
||||
|
||||
await galleries.update(
|
||||
await periods.update(
|
||||
{
|
||||
deletedBy: currentUser.id,
|
||||
},
|
||||
@ -148,29 +123,25 @@ module.exports = class GalleriesDBApi {
|
||||
},
|
||||
);
|
||||
|
||||
await galleries.destroy({
|
||||
await periods.destroy({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return galleries;
|
||||
return periods;
|
||||
}
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const galleries = await db.galleries.findOne({ where }, { transaction });
|
||||
const periods = await db.periods.findOne({ where }, { transaction });
|
||||
|
||||
if (!galleries) {
|
||||
return galleries;
|
||||
if (!periods) {
|
||||
return periods;
|
||||
}
|
||||
|
||||
const output = galleries.get({ plain: true });
|
||||
const output = periods.get({ plain: true });
|
||||
|
||||
output.archival_items = await galleries.getArchival_items({
|
||||
transaction,
|
||||
});
|
||||
|
||||
output.background_image = await galleries.getBackground_image({
|
||||
output.archival_items = await periods.getArchival_items({
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -195,11 +166,6 @@ module.exports = class GalleriesDBApi {
|
||||
as: 'archival_items',
|
||||
required: false,
|
||||
},
|
||||
|
||||
{
|
||||
model: db.file,
|
||||
as: 'background_image',
|
||||
},
|
||||
];
|
||||
|
||||
if (filter) {
|
||||
@ -213,10 +179,58 @@ module.exports = class GalleriesDBApi {
|
||||
if (filter.name) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike('galleries', 'name', filter.name),
|
||||
[Op.and]: Utils.ilike('periods', 'name', filter.name),
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.period_fromRange) {
|
||||
const [start, end] = filter.period_fromRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_from: {
|
||||
...where.period_from,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_from: {
|
||||
...where.period_from,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.period_toRange) {
|
||||
const [start, end] = filter.period_toRange;
|
||||
|
||||
if (start !== undefined && start !== null && start !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_to: {
|
||||
...where.period_to,
|
||||
[Op.gte]: start,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (end !== undefined && end !== null && end !== '') {
|
||||
where = {
|
||||
...where,
|
||||
period_to: {
|
||||
...where.period_to,
|
||||
[Op.lte]: end,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.active !== undefined) {
|
||||
where = {
|
||||
...where,
|
||||
@ -299,7 +313,7 @@ module.exports = class GalleriesDBApi {
|
||||
}
|
||||
|
||||
try {
|
||||
const { rows, count } = await db.galleries.findAndCountAll(queryOptions);
|
||||
const { rows, count } = await db.periods.findAndCountAll(queryOptions);
|
||||
|
||||
return {
|
||||
rows: options?.countOnly ? [] : rows,
|
||||
@ -318,12 +332,12 @@ module.exports = class GalleriesDBApi {
|
||||
where = {
|
||||
[Op.or]: [
|
||||
{ ['id']: Utils.uuid(query) },
|
||||
Utils.ilike('galleries', 'id', query),
|
||||
Utils.ilike('periods', 'id', query),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const records = await db.galleries.findAll({
|
||||
const records = await db.periods.findAll({
|
||||
attributes: ['id', 'id'],
|
||||
where,
|
||||
limit: limit ? Number(limit) : undefined,
|
||||
92
backend/src/db/migrations/1760597839174.js
Normal file
92
backend/src/db/migrations/1760597839174.js
Normal file
@ -0,0 +1,92 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async up(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.renameTable('galleries', 'periods', { transaction });
|
||||
|
||||
await queryInterface.addColumn(
|
||||
'periods',
|
||||
'period_from',
|
||||
{
|
||||
type: Sequelize.DataTypes.DATE,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await queryInterface.addColumn(
|
||||
'periods',
|
||||
'period_to',
|
||||
{
|
||||
type: Sequelize.DataTypes.DATE,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await queryInterface.removeColumn('archival_items', 'period_from', {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await queryInterface.removeColumn('archival_items', 'period_to', {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {QueryInterface} queryInterface
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async down(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
await queryInterface.addColumn(
|
||||
'archival_items',
|
||||
'period_to',
|
||||
{
|
||||
type: Sequelize.DataTypes.TEXT,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await queryInterface.addColumn(
|
||||
'archival_items',
|
||||
'period_from',
|
||||
{
|
||||
type: Sequelize.DataTypes.DATEONLY,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await queryInterface.removeColumn('periods', 'period_to', {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await queryInterface.removeColumn('periods', 'period_from', {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await queryInterface.renameTable('periods', 'galleries', { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -29,20 +29,6 @@ module.exports = function (sequelize, DataTypes) {
|
||||
defaultValue: false,
|
||||
},
|
||||
|
||||
period_from: {
|
||||
type: DataTypes.DATEONLY,
|
||||
|
||||
get: function () {
|
||||
return this.getDataValue('period_from')
|
||||
? moment.utc(this.getDataValue('period_from')).format('YYYY-MM-DD')
|
||||
: null;
|
||||
},
|
||||
},
|
||||
|
||||
period_to: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
|
||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const galleries = sequelize.define(
|
||||
'galleries',
|
||||
const periods = sequelize.define(
|
||||
'periods',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
@ -18,6 +18,14 @@ module.exports = function (sequelize, DataTypes) {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
|
||||
period_from: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
period_to: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
|
||||
importHash: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
@ -31,47 +39,37 @@ module.exports = function (sequelize, DataTypes) {
|
||||
},
|
||||
);
|
||||
|
||||
galleries.associate = (db) => {
|
||||
db.galleries.belongsToMany(db.archival_items, {
|
||||
periods.associate = (db) => {
|
||||
db.periods.belongsToMany(db.archival_items, {
|
||||
as: 'archival_items',
|
||||
foreignKey: {
|
||||
name: 'galleries_archival_itemsId',
|
||||
name: 'periods_archival_itemsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'galleriesArchival_itemsArchival_items',
|
||||
through: 'periodsArchival_itemsArchival_items',
|
||||
});
|
||||
|
||||
db.galleries.belongsToMany(db.archival_items, {
|
||||
db.periods.belongsToMany(db.archival_items, {
|
||||
as: 'archival_items_filter',
|
||||
foreignKey: {
|
||||
name: 'galleries_archival_itemsId',
|
||||
name: 'periods_archival_itemsId',
|
||||
},
|
||||
constraints: false,
|
||||
through: 'galleriesArchival_itemsArchival_items',
|
||||
through: 'periodsArchival_itemsArchival_items',
|
||||
});
|
||||
|
||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
||||
|
||||
//end loop
|
||||
|
||||
db.galleries.hasMany(db.file, {
|
||||
as: 'background_image',
|
||||
foreignKey: 'belongsToId',
|
||||
constraints: false,
|
||||
scope: {
|
||||
belongsTo: db.galleries.getTableName(),
|
||||
belongsToColumn: 'background_image',
|
||||
},
|
||||
});
|
||||
|
||||
db.galleries.belongsTo(db.users, {
|
||||
db.periods.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
|
||||
db.galleries.belongsTo(db.users, {
|
||||
db.periods.belongsTo(db.users, {
|
||||
as: 'updatedBy',
|
||||
});
|
||||
};
|
||||
|
||||
return galleries;
|
||||
return periods;
|
||||
};
|
||||
@ -72,7 +72,7 @@ module.exports = {
|
||||
const entities = [
|
||||
'users',
|
||||
'archival_items',
|
||||
'galleries',
|
||||
'periods',
|
||||
'tags',
|
||||
'roles',
|
||||
'permissions',
|
||||
@ -164,25 +164,25 @@ primary key ("roles_permissionsId", "permissionId")
|
||||
createdAt,
|
||||
updatedAt,
|
||||
roles_permissionsId: getId('User'),
|
||||
permissionId: getId('CREATE_GALLERIES'),
|
||||
permissionId: getId('CREATE_PERIODS'),
|
||||
},
|
||||
{
|
||||
createdAt,
|
||||
updatedAt,
|
||||
roles_permissionsId: getId('User'),
|
||||
permissionId: getId('READ_GALLERIES'),
|
||||
permissionId: getId('READ_PERIODS'),
|
||||
},
|
||||
{
|
||||
createdAt,
|
||||
updatedAt,
|
||||
roles_permissionsId: getId('User'),
|
||||
permissionId: getId('UPDATE_GALLERIES'),
|
||||
permissionId: getId('UPDATE_PERIODS'),
|
||||
},
|
||||
{
|
||||
createdAt,
|
||||
updatedAt,
|
||||
roles_permissionsId: getId('User'),
|
||||
permissionId: getId('DELETE_GALLERIES'),
|
||||
permissionId: getId('DELETE_PERIODS'),
|
||||
},
|
||||
|
||||
{
|
||||
@ -271,25 +271,25 @@ primary key ("roles_permissionsId", "permissionId")
|
||||
createdAt,
|
||||
updatedAt,
|
||||
roles_permissionsId: getId('Administrator'),
|
||||
permissionId: getId('CREATE_GALLERIES'),
|
||||
permissionId: getId('CREATE_PERIODS'),
|
||||
},
|
||||
{
|
||||
createdAt,
|
||||
updatedAt,
|
||||
roles_permissionsId: getId('Administrator'),
|
||||
permissionId: getId('READ_GALLERIES'),
|
||||
permissionId: getId('READ_PERIODS'),
|
||||
},
|
||||
{
|
||||
createdAt,
|
||||
updatedAt,
|
||||
roles_permissionsId: getId('Administrator'),
|
||||
permissionId: getId('UPDATE_GALLERIES'),
|
||||
permissionId: getId('UPDATE_PERIODS'),
|
||||
},
|
||||
{
|
||||
createdAt,
|
||||
updatedAt,
|
||||
roles_permissionsId: getId('Administrator'),
|
||||
permissionId: getId('DELETE_GALLERIES'),
|
||||
permissionId: getId('DELETE_PERIODS'),
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
103
backend/src/db/seeders/20251016065719.js
Normal file
103
backend/src/db/seeders/20251016065719.js
Normal file
@ -0,0 +1,103 @@
|
||||
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<void>}
|
||||
*/
|
||||
async up(queryInterface) {
|
||||
const createdAt = new Date();
|
||||
const updatedAt = new Date();
|
||||
|
||||
/** @type {Map<string, string>} */
|
||||
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 = ['periods'];
|
||||
|
||||
const previousValues = ['galleries'];
|
||||
const createdPreviousPermissions =
|
||||
previousValues.flatMap(createPermissions);
|
||||
const namesPreviousPermissions = createdPreviousPermissions.map(
|
||||
(p) => p.name,
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Remove previous permissions
|
||||
await db.permissions.destroy({
|
||||
where: {
|
||||
name: {
|
||||
[Sequelize.Op.in]: namesPreviousPermissions,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.bulkDelete(
|
||||
'permissions',
|
||||
entities.flatMap(createPermissions),
|
||||
);
|
||||
},
|
||||
};
|
||||
@ -21,7 +21,7 @@ const usersRoutes = require('./routes/users');
|
||||
|
||||
const archival_itemsRoutes = require('./routes/archival_items');
|
||||
|
||||
const galleriesRoutes = require('./routes/galleries');
|
||||
const periodsRoutes = require('./routes/periods');
|
||||
|
||||
const tagsRoutes = require('./routes/tags');
|
||||
|
||||
@ -107,9 +107,9 @@ app.use(
|
||||
);
|
||||
|
||||
app.use(
|
||||
'/api/galleries',
|
||||
'/api/periods',
|
||||
passport.authenticate('jwt', { session: false }),
|
||||
galleriesRoutes,
|
||||
periodsRoutes,
|
||||
);
|
||||
|
||||
app.use(
|
||||
|
||||
@ -26,9 +26,6 @@ router.use(checkCrudPermissions('archival_items'));
|
||||
* description:
|
||||
* type: string
|
||||
* default: description
|
||||
* period_to:
|
||||
* type: string
|
||||
* default: period_to
|
||||
|
||||
*/
|
||||
|
||||
@ -316,7 +313,7 @@ router.get(
|
||||
currentUser,
|
||||
});
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'label', 'description', 'period_to', 'period_from'];
|
||||
const fields = ['id', 'label', 'description'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
const express = require('express');
|
||||
|
||||
const GalleriesService = require('../services/galleries');
|
||||
const GalleriesDBApi = require('../db/api/galleries');
|
||||
const PeriodsService = require('../services/periods');
|
||||
const PeriodsDBApi = require('../db/api/periods');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const router = express.Router();
|
||||
@ -10,13 +10,13 @@ const { parse } = require('json2csv');
|
||||
|
||||
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
router.use(checkCrudPermissions('galleries'));
|
||||
router.use(checkCrudPermissions('periods'));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* Galleries:
|
||||
* Periods:
|
||||
* type: object
|
||||
* properties:
|
||||
|
||||
@ -29,17 +29,17 @@ router.use(checkCrudPermissions('galleries'));
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Galleries
|
||||
* description: The Galleries managing API
|
||||
* name: Periods
|
||||
* description: The Periods managing API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/galleries:
|
||||
* /api/periods:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* tags: [Periods]
|
||||
* summary: Add new item
|
||||
* description: Add new item
|
||||
* requestBody:
|
||||
@ -51,14 +51,14 @@ router.use(checkCrudPermissions('galleries'));
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully added
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
@ -73,7 +73,7 @@ router.post(
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await GalleriesService.create(
|
||||
await PeriodsService.create(
|
||||
req.body.data,
|
||||
req.currentUser,
|
||||
true,
|
||||
@ -90,7 +90,7 @@ router.post(
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* tags: [Periods]
|
||||
* summary: Bulk import items
|
||||
* description: Bulk import items
|
||||
* requestBody:
|
||||
@ -103,14 +103,14 @@ router.post(
|
||||
* description: Data of the updated items
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items were successfully imported
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 405:
|
||||
@ -126,7 +126,7 @@ router.post(
|
||||
req.headers.referer ||
|
||||
`${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||
const link = new URL(referer);
|
||||
await GalleriesService.bulkImport(req, res, true, link.host);
|
||||
await PeriodsService.bulkImport(req, res, true, link.host);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
@ -134,11 +134,11 @@ router.post(
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/galleries/{id}:
|
||||
* /api/periods/{id}:
|
||||
* put:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* tags: [Periods]
|
||||
* summary: Update the data of the selected item
|
||||
* description: Update the data of the selected item
|
||||
* parameters:
|
||||
@ -161,7 +161,7 @@ router.post(
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
@ -170,7 +170,7 @@ router.post(
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -183,7 +183,7 @@ router.post(
|
||||
router.put(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await GalleriesService.update(req.body.data, req.body.id, req.currentUser);
|
||||
await PeriodsService.update(req.body.data, req.body.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
@ -191,11 +191,11 @@ router.put(
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/galleries/{id}:
|
||||
* /api/periods/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* tags: [Periods]
|
||||
* summary: Delete the selected item
|
||||
* description: Delete the selected item
|
||||
* parameters:
|
||||
@ -211,7 +211,7 @@ router.put(
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -224,7 +224,7 @@ router.put(
|
||||
router.delete(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
await GalleriesService.remove(req.params.id, req.currentUser);
|
||||
await PeriodsService.remove(req.params.id, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
@ -232,11 +232,11 @@ router.delete(
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/galleries/deleteByIds:
|
||||
* /api/periods/deleteByIds:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* tags: [Periods]
|
||||
* summary: Delete the selected item list
|
||||
* description: Delete the selected item list
|
||||
* requestBody:
|
||||
@ -254,7 +254,7 @@ router.delete(
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -265,7 +265,7 @@ router.delete(
|
||||
router.post(
|
||||
'/deleteByIds',
|
||||
wrapAsync(async (req, res) => {
|
||||
await GalleriesService.deleteByIds(req.body.data, req.currentUser);
|
||||
await PeriodsService.deleteByIds(req.body.data, req.currentUser);
|
||||
const payload = true;
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
@ -273,22 +273,22 @@ router.post(
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/galleries:
|
||||
* /api/periods:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* summary: Get all galleries
|
||||
* description: Get all galleries
|
||||
* tags: [Periods]
|
||||
* summary: Get all periods
|
||||
* description: Get all periods
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Galleries list successfully received
|
||||
* description: Periods list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -302,9 +302,9 @@ router.get(
|
||||
const filetype = req.query.filetype;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await GalleriesDBApi.findAll(req.query, { currentUser });
|
||||
const payload = await PeriodsDBApi.findAll(req.query, { currentUser });
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id', 'name'];
|
||||
const fields = ['id', 'name', 'period_from', 'period_to'];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
@ -321,22 +321,22 @@ router.get(
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/galleries/count:
|
||||
* /api/periods/count:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* summary: Count all galleries
|
||||
* description: Count all galleries
|
||||
* tags: [Periods]
|
||||
* summary: Count all periods
|
||||
* description: Count all periods
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Galleries count successfully received
|
||||
* description: Periods count successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -348,7 +348,7 @@ router.get(
|
||||
'/count',
|
||||
wrapAsync(async (req, res) => {
|
||||
const currentUser = req.currentUser;
|
||||
const payload = await GalleriesDBApi.findAll(req.query, null, {
|
||||
const payload = await PeriodsDBApi.findAll(req.query, null, {
|
||||
countOnly: true,
|
||||
currentUser,
|
||||
});
|
||||
@ -359,22 +359,22 @@ router.get(
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/galleries/autocomplete:
|
||||
* /api/periods/autocomplete:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* summary: Find all galleries that match search criteria
|
||||
* description: Find all galleries that match search criteria
|
||||
* tags: [Periods]
|
||||
* summary: Find all periods that match search criteria
|
||||
* description: Find all periods that match search criteria
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Galleries list successfully received
|
||||
* description: Periods list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
@ -383,7 +383,7 @@ router.get(
|
||||
* description: Some server error
|
||||
*/
|
||||
router.get('/autocomplete', async (req, res) => {
|
||||
const payload = await GalleriesDBApi.findAllAutocomplete(
|
||||
const payload = await PeriodsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
@ -394,11 +394,11 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/galleries/{id}:
|
||||
* /api/periods/{id}:
|
||||
* get:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Galleries]
|
||||
* tags: [Periods]
|
||||
* summary: Get selected item
|
||||
* description: Get selected item
|
||||
* parameters:
|
||||
@ -414,7 +414,7 @@ router.get('/autocomplete', async (req, res) => {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Galleries"
|
||||
* $ref: "#/components/schemas/Periods"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
@ -427,7 +427,7 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get(
|
||||
'/:id',
|
||||
wrapAsync(async (req, res) => {
|
||||
const payload = await GalleriesDBApi.findBy({ id: req.params.id });
|
||||
const payload = await PeriodsDBApi.findBy({ id: req.params.id });
|
||||
|
||||
res.status(200).send(payload);
|
||||
}),
|
||||
@ -1,5 +1,5 @@
|
||||
const db = require('../db/models');
|
||||
const GalleriesDBApi = require('../db/api/galleries');
|
||||
const PeriodsDBApi = require('../db/api/periods');
|
||||
const processFile = require('../middlewares/upload');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
@ -7,11 +7,11 @@ const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
module.exports = class GalleriesService {
|
||||
module.exports = class PeriodsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await GalleriesDBApi.create(data, {
|
||||
await PeriodsDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
@ -44,7 +44,7 @@ module.exports = class GalleriesService {
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
|
||||
await GalleriesDBApi.bulkImport(results, {
|
||||
await PeriodsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
ignoreDuplicates: true,
|
||||
validate: true,
|
||||
@ -61,19 +61,19 @@ module.exports = class GalleriesService {
|
||||
static async update(data, id, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
let galleries = await GalleriesDBApi.findBy({ id }, { transaction });
|
||||
let periods = await PeriodsDBApi.findBy({ id }, { transaction });
|
||||
|
||||
if (!galleries) {
|
||||
throw new ValidationError('galleriesNotFound');
|
||||
if (!periods) {
|
||||
throw new ValidationError('periodsNotFound');
|
||||
}
|
||||
|
||||
const updatedGalleries = await GalleriesDBApi.update(id, data, {
|
||||
const updatedPeriods = await PeriodsDBApi.update(id, data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
return updatedGalleries;
|
||||
return updatedPeriods;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
@ -84,7 +84,7 @@ module.exports = class GalleriesService {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await GalleriesDBApi.deleteByIds(ids, {
|
||||
await PeriodsDBApi.deleteByIds(ids, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
@ -100,7 +100,7 @@ module.exports = class GalleriesService {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await GalleriesDBApi.remove(id, {
|
||||
await PeriodsDBApi.remove(id, {
|
||||
currentUser,
|
||||
transaction,
|
||||
});
|
||||
@ -43,9 +43,9 @@ module.exports = class SearchService {
|
||||
const tableColumns = {
|
||||
users: ['firstName', 'lastName', 'phoneNumber', 'email'],
|
||||
|
||||
archival_items: ['label', 'description', 'period_to'],
|
||||
archival_items: ['label', 'description'],
|
||||
|
||||
galleries: ['name'],
|
||||
periods: ['name'],
|
||||
|
||||
tags: ['name'],
|
||||
};
|
||||
|
||||
@ -127,28 +127,6 @@ const CardArchival_items = ({
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Period From
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.dateFormatter(item.period_from)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Period To
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{item.period_to}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>Tags</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
|
||||
@ -89,18 +89,6 @@ const ListArchival_items = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Period From</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.dateFormatter(item.period_from)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Period To</p>
|
||||
<p className={'line-clamp-2'}>{item.period_to}</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Tags</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
|
||||
@ -96,34 +96,6 @@ export const loadColumns = async (
|
||||
type: 'boolean',
|
||||
},
|
||||
|
||||
{
|
||||
field: 'period_from',
|
||||
headerName: 'Period From',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
type: 'date',
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
new Date(params.row.period_from),
|
||||
},
|
||||
|
||||
{
|
||||
field: 'period_to',
|
||||
headerName: 'Period To',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'tags',
|
||||
headerName: 'Tags',
|
||||
|
||||
@ -11,7 +11,7 @@ import Link from 'next/link';
|
||||
import { hasPermission } from '../../helpers/userPermissions';
|
||||
|
||||
type Props = {
|
||||
galleries: any[];
|
||||
periods: any[];
|
||||
loading: boolean;
|
||||
onDelete: (id: string) => void;
|
||||
currentPage: number;
|
||||
@ -19,8 +19,8 @@ type Props = {
|
||||
onPageChange: (page: number) => void;
|
||||
};
|
||||
|
||||
const CardGalleries = ({
|
||||
galleries,
|
||||
const CardPeriods = ({
|
||||
periods,
|
||||
loading,
|
||||
onDelete,
|
||||
currentPage,
|
||||
@ -36,7 +36,7 @@ const CardGalleries = ({
|
||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||
|
||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_GALLERIES');
|
||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PERIODS');
|
||||
|
||||
return (
|
||||
<div className={'p-4'}>
|
||||
@ -46,7 +46,7 @@ const CardGalleries = ({
|
||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
||||
>
|
||||
{!loading &&
|
||||
galleries.map((item, index) => (
|
||||
periods.map((item, index) => (
|
||||
<li
|
||||
key={item.id}
|
||||
className={`overflow-hidden ${
|
||||
@ -56,27 +56,21 @@ const CardGalleries = ({
|
||||
}`}
|
||||
>
|
||||
<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`}
|
||||
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={`/galleries/galleries-view/?id=${item.id}`}
|
||||
className={'cursor-pointer'}
|
||||
href={`/periods/periods-view/?id=${item.id}`}
|
||||
className='text-lg font-bold leading-6 line-clamp-1'
|
||||
>
|
||||
<ImageField
|
||||
name={'Avatar'}
|
||||
image={item.background_image}
|
||||
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.id}</p>
|
||||
{item.id}
|
||||
</Link>
|
||||
|
||||
<div className='ml-auto md:absolute md:top-0 md:right-0 '>
|
||||
<div className='ml-auto '>
|
||||
<ListActionsPopover
|
||||
onDelete={onDelete}
|
||||
itemId={item.id}
|
||||
pathEdit={`/galleries/galleries-edit/?id=${item.id}`}
|
||||
pathView={`/galleries/galleries-view/?id=${item.id}`}
|
||||
pathEdit={`/periods/periods-edit/?id=${item.id}`}
|
||||
pathView={`/periods/periods-view/?id=${item.id}`}
|
||||
hasUpdatePermission={hasUpdatePermission}
|
||||
/>
|
||||
</div>
|
||||
@ -104,22 +98,29 @@ const CardGalleries = ({
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Background Image
|
||||
Period From
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium'>
|
||||
<ImageField
|
||||
name={'Avatar'}
|
||||
image={item.background_image}
|
||||
className='mx-auto w-8 h-8'
|
||||
/>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.dateTimeFormatter(item.period_from)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-x-4 py-3'>
|
||||
<dt className=' text-gray-500 dark:text-dark-600'>
|
||||
Period To
|
||||
</dt>
|
||||
<dd className='flex items-start gap-x-2'>
|
||||
<div className='font-medium line-clamp-4'>
|
||||
{dataFormatter.dateTimeFormatter(item.period_to)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
))}
|
||||
{!loading && galleries.length === 0 && (
|
||||
{!loading && periods.length === 0 && (
|
||||
<div className='col-span-full flex items-center justify-center h-40'>
|
||||
<p className=''>No data to display</p>
|
||||
</div>
|
||||
@ -136,4 +137,4 @@ const CardGalleries = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default CardGalleries;
|
||||
export default CardPeriods;
|
||||
@ -12,7 +12,7 @@ import Link from 'next/link';
|
||||
import { hasPermission } from '../../helpers/userPermissions';
|
||||
|
||||
type Props = {
|
||||
galleries: any[];
|
||||
periods: any[];
|
||||
loading: boolean;
|
||||
onDelete: (id: string) => void;
|
||||
currentPage: number;
|
||||
@ -20,8 +20,8 @@ type Props = {
|
||||
onPageChange: (page: number) => void;
|
||||
};
|
||||
|
||||
const ListGalleries = ({
|
||||
galleries,
|
||||
const ListPeriods = ({
|
||||
periods,
|
||||
loading,
|
||||
onDelete,
|
||||
currentPage,
|
||||
@ -29,7 +29,7 @@ const ListGalleries = ({
|
||||
onPageChange,
|
||||
}: Props) => {
|
||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_GALLERIES');
|
||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PERIODS');
|
||||
|
||||
const corners = useAppSelector((state) => state.style.corners);
|
||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
||||
@ -39,23 +39,14 @@ const ListGalleries = ({
|
||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
||||
{loading && <LoadingSpinner />}
|
||||
{!loading &&
|
||||
galleries.map((item) => (
|
||||
periods.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.background_image}
|
||||
className='w-24 h-24 rounded-l overflow-hidden hidden md:block'
|
||||
imageClassName={
|
||||
'rounded-l rounded-r-none h-full object-cover'
|
||||
}
|
||||
/>
|
||||
|
||||
<Link
|
||||
href={`/galleries/galleries-view/?id=${item.id}`}
|
||||
href={`/periods/periods-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'
|
||||
}
|
||||
@ -77,28 +68,31 @@ const ListGalleries = ({
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>
|
||||
Background Image
|
||||
<p className={'text-xs text-gray-500 '}>Period From</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.dateTimeFormatter(item.period_from)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'flex-1 px-3'}>
|
||||
<p className={'text-xs text-gray-500 '}>Period To</p>
|
||||
<p className={'line-clamp-2'}>
|
||||
{dataFormatter.dateTimeFormatter(item.period_to)}
|
||||
</p>
|
||||
<ImageField
|
||||
name={'Avatar'}
|
||||
image={item.background_image}
|
||||
className='mx-auto w-8 h-8'
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
<ListActionsPopover
|
||||
onDelete={onDelete}
|
||||
itemId={item.id}
|
||||
pathEdit={`/galleries/galleries-edit/?id=${item.id}`}
|
||||
pathView={`/galleries/galleries-view/?id=${item.id}`}
|
||||
pathEdit={`/periods/periods-edit/?id=${item.id}`}
|
||||
pathView={`/periods/periods-view/?id=${item.id}`}
|
||||
hasUpdatePermission={hasUpdatePermission}
|
||||
/>
|
||||
</div>
|
||||
</CardBox>
|
||||
</div>
|
||||
))}
|
||||
{!loading && galleries.length === 0 && (
|
||||
{!loading && periods.length === 0 && (
|
||||
<div className='col-span-full flex items-center justify-center h-40'>
|
||||
<p className=''>No data to display</p>
|
||||
</div>
|
||||
@ -115,4 +109,4 @@ const ListGalleries = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default ListGalleries;
|
||||
export default ListPeriods;
|
||||
@ -10,19 +10,19 @@ import {
|
||||
deleteItem,
|
||||
setRefetch,
|
||||
deleteItemsByIds,
|
||||
} from '../../stores/galleries/galleriesSlice';
|
||||
} from '../../stores/periods/periodsSlice';
|
||||
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 './configureGalleriesCols';
|
||||
import { loadColumns } from './configurePeriodsCols';
|
||||
import _ from 'lodash';
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import { dataGridStyles } from '../../styles';
|
||||
|
||||
const perPage = 10;
|
||||
|
||||
const TableSampleGalleries = ({
|
||||
const TableSamplePeriods = ({
|
||||
filterItems,
|
||||
setFilterItems,
|
||||
filters,
|
||||
@ -47,12 +47,12 @@ const TableSampleGalleries = ({
|
||||
]);
|
||||
|
||||
const {
|
||||
galleries,
|
||||
periods,
|
||||
loading,
|
||||
count,
|
||||
notify: galleriesNotify,
|
||||
notify: periodsNotify,
|
||||
refetch,
|
||||
} = useAppSelector((state) => state.galleries);
|
||||
} = useAppSelector((state) => state.periods);
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||
@ -73,13 +73,10 @@ const TableSampleGalleries = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (galleriesNotify.showNotification) {
|
||||
notify(
|
||||
galleriesNotify.typeNotification,
|
||||
galleriesNotify.textNotification,
|
||||
);
|
||||
if (periodsNotify.showNotification) {
|
||||
notify(periodsNotify.typeNotification, periodsNotify.textNotification);
|
||||
}
|
||||
}, [galleriesNotify.showNotification]);
|
||||
}, [periodsNotify.showNotification]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentUser) return;
|
||||
@ -184,7 +181,7 @@ const TableSampleGalleries = ({
|
||||
useEffect(() => {
|
||||
if (!currentUser) return;
|
||||
|
||||
loadColumns(handleDeleteModalAction, `galleries`, currentUser).then(
|
||||
loadColumns(handleDeleteModalAction, `periods`, currentUser).then(
|
||||
(newCols) => setColumns(newCols),
|
||||
);
|
||||
}, [currentUser]);
|
||||
@ -218,7 +215,7 @@ const TableSampleGalleries = ({
|
||||
sx={dataGridStyles}
|
||||
className={'datagrid--table'}
|
||||
getRowClassName={() => `datagrid--row`}
|
||||
rows={galleries ?? []}
|
||||
rows={periods ?? []}
|
||||
columns={columns}
|
||||
initialState={{
|
||||
pagination: {
|
||||
@ -481,4 +478,4 @@ const TableSampleGalleries = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default TableSampleGalleries;
|
||||
export default TableSamplePeriods;
|
||||
@ -35,7 +35,7 @@ export const loadColumns = async (
|
||||
}
|
||||
}
|
||||
|
||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_GALLERIES');
|
||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_PERIODS');
|
||||
|
||||
return [
|
||||
{
|
||||
@ -70,23 +70,35 @@ export const loadColumns = async (
|
||||
},
|
||||
|
||||
{
|
||||
field: 'background_image',
|
||||
headerName: 'Background Image',
|
||||
field: 'period_from',
|
||||
headerName: 'Period From',
|
||||
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?.background_image}
|
||||
className='w-24 h-24 mx-auto lg:w-6 lg:h-6'
|
||||
/>
|
||||
),
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
type: 'dateTime',
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
new Date(params.row.period_from),
|
||||
},
|
||||
|
||||
{
|
||||
field: 'period_to',
|
||||
headerName: 'Period To',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
type: 'dateTime',
|
||||
valueGetter: (params: GridValueGetterParams) =>
|
||||
new Date(params.row.period_to),
|
||||
},
|
||||
|
||||
{
|
||||
@ -101,8 +113,8 @@ export const loadColumns = async (
|
||||
<ListActionsPopover
|
||||
onDelete={onDelete}
|
||||
itemId={params?.row?.id}
|
||||
pathEdit={`/galleries/galleries-edit/?id=${params?.row?.id}`}
|
||||
pathView={`/galleries/galleries-view/?id=${params?.row?.id}`}
|
||||
pathEdit={`/periods/periods-edit/?id=${params?.row?.id}`}
|
||||
pathView={`/periods/periods-view/?id=${params?.row?.id}`}
|
||||
hasUpdatePermission={hasUpdatePermission}
|
||||
/>
|
||||
</div>,
|
||||
@ -25,12 +25,12 @@ const menuAside: MenuAsideItem[] = [
|
||||
permissions: 'READ_ARCHIVAL_ITEMS',
|
||||
},
|
||||
{
|
||||
href: '/galleries/galleries-list',
|
||||
label: 'Galleries',
|
||||
href: '/periods/periods-list',
|
||||
label: 'Periods',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_GALLERIES',
|
||||
permissions: 'READ_PERIODS',
|
||||
},
|
||||
{
|
||||
href: '/tags/tags-list',
|
||||
|
||||
@ -44,10 +44,6 @@ const EditArchival_items = () => {
|
||||
|
||||
isPublished: false,
|
||||
|
||||
period_from: new Date(),
|
||||
|
||||
period_to: '',
|
||||
|
||||
tags: [],
|
||||
};
|
||||
const [initialValues, setInitialValues] = useState(initVals);
|
||||
@ -135,28 +131,6 @@ const EditArchival_items = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period From'>
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd'
|
||||
selected={
|
||||
initialValues.period_from
|
||||
? new Date(
|
||||
dayjs(initialValues.period_from).format(
|
||||
'YYYY-MM-DD hh:mm',
|
||||
),
|
||||
)
|
||||
: null
|
||||
}
|
||||
onChange={(date) =>
|
||||
setInitialValues({ ...initialValues, period_from: date })
|
||||
}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period To'>
|
||||
<Field name='period_to' placeholder='Period To' />
|
||||
</FormField>
|
||||
|
||||
<FormField label='Tags' labelFor='tags'>
|
||||
<Field
|
||||
name='tags'
|
||||
|
||||
@ -44,10 +44,6 @@ const EditArchival_itemsPage = () => {
|
||||
|
||||
isPublished: false,
|
||||
|
||||
period_from: new Date(),
|
||||
|
||||
period_to: '',
|
||||
|
||||
tags: [],
|
||||
};
|
||||
const [initialValues, setInitialValues] = useState(initVals);
|
||||
@ -133,28 +129,6 @@ const EditArchival_itemsPage = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period From'>
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd'
|
||||
selected={
|
||||
initialValues.period_from
|
||||
? new Date(
|
||||
dayjs(initialValues.period_from).format(
|
||||
'YYYY-MM-DD hh:mm',
|
||||
),
|
||||
)
|
||||
: null
|
||||
}
|
||||
onChange={(date) =>
|
||||
setInitialValues({ ...initialValues, period_from: date })
|
||||
}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period To'>
|
||||
<Field name='period_to' placeholder='Period To' />
|
||||
</FormField>
|
||||
|
||||
<FormField label='Tags' labelFor='tags'>
|
||||
<Field
|
||||
name='tags'
|
||||
|
||||
@ -34,7 +34,6 @@ const Archival_itemsTablesPage = () => {
|
||||
const [filters] = useState([
|
||||
{ label: 'Label', title: 'label' },
|
||||
{ label: 'Description', title: 'description' },
|
||||
{ label: 'Period To', title: 'period_to' },
|
||||
|
||||
{ label: 'Tags', title: 'tags' },
|
||||
]);
|
||||
|
||||
@ -41,11 +41,6 @@ const initialValues = {
|
||||
|
||||
isPublished: false,
|
||||
|
||||
period_from: '',
|
||||
datePeriod_from: '',
|
||||
|
||||
period_to: '',
|
||||
|
||||
tags: [],
|
||||
};
|
||||
|
||||
@ -108,18 +103,6 @@ const Archival_itemsNew = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period From'>
|
||||
<Field
|
||||
type='date'
|
||||
name='period_from'
|
||||
placeholder='Period From'
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period To'>
|
||||
<Field name='period_to' placeholder='Period To' />
|
||||
</FormField>
|
||||
|
||||
<FormField label='Tags' labelFor='tags'>
|
||||
<Field
|
||||
name='tags'
|
||||
|
||||
@ -34,7 +34,6 @@ const Archival_itemsTablesPage = () => {
|
||||
const [filters] = useState([
|
||||
{ label: 'Label', title: 'label' },
|
||||
{ label: 'Description', title: 'description' },
|
||||
{ label: 'Period To', title: 'period_to' },
|
||||
|
||||
{ label: 'Tags', title: 'tags' },
|
||||
]);
|
||||
|
||||
@ -88,32 +88,6 @@ const Archival_itemsView = () => {
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period From'>
|
||||
{archival_items.period_from ? (
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd'
|
||||
showTimeSelect
|
||||
selected={
|
||||
archival_items.period_from
|
||||
? new Date(
|
||||
dayjs(archival_items.period_from).format(
|
||||
'YYYY-MM-DD hh:mm',
|
||||
),
|
||||
)
|
||||
: null
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
) : (
|
||||
<p>No Period From</p>
|
||||
)}
|
||||
</FormField>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Period To</p>
|
||||
<p>{archival_items?.period_to}</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Tags</p>
|
||||
<CardBox
|
||||
|
||||
@ -30,7 +30,7 @@ const Dashboard = () => {
|
||||
|
||||
const [users, setUsers] = React.useState(loadingMessage);
|
||||
const [archival_items, setArchival_items] = React.useState(loadingMessage);
|
||||
const [galleries, setGalleries] = React.useState(loadingMessage);
|
||||
const [periods, setPeriods] = React.useState(loadingMessage);
|
||||
const [tags, setTags] = React.useState(loadingMessage);
|
||||
const [roles, setRoles] = React.useState(loadingMessage);
|
||||
const [permissions, setPermissions] = React.useState(loadingMessage);
|
||||
@ -47,7 +47,7 @@ const Dashboard = () => {
|
||||
const entities = [
|
||||
'users',
|
||||
'archival_items',
|
||||
'galleries',
|
||||
'periods',
|
||||
'tags',
|
||||
'roles',
|
||||
'permissions',
|
||||
@ -55,7 +55,7 @@ const Dashboard = () => {
|
||||
const fns = [
|
||||
setUsers,
|
||||
setArchival_items,
|
||||
setGalleries,
|
||||
setPeriods,
|
||||
setTags,
|
||||
setRoles,
|
||||
setPermissions,
|
||||
@ -235,8 +235,8 @@ const Dashboard = () => {
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{hasPermission(currentUser, 'READ_GALLERIES') && (
|
||||
<Link href={'/galleries/galleries-list'}>
|
||||
{hasPermission(currentUser, 'READ_PERIODS') && (
|
||||
<Link href={'/periods/periods-list'}>
|
||||
<div
|
||||
className={`${
|
||||
corners !== 'rounded-full' ? corners : 'rounded-3xl'
|
||||
@ -245,10 +245,10 @@ const Dashboard = () => {
|
||||
<div className='flex justify-between align-center'>
|
||||
<div>
|
||||
<div className='text-lg leading-tight text-gray-500 dark:text-gray-400'>
|
||||
Galleries
|
||||
Periods
|
||||
</div>
|
||||
<div className='text-3xl leading-tight font-semibold'>
|
||||
{galleries}
|
||||
{periods}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@ -25,14 +25,14 @@ import { SelectFieldMany } from '../../components/SelectFieldMany';
|
||||
import { SwitchField } from '../../components/SwitchField';
|
||||
import { RichTextField } from '../../components/RichTextField';
|
||||
|
||||
import { update, fetch } from '../../stores/galleries/galleriesSlice';
|
||||
import { update, fetch } from '../../stores/periods/periodsSlice';
|
||||
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 EditGalleries = () => {
|
||||
const EditPeriods = () => {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const initVals = {
|
||||
@ -40,50 +40,50 @@ const EditGalleries = () => {
|
||||
|
||||
archival_items: [],
|
||||
|
||||
background_image: [],
|
||||
period_from: new Date(),
|
||||
|
||||
period_to: new Date(),
|
||||
};
|
||||
const [initialValues, setInitialValues] = useState(initVals);
|
||||
|
||||
const { galleries } = useAppSelector((state) => state.galleries);
|
||||
const { periods } = useAppSelector((state) => state.periods);
|
||||
|
||||
const { galleriesId } = router.query;
|
||||
const { periodsId } = router.query;
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id: galleriesId }));
|
||||
}, [galleriesId]);
|
||||
dispatch(fetch({ id: periodsId }));
|
||||
}, [periodsId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof galleries === 'object') {
|
||||
setInitialValues(galleries);
|
||||
if (typeof periods === 'object') {
|
||||
setInitialValues(periods);
|
||||
}
|
||||
}, [galleries]);
|
||||
}, [periods]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof galleries === 'object') {
|
||||
if (typeof periods === 'object') {
|
||||
const newInitialVal = { ...initVals };
|
||||
|
||||
Object.keys(initVals).forEach(
|
||||
(el) => (newInitialVal[el] = galleries[el]),
|
||||
);
|
||||
Object.keys(initVals).forEach((el) => (newInitialVal[el] = periods[el]));
|
||||
|
||||
setInitialValues(newInitialVal);
|
||||
}
|
||||
}, [galleries]);
|
||||
}, [periods]);
|
||||
|
||||
const handleSubmit = async (data) => {
|
||||
await dispatch(update({ id: galleriesId, data }));
|
||||
await router.push('/galleries/galleries-list');
|
||||
await dispatch(update({ id: periodsId, data }));
|
||||
await router.push('/periods/periods-list');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('Edit galleries')}</title>
|
||||
<title>{getPageTitle('Edit periods')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={mdiChartTimelineVariant}
|
||||
title={'Edit galleries'}
|
||||
title={'Edit periods'}
|
||||
main
|
||||
>
|
||||
{''}
|
||||
@ -110,20 +110,42 @@ const EditGalleries = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField>
|
||||
<Field
|
||||
label='Background Image'
|
||||
color='info'
|
||||
icon={mdiUpload}
|
||||
path={'galleries/background_image'}
|
||||
name='background_image'
|
||||
id='background_image'
|
||||
schema={{
|
||||
size: undefined,
|
||||
formats: undefined,
|
||||
}}
|
||||
component={FormImagePicker}
|
||||
></Field>
|
||||
<FormField label='Period From'>
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd hh:mm'
|
||||
showTimeSelect
|
||||
selected={
|
||||
initialValues.period_from
|
||||
? new Date(
|
||||
dayjs(initialValues.period_from).format(
|
||||
'YYYY-MM-DD hh:mm',
|
||||
),
|
||||
)
|
||||
: null
|
||||
}
|
||||
onChange={(date) =>
|
||||
setInitialValues({ ...initialValues, period_from: date })
|
||||
}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period To'>
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd hh:mm'
|
||||
showTimeSelect
|
||||
selected={
|
||||
initialValues.period_to
|
||||
? new Date(
|
||||
dayjs(initialValues.period_to).format(
|
||||
'YYYY-MM-DD hh:mm',
|
||||
),
|
||||
)
|
||||
: null
|
||||
}
|
||||
onChange={(date) =>
|
||||
setInitialValues({ ...initialValues, period_to: date })
|
||||
}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
@ -135,7 +157,7 @@ const EditGalleries = () => {
|
||||
color='danger'
|
||||
outline
|
||||
label='Cancel'
|
||||
onClick={() => router.push('/galleries/galleries-list')}
|
||||
onClick={() => router.push('/periods/periods-list')}
|
||||
/>
|
||||
</BaseButtons>
|
||||
</Form>
|
||||
@ -146,12 +168,12 @@ const EditGalleries = () => {
|
||||
);
|
||||
};
|
||||
|
||||
EditGalleries.getLayout = function getLayout(page: ReactElement) {
|
||||
EditPeriods.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'UPDATE_GALLERIES'}>
|
||||
<LayoutAuthenticated permission={'UPDATE_PERIODS'}>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditGalleries;
|
||||
export default EditPeriods;
|
||||
@ -25,14 +25,14 @@ import { SelectFieldMany } from '../../components/SelectFieldMany';
|
||||
import { SwitchField } from '../../components/SwitchField';
|
||||
import { RichTextField } from '../../components/RichTextField';
|
||||
|
||||
import { update, fetch } from '../../stores/galleries/galleriesSlice';
|
||||
import { update, fetch } from '../../stores/periods/periodsSlice';
|
||||
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 EditGalleriesPage = () => {
|
||||
const EditPeriodsPage = () => {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const initVals = {
|
||||
@ -40,11 +40,13 @@ const EditGalleriesPage = () => {
|
||||
|
||||
archival_items: [],
|
||||
|
||||
background_image: [],
|
||||
period_from: new Date(),
|
||||
|
||||
period_to: new Date(),
|
||||
};
|
||||
const [initialValues, setInitialValues] = useState(initVals);
|
||||
|
||||
const { galleries } = useAppSelector((state) => state.galleries);
|
||||
const { periods } = useAppSelector((state) => state.periods);
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
@ -53,35 +55,33 @@ const EditGalleriesPage = () => {
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof galleries === 'object') {
|
||||
setInitialValues(galleries);
|
||||
if (typeof periods === 'object') {
|
||||
setInitialValues(periods);
|
||||
}
|
||||
}, [galleries]);
|
||||
}, [periods]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof galleries === 'object') {
|
||||
if (typeof periods === 'object') {
|
||||
const newInitialVal = { ...initVals };
|
||||
Object.keys(initVals).forEach(
|
||||
(el) => (newInitialVal[el] = galleries[el]),
|
||||
);
|
||||
Object.keys(initVals).forEach((el) => (newInitialVal[el] = periods[el]));
|
||||
setInitialValues(newInitialVal);
|
||||
}
|
||||
}, [galleries]);
|
||||
}, [periods]);
|
||||
|
||||
const handleSubmit = async (data) => {
|
||||
await dispatch(update({ id: id, data }));
|
||||
await router.push('/galleries/galleries-list');
|
||||
await router.push('/periods/periods-list');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('Edit galleries')}</title>
|
||||
<title>{getPageTitle('Edit periods')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={mdiChartTimelineVariant}
|
||||
title={'Edit galleries'}
|
||||
title={'Edit periods'}
|
||||
main
|
||||
>
|
||||
{''}
|
||||
@ -108,20 +108,42 @@ const EditGalleriesPage = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField>
|
||||
<Field
|
||||
label='Background Image'
|
||||
color='info'
|
||||
icon={mdiUpload}
|
||||
path={'galleries/background_image'}
|
||||
name='background_image'
|
||||
id='background_image'
|
||||
schema={{
|
||||
size: undefined,
|
||||
formats: undefined,
|
||||
}}
|
||||
component={FormImagePicker}
|
||||
></Field>
|
||||
<FormField label='Period From'>
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd hh:mm'
|
||||
showTimeSelect
|
||||
selected={
|
||||
initialValues.period_from
|
||||
? new Date(
|
||||
dayjs(initialValues.period_from).format(
|
||||
'YYYY-MM-DD hh:mm',
|
||||
),
|
||||
)
|
||||
: null
|
||||
}
|
||||
onChange={(date) =>
|
||||
setInitialValues({ ...initialValues, period_from: date })
|
||||
}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period To'>
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd hh:mm'
|
||||
showTimeSelect
|
||||
selected={
|
||||
initialValues.period_to
|
||||
? new Date(
|
||||
dayjs(initialValues.period_to).format(
|
||||
'YYYY-MM-DD hh:mm',
|
||||
),
|
||||
)
|
||||
: null
|
||||
}
|
||||
onChange={(date) =>
|
||||
setInitialValues({ ...initialValues, period_to: date })
|
||||
}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
@ -133,7 +155,7 @@ const EditGalleriesPage = () => {
|
||||
color='danger'
|
||||
outline
|
||||
label='Cancel'
|
||||
onClick={() => router.push('/galleries/galleries-list')}
|
||||
onClick={() => router.push('/periods/periods-list')}
|
||||
/>
|
||||
</BaseButtons>
|
||||
</Form>
|
||||
@ -144,12 +166,12 @@ const EditGalleriesPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
EditGalleriesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
EditPeriodsPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'UPDATE_GALLERIES'}>
|
||||
<LayoutAuthenticated permission={'UPDATE_PERIODS'}>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditGalleriesPage;
|
||||
export default EditPeriodsPage;
|
||||
@ -7,18 +7,18 @@ import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import SectionMain from '../../components/SectionMain';
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
|
||||
import { getPageTitle } from '../../config';
|
||||
import TableGalleries from '../../components/Galleries/TableGalleries';
|
||||
import TablePeriods from '../../components/Periods/TablePeriods';
|
||||
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/galleries/galleriesSlice';
|
||||
import { setRefetch, uploadCsv } from '../../stores/periods/periodsSlice';
|
||||
|
||||
import { hasPermission } from '../../helpers/userPermissions';
|
||||
|
||||
const GalleriesTablesPage = () => {
|
||||
const PeriodsTablesPage = () => {
|
||||
const [filterItems, setFilterItems] = useState([]);
|
||||
const [csvFile, setCsvFile] = useState<File | null>(null);
|
||||
const [isModalActive, setIsModalActive] = useState(false);
|
||||
@ -31,11 +31,14 @@ const GalleriesTablesPage = () => {
|
||||
const [filters] = useState([
|
||||
{ label: 'Name', title: 'name' },
|
||||
|
||||
{ label: 'Period From', title: 'period_from', date: 'true' },
|
||||
{ label: 'Period To', title: 'period_to', date: 'true' },
|
||||
|
||||
{ label: 'Archival Items', title: 'archival_items' },
|
||||
]);
|
||||
|
||||
const hasCreatePermission =
|
||||
currentUser && hasPermission(currentUser, 'CREATE_GALLERIES');
|
||||
currentUser && hasPermission(currentUser, 'CREATE_PERIODS');
|
||||
|
||||
const addFilter = () => {
|
||||
const newItem = {
|
||||
@ -51,9 +54,9 @@ const GalleriesTablesPage = () => {
|
||||
setFilterItems([...filterItems, newItem]);
|
||||
};
|
||||
|
||||
const getGalleriesCSV = async () => {
|
||||
const getPeriodsCSV = async () => {
|
||||
const response = await axios({
|
||||
url: '/galleries?filetype=csv',
|
||||
url: '/periods?filetype=csv',
|
||||
method: 'GET',
|
||||
responseType: 'blob',
|
||||
});
|
||||
@ -61,7 +64,7 @@ const GalleriesTablesPage = () => {
|
||||
const blob = new Blob([response.data], { type: type });
|
||||
const link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = 'galleriesCSV.csv';
|
||||
link.download = 'periodsCSV.csv';
|
||||
link.click();
|
||||
};
|
||||
|
||||
@ -81,12 +84,12 @@ const GalleriesTablesPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('Galleries')}</title>
|
||||
<title>{getPageTitle('Periods')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={mdiChartTimelineVariant}
|
||||
title='Galleries'
|
||||
title='Periods'
|
||||
main
|
||||
>
|
||||
{''}
|
||||
@ -95,7 +98,7 @@ const GalleriesTablesPage = () => {
|
||||
{hasCreatePermission && (
|
||||
<BaseButton
|
||||
className={'mr-3'}
|
||||
href={'/galleries/galleries-new'}
|
||||
href={'/periods/periods-new'}
|
||||
color='info'
|
||||
label='New Item'
|
||||
/>
|
||||
@ -111,7 +114,7 @@ const GalleriesTablesPage = () => {
|
||||
className={'mr-3'}
|
||||
color='info'
|
||||
label='Download CSV'
|
||||
onClick={getGalleriesCSV}
|
||||
onClick={getPeriodsCSV}
|
||||
/>
|
||||
|
||||
{hasCreatePermission && (
|
||||
@ -128,7 +131,7 @@ const GalleriesTablesPage = () => {
|
||||
</CardBox>
|
||||
|
||||
<CardBox className='mb-6' hasTable>
|
||||
<TableGalleries
|
||||
<TablePeriods
|
||||
filterItems={filterItems}
|
||||
setFilterItems={setFilterItems}
|
||||
filters={filters}
|
||||
@ -155,12 +158,12 @@ const GalleriesTablesPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
GalleriesTablesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
PeriodsTablesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'READ_GALLERIES'}>
|
||||
<LayoutAuthenticated permission={'READ_PERIODS'}>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default GalleriesTablesPage;
|
||||
export default PeriodsTablesPage;
|
||||
@ -27,7 +27,7 @@ import { SelectField } from '../../components/SelectField';
|
||||
import { SelectFieldMany } from '../../components/SelectFieldMany';
|
||||
import { RichTextField } from '../../components/RichTextField';
|
||||
|
||||
import { create } from '../../stores/galleries/galleriesSlice';
|
||||
import { create } from '../../stores/periods/periodsSlice';
|
||||
import { useAppDispatch } from '../../stores/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import moment from 'moment';
|
||||
@ -37,16 +37,18 @@ const initialValues = {
|
||||
|
||||
archival_items: [],
|
||||
|
||||
background_image: [],
|
||||
period_from: '',
|
||||
|
||||
period_to: '',
|
||||
};
|
||||
|
||||
const GalleriesNew = () => {
|
||||
const PeriodsNew = () => {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleSubmit = async (data) => {
|
||||
await dispatch(create(data));
|
||||
await router.push('/galleries/galleries-list');
|
||||
await router.push('/periods/periods-list');
|
||||
};
|
||||
return (
|
||||
<>
|
||||
@ -81,20 +83,20 @@ const GalleriesNew = () => {
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
<FormField>
|
||||
<FormField label='Period From'>
|
||||
<Field
|
||||
label='Background Image'
|
||||
color='info'
|
||||
icon={mdiUpload}
|
||||
path={'galleries/background_image'}
|
||||
name='background_image'
|
||||
id='background_image'
|
||||
schema={{
|
||||
size: undefined,
|
||||
formats: undefined,
|
||||
}}
|
||||
component={FormImagePicker}
|
||||
></Field>
|
||||
type='datetime-local'
|
||||
name='period_from'
|
||||
placeholder='Period From'
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period To'>
|
||||
<Field
|
||||
type='datetime-local'
|
||||
name='period_to'
|
||||
placeholder='Period To'
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
@ -106,7 +108,7 @@ const GalleriesNew = () => {
|
||||
color='danger'
|
||||
outline
|
||||
label='Cancel'
|
||||
onClick={() => router.push('/galleries/galleries-list')}
|
||||
onClick={() => router.push('/periods/periods-list')}
|
||||
/>
|
||||
</BaseButtons>
|
||||
</Form>
|
||||
@ -117,12 +119,12 @@ const GalleriesNew = () => {
|
||||
);
|
||||
};
|
||||
|
||||
GalleriesNew.getLayout = function getLayout(page: ReactElement) {
|
||||
PeriodsNew.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'CREATE_GALLERIES'}>
|
||||
<LayoutAuthenticated permission={'CREATE_PERIODS'}>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default GalleriesNew;
|
||||
export default PeriodsNew;
|
||||
@ -7,18 +7,18 @@ import LayoutAuthenticated from '../../layouts/Authenticated';
|
||||
import SectionMain from '../../components/SectionMain';
|
||||
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
|
||||
import { getPageTitle } from '../../config';
|
||||
import TableGalleries from '../../components/Galleries/TableGalleries';
|
||||
import TablePeriods from '../../components/Periods/TablePeriods';
|
||||
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/galleries/galleriesSlice';
|
||||
import { setRefetch, uploadCsv } from '../../stores/periods/periodsSlice';
|
||||
|
||||
import { hasPermission } from '../../helpers/userPermissions';
|
||||
|
||||
const GalleriesTablesPage = () => {
|
||||
const PeriodsTablesPage = () => {
|
||||
const [filterItems, setFilterItems] = useState([]);
|
||||
const [csvFile, setCsvFile] = useState<File | null>(null);
|
||||
const [isModalActive, setIsModalActive] = useState(false);
|
||||
@ -31,11 +31,14 @@ const GalleriesTablesPage = () => {
|
||||
const [filters] = useState([
|
||||
{ label: 'Name', title: 'name' },
|
||||
|
||||
{ label: 'Period From', title: 'period_from', date: 'true' },
|
||||
{ label: 'Period To', title: 'period_to', date: 'true' },
|
||||
|
||||
{ label: 'Archival Items', title: 'archival_items' },
|
||||
]);
|
||||
|
||||
const hasCreatePermission =
|
||||
currentUser && hasPermission(currentUser, 'CREATE_GALLERIES');
|
||||
currentUser && hasPermission(currentUser, 'CREATE_PERIODS');
|
||||
|
||||
const addFilter = () => {
|
||||
const newItem = {
|
||||
@ -51,9 +54,9 @@ const GalleriesTablesPage = () => {
|
||||
setFilterItems([...filterItems, newItem]);
|
||||
};
|
||||
|
||||
const getGalleriesCSV = async () => {
|
||||
const getPeriodsCSV = async () => {
|
||||
const response = await axios({
|
||||
url: '/galleries?filetype=csv',
|
||||
url: '/periods?filetype=csv',
|
||||
method: 'GET',
|
||||
responseType: 'blob',
|
||||
});
|
||||
@ -61,7 +64,7 @@ const GalleriesTablesPage = () => {
|
||||
const blob = new Blob([response.data], { type: type });
|
||||
const link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = 'galleriesCSV.csv';
|
||||
link.download = 'periodsCSV.csv';
|
||||
link.click();
|
||||
};
|
||||
|
||||
@ -81,12 +84,12 @@ const GalleriesTablesPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('Galleries')}</title>
|
||||
<title>{getPageTitle('Periods')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={mdiChartTimelineVariant}
|
||||
title='Galleries'
|
||||
title='Periods'
|
||||
main
|
||||
>
|
||||
{''}
|
||||
@ -95,7 +98,7 @@ const GalleriesTablesPage = () => {
|
||||
{hasCreatePermission && (
|
||||
<BaseButton
|
||||
className={'mr-3'}
|
||||
href={'/galleries/galleries-new'}
|
||||
href={'/periods/periods-new'}
|
||||
color='info'
|
||||
label='New Item'
|
||||
/>
|
||||
@ -111,7 +114,7 @@ const GalleriesTablesPage = () => {
|
||||
className={'mr-3'}
|
||||
color='info'
|
||||
label='Download CSV'
|
||||
onClick={getGalleriesCSV}
|
||||
onClick={getPeriodsCSV}
|
||||
/>
|
||||
|
||||
{hasCreatePermission && (
|
||||
@ -127,7 +130,7 @@ const GalleriesTablesPage = () => {
|
||||
</div>
|
||||
</CardBox>
|
||||
<CardBox className='mb-6' hasTable>
|
||||
<TableGalleries
|
||||
<TablePeriods
|
||||
filterItems={filterItems}
|
||||
setFilterItems={setFilterItems}
|
||||
filters={filters}
|
||||
@ -154,12 +157,12 @@ const GalleriesTablesPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
GalleriesTablesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
PeriodsTablesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'READ_GALLERIES'}>
|
||||
<LayoutAuthenticated permission={'READ_PERIODS'}>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default GalleriesTablesPage;
|
||||
export default PeriodsTablesPage;
|
||||
@ -5,7 +5,7 @@ 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/galleries/galleriesSlice';
|
||||
import { fetch } from '../../stores/periods/periodsSlice';
|
||||
import { saveFile } from '../../helpers/fileSaver';
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from '../../components/ImageField';
|
||||
@ -20,10 +20,10 @@ import { mdiChartTimelineVariant } from '@mdi/js';
|
||||
import { SwitchField } from '../../components/SwitchField';
|
||||
import FormField from '../../components/FormField';
|
||||
|
||||
const GalleriesView = () => {
|
||||
const PeriodsView = () => {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const { galleries } = useAppSelector((state) => state.galleries);
|
||||
const { periods } = useAppSelector((state) => state.periods);
|
||||
|
||||
const { id } = router.query;
|
||||
|
||||
@ -39,24 +39,24 @@ const GalleriesView = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('View galleries')}</title>
|
||||
<title>{getPageTitle('View periods')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={mdiChartTimelineVariant}
|
||||
title={removeLastCharacter('View galleries')}
|
||||
title={removeLastCharacter('View periods')}
|
||||
main
|
||||
>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Edit'
|
||||
href={`/galleries/galleries-edit/?id=${id}`}
|
||||
href={`/periods/periods-edit/?id=${id}`}
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Name</p>
|
||||
<p>{galleries?.name}</p>
|
||||
<p>{periods?.name}</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
@ -74,16 +74,12 @@ const GalleriesView = () => {
|
||||
<th>Description</th>
|
||||
|
||||
<th>Is Published</th>
|
||||
|
||||
<th>Period From</th>
|
||||
|
||||
<th>Period To</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{galleries.archival_items &&
|
||||
Array.isArray(galleries.archival_items) &&
|
||||
galleries.archival_items.map((item: any) => (
|
||||
{periods.archival_items &&
|
||||
Array.isArray(periods.archival_items) &&
|
||||
periods.archival_items.map((item: any) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
onClick={() =>
|
||||
@ -99,42 +95,61 @@ const GalleriesView = () => {
|
||||
<td data-label='isPublished'>
|
||||
{dataFormatter.booleanFormatter(item.isPublished)}
|
||||
</td>
|
||||
|
||||
<td data-label='period_from'>
|
||||
{dataFormatter.dateFormatter(item.period_from)}
|
||||
</td>
|
||||
|
||||
<td data-label='period_to'>{item.period_to}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!galleries?.archival_items?.length && (
|
||||
{!periods?.archival_items?.length && (
|
||||
<div className={'text-center py-4'}>No data</div>
|
||||
)}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Background Image</p>
|
||||
{galleries?.background_image?.length ? (
|
||||
<ImageField
|
||||
name={'background_image'}
|
||||
image={galleries?.background_image}
|
||||
className='w-20 h-20'
|
||||
<FormField label='Period From'>
|
||||
{periods.period_from ? (
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd hh:mm'
|
||||
showTimeSelect
|
||||
selected={
|
||||
periods.period_from
|
||||
? new Date(
|
||||
dayjs(periods.period_from).format('YYYY-MM-DD hh:mm'),
|
||||
)
|
||||
: null
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
) : (
|
||||
<p>No Background Image</p>
|
||||
<p>No Period From</p>
|
||||
)}
|
||||
</div>
|
||||
</FormField>
|
||||
|
||||
<FormField label='Period To'>
|
||||
{periods.period_to ? (
|
||||
<DatePicker
|
||||
dateFormat='yyyy-MM-dd hh:mm'
|
||||
showTimeSelect
|
||||
selected={
|
||||
periods.period_to
|
||||
? new Date(
|
||||
dayjs(periods.period_to).format('YYYY-MM-DD hh:mm'),
|
||||
)
|
||||
: null
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
) : (
|
||||
<p>No Period To</p>
|
||||
)}
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Back'
|
||||
onClick={() => router.push('/galleries/galleries-list')}
|
||||
onClick={() => router.push('/periods/periods-list')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
@ -142,12 +157,12 @@ const GalleriesView = () => {
|
||||
);
|
||||
};
|
||||
|
||||
GalleriesView.getLayout = function getLayout(page: ReactElement) {
|
||||
PeriodsView.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated permission={'READ_GALLERIES'}>
|
||||
<LayoutAuthenticated permission={'READ_PERIODS'}>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
);
|
||||
};
|
||||
|
||||
export default GalleriesView;
|
||||
export default PeriodsView;
|
||||
@ -7,7 +7,7 @@ import {
|
||||
} from '../../helpers/notifyStateHandler';
|
||||
|
||||
interface MainState {
|
||||
galleries: any;
|
||||
periods: any;
|
||||
loading: boolean;
|
||||
count: number;
|
||||
refetch: boolean;
|
||||
@ -20,7 +20,7 @@ interface MainState {
|
||||
}
|
||||
|
||||
const initialState: MainState = {
|
||||
galleries: [],
|
||||
periods: [],
|
||||
loading: false,
|
||||
count: 0,
|
||||
refetch: false,
|
||||
@ -32,19 +32,19 @@ const initialState: MainState = {
|
||||
},
|
||||
};
|
||||
|
||||
export const fetch = createAsyncThunk('galleries/fetch', async (data: any) => {
|
||||
export const fetch = createAsyncThunk('periods/fetch', async (data: any) => {
|
||||
const { id, query } = data;
|
||||
const result = await axios.get(`galleries${query || (id ? `/${id}` : '')}`);
|
||||
const result = await axios.get(`periods${query || (id ? `/${id}` : '')}`);
|
||||
return id
|
||||
? result.data
|
||||
: { rows: result.data.rows, count: result.data.count };
|
||||
});
|
||||
|
||||
export const deleteItemsByIds = createAsyncThunk(
|
||||
'galleries/deleteByIds',
|
||||
'periods/deleteByIds',
|
||||
async (data: any, { rejectWithValue }) => {
|
||||
try {
|
||||
await axios.post('galleries/deleteByIds', { data });
|
||||
await axios.post('periods/deleteByIds', { data });
|
||||
} catch (error) {
|
||||
if (!error.response) {
|
||||
throw error;
|
||||
@ -56,10 +56,10 @@ export const deleteItemsByIds = createAsyncThunk(
|
||||
);
|
||||
|
||||
export const deleteItem = createAsyncThunk(
|
||||
'galleries/deleteGalleries',
|
||||
'periods/deletePeriods',
|
||||
async (id: string, { rejectWithValue }) => {
|
||||
try {
|
||||
await axios.delete(`galleries/${id}`);
|
||||
await axios.delete(`periods/${id}`);
|
||||
} catch (error) {
|
||||
if (!error.response) {
|
||||
throw error;
|
||||
@ -71,10 +71,10 @@ export const deleteItem = createAsyncThunk(
|
||||
);
|
||||
|
||||
export const create = createAsyncThunk(
|
||||
'galleries/createGalleries',
|
||||
'periods/createPeriods',
|
||||
async (data: any, { rejectWithValue }) => {
|
||||
try {
|
||||
const result = await axios.post('galleries', { data });
|
||||
const result = await axios.post('periods', { data });
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
if (!error.response) {
|
||||
@ -87,14 +87,14 @@ export const create = createAsyncThunk(
|
||||
);
|
||||
|
||||
export const uploadCsv = createAsyncThunk(
|
||||
'galleries/uploadCsv',
|
||||
'periods/uploadCsv',
|
||||
async (file: File, { rejectWithValue }) => {
|
||||
try {
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
data.append('filename', file.name);
|
||||
|
||||
const result = await axios.post('galleries/bulk-import', data, {
|
||||
const result = await axios.post('periods/bulk-import', data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
@ -112,10 +112,10 @@ export const uploadCsv = createAsyncThunk(
|
||||
);
|
||||
|
||||
export const update = createAsyncThunk(
|
||||
'galleries/updateGalleries',
|
||||
'periods/updatePeriods',
|
||||
async (payload: any, { rejectWithValue }) => {
|
||||
try {
|
||||
const result = await axios.put(`galleries/${payload.id}`, {
|
||||
const result = await axios.put(`periods/${payload.id}`, {
|
||||
id: payload.id,
|
||||
data: payload.data,
|
||||
});
|
||||
@ -130,8 +130,8 @@ export const update = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
export const galleriesSlice = createSlice({
|
||||
name: 'galleries',
|
||||
export const periodsSlice = createSlice({
|
||||
name: 'periods',
|
||||
initialState,
|
||||
reducers: {
|
||||
setRefetch: (state, action: PayloadAction<boolean>) => {
|
||||
@ -150,10 +150,10 @@ export const galleriesSlice = createSlice({
|
||||
|
||||
builder.addCase(fetch.fulfilled, (state, action) => {
|
||||
if (action.payload.rows && action.payload.count >= 0) {
|
||||
state.galleries = action.payload.rows;
|
||||
state.periods = action.payload.rows;
|
||||
state.count = action.payload.count;
|
||||
} else {
|
||||
state.galleries = action.payload;
|
||||
state.periods = action.payload;
|
||||
}
|
||||
state.loading = false;
|
||||
});
|
||||
@ -165,7 +165,7 @@ export const galleriesSlice = createSlice({
|
||||
|
||||
builder.addCase(deleteItemsByIds.fulfilled, (state) => {
|
||||
state.loading = false;
|
||||
fulfilledNotify(state, 'Galleries has been deleted');
|
||||
fulfilledNotify(state, 'Periods has been deleted');
|
||||
});
|
||||
|
||||
builder.addCase(deleteItemsByIds.rejected, (state, action) => {
|
||||
@ -180,7 +180,7 @@ export const galleriesSlice = createSlice({
|
||||
|
||||
builder.addCase(deleteItem.fulfilled, (state) => {
|
||||
state.loading = false;
|
||||
fulfilledNotify(state, `${'Galleries'.slice(0, -1)} has been deleted`);
|
||||
fulfilledNotify(state, `${'Periods'.slice(0, -1)} has been deleted`);
|
||||
});
|
||||
|
||||
builder.addCase(deleteItem.rejected, (state, action) => {
|
||||
@ -199,7 +199,7 @@ export const galleriesSlice = createSlice({
|
||||
|
||||
builder.addCase(create.fulfilled, (state) => {
|
||||
state.loading = false;
|
||||
fulfilledNotify(state, `${'Galleries'.slice(0, -1)} has been created`);
|
||||
fulfilledNotify(state, `${'Periods'.slice(0, -1)} has been created`);
|
||||
});
|
||||
|
||||
builder.addCase(update.pending, (state) => {
|
||||
@ -208,7 +208,7 @@ export const galleriesSlice = createSlice({
|
||||
});
|
||||
builder.addCase(update.fulfilled, (state) => {
|
||||
state.loading = false;
|
||||
fulfilledNotify(state, `${'Galleries'.slice(0, -1)} has been updated`);
|
||||
fulfilledNotify(state, `${'Periods'.slice(0, -1)} has been updated`);
|
||||
});
|
||||
builder.addCase(update.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
@ -221,7 +221,7 @@ export const galleriesSlice = createSlice({
|
||||
});
|
||||
builder.addCase(uploadCsv.fulfilled, (state) => {
|
||||
state.loading = false;
|
||||
fulfilledNotify(state, 'Galleries has been uploaded');
|
||||
fulfilledNotify(state, 'Periods has been uploaded');
|
||||
});
|
||||
builder.addCase(uploadCsv.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
@ -231,6 +231,6 @@ export const galleriesSlice = createSlice({
|
||||
});
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
export const { setRefetch } = galleriesSlice.actions;
|
||||
export const { setRefetch } = periodsSlice.actions;
|
||||
|
||||
export default galleriesSlice.reducer;
|
||||
export default periodsSlice.reducer;
|
||||
@ -6,7 +6,7 @@ import openAiSlice from './openAiSlice';
|
||||
|
||||
import usersSlice from './users/usersSlice';
|
||||
import archival_itemsSlice from './archival_items/archival_itemsSlice';
|
||||
import galleriesSlice from './galleries/galleriesSlice';
|
||||
import periodsSlice from './periods/periodsSlice';
|
||||
import tagsSlice from './tags/tagsSlice';
|
||||
import rolesSlice from './roles/rolesSlice';
|
||||
import permissionsSlice from './permissions/permissionsSlice';
|
||||
@ -20,7 +20,7 @@ export const store = configureStore({
|
||||
|
||||
users: usersSlice,
|
||||
archival_items: archival_itemsSlice,
|
||||
galleries: galleriesSlice,
|
||||
periods: periodsSlice,
|
||||
tags: tagsSlice,
|
||||
roles: rolesSlice,
|
||||
permissions: permissionsSlice,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user