Autosave: 20260216-190809
This commit is contained in:
parent
13a0a09a22
commit
1f9e116dc4
141
backend/src/db/api/breeders.js
Normal file
141
backend/src/db/api/breeders.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class BreedersDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.breeders.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.breeders.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.breeders.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.breeders.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.breeders.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.breeders.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('breeders', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.breeders.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('breeders', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.breeders.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
142
backend/src/db/api/containers.js
Normal file
142
backend/src/db/api/containers.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class ContainersDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.containers.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.containers.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.containers.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.containers.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.containers.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.containers.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('containers', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.containers.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('containers', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.containers.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
142
backend/src/db/api/crop_types.js
Normal file
142
backend/src/db/api/crop_types.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class Crop_typesDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.crop_types.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.crop_types.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.crop_types.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.crop_types.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.crop_types.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.crop_types.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('crop_types', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.crop_types.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('crop_types', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.crop_types.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
141
backend/src/db/api/groups.js
Normal file
141
backend/src/db/api/groups.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class groupsDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.groups.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.groups.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.groups.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.groups.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.groups.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.groups.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('groups', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.groups.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('groups', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.groups.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
142
backend/src/db/api/growth_habits.js
Normal file
142
backend/src/db/api/growth_habits.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class Growth_habitsDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.growth_habits.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.growth_habits.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.growth_habits.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.growth_habits.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.growth_habits.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.growth_habits.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('growth_habits', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.growth_habits.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('growth_habits', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.growth_habits.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
141
backend/src/db/api/locations.js
Normal file
141
backend/src/db/api/locations.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class locationsDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.locations.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.locations.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.locations.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.locations.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.locations.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.locations.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('locations', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.locations.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('locations', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.locations.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
141
backend/src/db/api/programs.js
Normal file
141
backend/src/db/api/programs.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class programsDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.programs.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.programs.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.programs.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.programs.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.programs.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.programs.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('programs', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.programs.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('programs', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.programs.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
142
backend/src/db/api/propagation_types.js
Normal file
142
backend/src/db/api/propagation_types.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class Propagation_typesDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.propagation_types.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.propagation_types.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.propagation_types.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.propagation_types.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.propagation_types.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.propagation_types.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('propagation_types', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.propagation_types.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('propagation_types', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.propagation_types.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
142
backend/src/db/api/retailer_programs.js
Normal file
142
backend/src/db/api/retailer_programs.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class Retailer_programsDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.retailer_programs.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.retailer_programs.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.retailer_programs.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.retailer_programs.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.retailer_programs.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.retailer_programs.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('retailer_programs', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.retailer_programs.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('retailer_programs', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.retailer_programs.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
141
backend/src/db/api/retailers.js
Normal file
141
backend/src/db/api/retailers.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class retailersDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.retailers.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.retailers.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.retailers.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.retailers.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.retailers.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.retailers.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('retailers', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.retailers.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('retailers', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.retailers.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
141
backend/src/db/api/sites.js
Normal file
141
backend/src/db/api/sites.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
const db = require('../models');
|
||||||
|
const Utils = require('../utils');
|
||||||
|
const Sequelize = db.Sequelize;
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
module.exports = class sitesDBApi {
|
||||||
|
static async create(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.sites.create(
|
||||||
|
{
|
||||||
|
id: data.id || undefined,
|
||||||
|
name: data.name || null,
|
||||||
|
importHash: data.importHash || null,
|
||||||
|
tenantId: data.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: data.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
},
|
||||||
|
{ transaction },
|
||||||
|
);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bulkImport(data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const recordsData = data.map((item, index) => ({
|
||||||
|
id: item.id || undefined,
|
||||||
|
name: item.name || null,
|
||||||
|
importHash: item.importHash || null,
|
||||||
|
tenantId: item.tenant || currentUser.tenantId || null,
|
||||||
|
organizationsId: item.organizations || currentUser.organizationsId || null,
|
||||||
|
createdById: currentUser.id,
|
||||||
|
updatedById: currentUser.id,
|
||||||
|
createdAt: new Date(Date.now() + index * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return await db.sites.bulkCreate(recordsData, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(id, data, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const record = await db.sites.findByPk(id, {}, { transaction });
|
||||||
|
const updatePayload = {};
|
||||||
|
if (data.name !== undefined) updatePayload.name = data.name;
|
||||||
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
|
await record.update(updatePayload, { transaction });
|
||||||
|
|
||||||
|
if (data.tenant !== undefined) await record.setTenant(data.tenant, { transaction });
|
||||||
|
if (data.organizations !== undefined) await record.setOrganizations(data.organizations, { transaction });
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, options) {
|
||||||
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
|
const records = await db.sites.findAll({
|
||||||
|
where: { id: { [Op.in]: ids } },
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
const record = await db.sites.findByPk(id, options);
|
||||||
|
await record.destroy({ transaction });
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findBy(where, options) {
|
||||||
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
return await db.sites.findOne({ where }, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAll(filter, globalAccess, options) {
|
||||||
|
const limit = filter.limit || 0;
|
||||||
|
const currentPage = +filter.page || 0;
|
||||||
|
const offset = currentPage * limit;
|
||||||
|
let where = {};
|
||||||
|
|
||||||
|
const user = (options && options.currentUser) || null;
|
||||||
|
if (!globalAccess && user?.organizationsId) {
|
||||||
|
where.organizationsId = user.organizationsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if (filter.id) where.id = Utils.uuid(filter.id);
|
||||||
|
if (filter.name) where[Op.and] = Utils.ilike('sites', 'name', filter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, count } = await db.sites.findAndCountAll({
|
||||||
|
where,
|
||||||
|
include: [
|
||||||
|
{ model: db.tenants, as: 'tenant' },
|
||||||
|
{ model: db.organizations, as: 'organizations' }
|
||||||
|
],
|
||||||
|
distinct: true,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']],
|
||||||
|
transaction: options?.transaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rows, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId) {
|
||||||
|
let where = {};
|
||||||
|
if (!globalAccess && organizationId) where.organizationsId = organizationId;
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
where[Op.or] = [
|
||||||
|
{ id: Utils.uuid(query) },
|
||||||
|
Utils.ilike('sites', 'name', query),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = await db.sites.findAll({
|
||||||
|
attributes: ['id', 'name'],
|
||||||
|
where,
|
||||||
|
limit: limit ? Number(limit) : undefined,
|
||||||
|
offset: offset ? Number(offset) : undefined,
|
||||||
|
order: [['name', 'ASC']],
|
||||||
|
});
|
||||||
|
|
||||||
|
return records.map((record) => ({ id: record.id, label: record.name }));
|
||||||
|
}
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
production: {
|
production: {
|
||||||
@ -12,11 +12,12 @@ module.exports = {
|
|||||||
seederStorage: 'sequelize',
|
seederStorage: 'sequelize',
|
||||||
},
|
},
|
||||||
development: {
|
development: {
|
||||||
username: 'postgres',
|
|
||||||
dialect: 'postgres',
|
dialect: 'postgres',
|
||||||
password: '',
|
username: process.env.DB_USER,
|
||||||
database: 'db_trial_tracker',
|
password: process.env.DB_PASS,
|
||||||
host: process.env.DB_HOST || 'localhost',
|
database: process.env.DB_NAME,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT,
|
||||||
logging: console.log,
|
logging: console.log,
|
||||||
seederStorage: 'sequelize',
|
seederStorage: 'sequelize',
|
||||||
},
|
},
|
||||||
|
|||||||
93
backend/src/db/migrations/1771207082257.js
Normal file
93
backend/src/db/migrations/1771207082257.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* @param {QueryInterface} queryInterface
|
||||||
|
* @param {Sequelize} Sequelize
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async up(queryInterface, Sequelize) {
|
||||||
|
const transaction = await queryInterface.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const tables = [
|
||||||
|
'propagation_types',
|
||||||
|
'growth_habits',
|
||||||
|
'crop_types',
|
||||||
|
'containers',
|
||||||
|
'retailer_programs'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const tableName of tables) {
|
||||||
|
await queryInterface.createTable(tableName, {
|
||||||
|
id: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
defaultValue: Sequelize.DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Sequelize.DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
tenantId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'tenants',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
organizationsId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'organizations',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createdById: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
updatedById: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createdAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
updatedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
deletedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
importHash: {
|
||||||
|
type: Sequelize.DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
}, { transaction });
|
||||||
|
}
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async down(queryInterface, Sequelize) {
|
||||||
|
const transaction = await queryInterface.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const tables = [
|
||||||
|
'propagation_types',
|
||||||
|
'growth_habits',
|
||||||
|
'crop_types',
|
||||||
|
'containers',
|
||||||
|
'retailer_programs'
|
||||||
|
];
|
||||||
|
for (const tableName of tables) {
|
||||||
|
await queryInterface.dropTable(tableName, { transaction });
|
||||||
|
}
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
138
backend/src/db/migrations/20260216000001-trials-update.js
Normal file
138
backend/src/db/migrations/20260216000001-trials-update.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
module.exports = {
|
||||||
|
async up(queryInterface, Sequelize) {
|
||||||
|
const transaction = await queryInterface.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
// Create breeders table
|
||||||
|
await queryInterface.createTable('breeders', {
|
||||||
|
id: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
defaultValue: Sequelize.DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Sequelize.DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
tenantId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'tenants',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
organizationsId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'organizations',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createdById: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
updatedById: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createdAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
updatedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
deletedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
importHash: {
|
||||||
|
type: Sequelize.DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
}, { transaction });
|
||||||
|
|
||||||
|
// Add columns to trials
|
||||||
|
await queryInterface.addColumn('trials', 'trial_code', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||||
|
await queryInterface.addColumn('trials', 'genus', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||||
|
await queryInterface.addColumn('trials', 'species', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||||
|
await queryInterface.addColumn('trials', 'colour', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||||
|
|
||||||
|
await queryInterface.addColumn('trials', 'breederId', {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'breeders',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
}, { transaction });
|
||||||
|
|
||||||
|
await queryInterface.addColumn('trials', 'propagationTypeId', {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'propagation_types',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
}, { transaction });
|
||||||
|
|
||||||
|
await queryInterface.addColumn('trials', 'cropTypeId', {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'crop_types',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
}, { transaction });
|
||||||
|
|
||||||
|
// Create join table for growth_habits (multi-select)
|
||||||
|
await queryInterface.createTable('trials_growth_habits', {
|
||||||
|
id: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
defaultValue: Sequelize.DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
trialId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'trials',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
allowNull: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
},
|
||||||
|
growthHabitId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'growth_habits',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
allowNull: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
},
|
||||||
|
createdAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
updatedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
}, { transaction });
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(queryInterface, Sequelize) {
|
||||||
|
const transaction = await queryInterface.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await queryInterface.dropTable('trials_growth_habits', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'cropTypeId', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'propagationTypeId', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'breederId', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'colour', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'species', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'genus', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'trial_code', { transaction });
|
||||||
|
await queryInterface.dropTable('breeders', { transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,200 @@
|
|||||||
|
module.exports = {
|
||||||
|
async up(queryInterface, Sequelize) {
|
||||||
|
const transaction = await queryInterface.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const tablesToCreate = ['retailers', 'programs', 'groups', 'sites', 'locations'];
|
||||||
|
|
||||||
|
for (const tableName of tablesToCreate) {
|
||||||
|
// Check if table exists
|
||||||
|
const tableExists = await queryInterface.describeTable(tableName).then(() => true).catch(() => false);
|
||||||
|
if (!tableExists) {
|
||||||
|
await queryInterface.createTable(tableName, {
|
||||||
|
id: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
defaultValue: Sequelize.DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Sequelize.DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
tenantId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'tenants',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
organizationsId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'organizations',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createdById: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
updatedById: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createdAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
updatedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
deletedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
importHash: {
|
||||||
|
type: Sequelize.DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
}, { transaction });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add columns to trials
|
||||||
|
const trialsColumns = await queryInterface.describeTable('trials');
|
||||||
|
|
||||||
|
const addColumnIfMissing = async (table, column, definition) => {
|
||||||
|
if (!trialsColumns[column]) {
|
||||||
|
await queryInterface.addColumn(table, column, definition, { transaction });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await addColumnIfMissing('trials', 'containerSize', { type: Sequelize.DataTypes.TEXT });
|
||||||
|
await addColumnIfMissing('trials', 'finishedHeight', { type: Sequelize.DataTypes.TEXT });
|
||||||
|
await addColumnIfMissing('trials', 'finishedSpread', { type: Sequelize.DataTypes.TEXT });
|
||||||
|
await addColumnIfMissing('trials', 'description', { type: Sequelize.DataTypes.TEXT });
|
||||||
|
await addColumnIfMissing('trials', 'internalReferenceNumber', { type: Sequelize.DataTypes.TEXT });
|
||||||
|
await addColumnIfMissing('trials', 'programYear', { type: Sequelize.DataTypes.INTEGER });
|
||||||
|
await addColumnIfMissing('trials', 'distributor', { type: Sequelize.DataTypes.TEXT });
|
||||||
|
await addColumnIfMissing('trials', 'quantity', { type: Sequelize.DataTypes.DECIMAL });
|
||||||
|
await addColumnIfMissing('trials', 'receiveWeek', { type: Sequelize.DataTypes.INTEGER });
|
||||||
|
await addColumnIfMissing('trials', 'finishWeek', { type: Sequelize.DataTypes.INTEGER });
|
||||||
|
await addColumnIfMissing('trials', 'tableId', { type: Sequelize.DataTypes.TEXT });
|
||||||
|
|
||||||
|
await addColumnIfMissing('trials', 'containerId', {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'containers', key: 'id' },
|
||||||
|
});
|
||||||
|
|
||||||
|
await addColumnIfMissing('trials', 'cultureSheetId', {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'files', key: 'id' },
|
||||||
|
});
|
||||||
|
|
||||||
|
await addColumnIfMissing('trials', 'programId', {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'programs', key: 'id' },
|
||||||
|
});
|
||||||
|
|
||||||
|
await addColumnIfMissing('trials', 'siteId', {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'sites', key: 'id' },
|
||||||
|
});
|
||||||
|
|
||||||
|
await addColumnIfMissing('trials', 'locationId', {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'locations', key: 'id' },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create junction tables for multi-select
|
||||||
|
const retailersJunctionExists = await queryInterface.describeTable('trials_retailers').then(() => true).catch(() => false);
|
||||||
|
if (!retailersJunctionExists) {
|
||||||
|
await queryInterface.createTable('trials_retailers', {
|
||||||
|
id: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
defaultValue: Sequelize.DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
trialId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'trials', key: 'id' },
|
||||||
|
allowNull: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
},
|
||||||
|
retailerId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'retailers', key: 'id' },
|
||||||
|
allowNull: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
},
|
||||||
|
createdAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
updatedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
}, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupsJunctionExists = await queryInterface.describeTable('trials_groups').then(() => true).catch(() => false);
|
||||||
|
if (!groupsJunctionExists) {
|
||||||
|
await queryInterface.createTable('trials_groups', {
|
||||||
|
id: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
defaultValue: Sequelize.DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
trialId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'trials', key: 'id' },
|
||||||
|
allowNull: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
},
|
||||||
|
groupId: {
|
||||||
|
type: Sequelize.DataTypes.UUID,
|
||||||
|
references: { model: 'groups', key: 'id' },
|
||||||
|
allowNull: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
},
|
||||||
|
createdAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
updatedAt: { type: Sequelize.DataTypes.DATE },
|
||||||
|
}, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(queryInterface, Sequelize) {
|
||||||
|
const transaction = await queryInterface.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await queryInterface.dropTable('trials_groups', { transaction });
|
||||||
|
await queryInterface.dropTable('trials_retailers', { transaction });
|
||||||
|
|
||||||
|
await queryInterface.removeColumn('trials', 'locationId', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'siteId', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'programId', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'cultureSheetId', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'containerId', { transaction });
|
||||||
|
|
||||||
|
await queryInterface.removeColumn('trials', 'tableId', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'finishWeek', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'receiveWeek', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'quantity', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'distributor', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'programYear', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'internalReferenceNumber', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'description', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'finishedSpread', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'finishedHeight', { transaction });
|
||||||
|
await queryInterface.removeColumn('trials', 'containerSize', { transaction });
|
||||||
|
|
||||||
|
const tablesToDrop = ['retailers', 'programs', 'groups', 'sites', 'locations'];
|
||||||
|
for (const tableName of tablesToDrop) {
|
||||||
|
await queryInterface.dropTable(tableName, { transaction });
|
||||||
|
}
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: async (queryInterface, Sequelize) => {
|
||||||
|
await queryInterface.addColumn('trials', 'attributes', {
|
||||||
|
type: Sequelize.JSONB,
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: {},
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('trials', 'trial_category', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 'PLANT',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: async (queryInterface) => {
|
||||||
|
await queryInterface.removeColumn('trials', 'attributes');
|
||||||
|
await queryInterface.removeColumn('trials', 'trial_category');
|
||||||
|
},
|
||||||
|
};
|
||||||
54
backend/src/db/models/breeders.js
Normal file
54
backend/src/db/models/breeders.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const breeders = sequelize.define(
|
||||||
|
'breeders',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
breeders.associate = (db) => {
|
||||||
|
db.breeders.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.breeders.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.breeders.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.breeders.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return breeders;
|
||||||
|
};
|
||||||
55
backend/src/db/models/containers.js
Normal file
55
backend/src/db/models/containers.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const containers = sequelize.define(
|
||||||
|
'containers',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
containers.associate = (db) => {
|
||||||
|
db.containers.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.containers.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.containers.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.containers.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return containers;
|
||||||
|
};
|
||||||
55
backend/src/db/models/crop_types.js
Normal file
55
backend/src/db/models/crop_types.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const crop_types = sequelize.define(
|
||||||
|
'crop_types',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
crop_types.associate = (db) => {
|
||||||
|
db.crop_types.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.crop_types.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.crop_types.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.crop_types.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return crop_types;
|
||||||
|
};
|
||||||
54
backend/src/db/models/groups.js
Normal file
54
backend/src/db/models/groups.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const groups = sequelize.define(
|
||||||
|
'groups',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
groups.associate = (db) => {
|
||||||
|
db.groups.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.groups.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.groups.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.groups.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
};
|
||||||
55
backend/src/db/models/growth_habits.js
Normal file
55
backend/src/db/models/growth_habits.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const growth_habits = sequelize.define(
|
||||||
|
'growth_habits',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
growth_habits.associate = (db) => {
|
||||||
|
db.growth_habits.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.growth_habits.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.growth_habits.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.growth_habits.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return growth_habits;
|
||||||
|
};
|
||||||
54
backend/src/db/models/locations.js
Normal file
54
backend/src/db/models/locations.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const locations = sequelize.define(
|
||||||
|
'locations',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
locations.associate = (db) => {
|
||||||
|
db.locations.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.locations.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.locations.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.locations.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return locations;
|
||||||
|
};
|
||||||
54
backend/src/db/models/programs.js
Normal file
54
backend/src/db/models/programs.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const programs = sequelize.define(
|
||||||
|
'programs',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
programs.associate = (db) => {
|
||||||
|
db.programs.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.programs.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.programs.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.programs.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return programs;
|
||||||
|
};
|
||||||
55
backend/src/db/models/propagation_types.js
Normal file
55
backend/src/db/models/propagation_types.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const propagation_types = sequelize.define(
|
||||||
|
'propagation_types',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
propagation_types.associate = (db) => {
|
||||||
|
db.propagation_types.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.propagation_types.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.propagation_types.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.propagation_types.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return propagation_types;
|
||||||
|
};
|
||||||
55
backend/src/db/models/retailer_programs.js
Normal file
55
backend/src/db/models/retailer_programs.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const retailer_programs = sequelize.define(
|
||||||
|
'retailer_programs',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
retailer_programs.associate = (db) => {
|
||||||
|
db.retailer_programs.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.retailer_programs.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.retailer_programs.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.retailer_programs.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return retailer_programs;
|
||||||
|
};
|
||||||
54
backend/src/db/models/retailers.js
Normal file
54
backend/src/db/models/retailers.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const retailers = sequelize.define(
|
||||||
|
'retailers',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
retailers.associate = (db) => {
|
||||||
|
db.retailers.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.retailers.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.retailers.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.retailers.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return retailers;
|
||||||
|
};
|
||||||
54
backend/src/db/models/sites.js
Normal file
54
backend/src/db/models/sites.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
const sites = sequelize.define(
|
||||||
|
'sites',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
importHash: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
freezeTableName: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
sites.associate = (db) => {
|
||||||
|
db.sites.belongsTo(db.tenants, {
|
||||||
|
as: 'tenant',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'tenantId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.sites.belongsTo(db.organizations, {
|
||||||
|
as: 'organizations',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'organizationsId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.sites.belongsTo(db.users, {
|
||||||
|
as: 'createdBy',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.sites.belongsTo(db.users, {
|
||||||
|
as: 'updatedBy',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return sites;
|
||||||
|
};
|
||||||
@ -1,8 +1,4 @@
|
|||||||
const config = require('../../config');
|
const config = require('../../config');
|
||||||
const providers = config.providers;
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const bcrypt = require('bcrypt');
|
|
||||||
const moment = require('moment');
|
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const trials = sequelize.define(
|
const trials = sequelize.define(
|
||||||
@ -13,165 +9,122 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
defaultValue: DataTypes.UUIDV4,
|
defaultValue: DataTypes.UUIDV4,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
name: {
|
||||||
name: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
trial_code: {
|
||||||
variety_name: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
variety_name: {
|
||||||
breeder: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
genus: {
|
||||||
batch_code: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
species: {
|
||||||
trial_type: {
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
colour: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
batch_code: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
trial_type: {
|
||||||
type: DataTypes.ENUM,
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
values: [
|
values: [
|
||||||
|
"new_variety",
|
||||||
"new_variety",
|
"performance",
|
||||||
|
"disease_resistance",
|
||||||
|
"yield",
|
||||||
"performance",
|
"quality",
|
||||||
|
"other"
|
||||||
|
|
||||||
"disease_resistance",
|
|
||||||
|
|
||||||
|
|
||||||
"yield",
|
|
||||||
|
|
||||||
|
|
||||||
"quality",
|
|
||||||
|
|
||||||
|
|
||||||
"other"
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
},
|
},
|
||||||
|
status: {
|
||||||
status: {
|
|
||||||
type: DataTypes.ENUM,
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
values: [
|
values: [
|
||||||
|
"setup",
|
||||||
"setup",
|
"in_progress",
|
||||||
|
"paused",
|
||||||
|
"completed",
|
||||||
"in_progress",
|
"archived"
|
||||||
|
|
||||||
|
|
||||||
"paused",
|
|
||||||
|
|
||||||
|
|
||||||
"completed",
|
|
||||||
|
|
||||||
|
|
||||||
"archived"
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
},
|
},
|
||||||
|
planted_at: {
|
||||||
planted_at: {
|
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
harvested_at: {
|
||||||
harvested_at: {
|
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
greenhouse: {
|
||||||
greenhouse: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
zone: {
|
||||||
zone: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
bench: {
|
||||||
bench: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
plants_count: {
|
||||||
plants_count: {
|
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
target_temperature_c: {
|
||||||
target_temperature_c: {
|
|
||||||
type: DataTypes.DECIMAL,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
target_humidity_percent: {
|
||||||
target_humidity_percent: {
|
|
||||||
type: DataTypes.DECIMAL,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
target_ec: {
|
||||||
target_ec: {
|
|
||||||
type: DataTypes.DECIMAL,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
target_ph: {
|
||||||
target_ph: {
|
|
||||||
type: DataTypes.DECIMAL,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
notes: {
|
||||||
notes: {
|
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
containerSize: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
finishedHeight: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
finishedSpread: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
internalReferenceNumber: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
programYear: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
},
|
||||||
|
distributor: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
quantity: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
},
|
||||||
|
receiveWeek: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
},
|
||||||
|
finishWeek: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
},
|
||||||
|
tableId: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
},
|
||||||
|
attributes: {
|
||||||
|
type: DataTypes.JSONB,
|
||||||
|
defaultValue: {},
|
||||||
|
},
|
||||||
|
trial_category: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: 'PLANT',
|
||||||
|
},
|
||||||
importHash: {
|
importHash: {
|
||||||
type: DataTypes.STRING(255),
|
type: DataTypes.STRING(255),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
@ -186,19 +139,66 @@ notes: {
|
|||||||
);
|
);
|
||||||
|
|
||||||
trials.associate = (db) => {
|
trials.associate = (db) => {
|
||||||
|
db.trials.belongsTo(db.breeders, {
|
||||||
|
as: 'breeder',
|
||||||
|
foreignKey: 'breederId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsTo(db.propagation_types, {
|
||||||
|
as: 'propagationType',
|
||||||
|
foreignKey: 'propagationTypeId',
|
||||||
|
});
|
||||||
|
|
||||||
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
|
db.trials.belongsTo(db.crop_types, {
|
||||||
|
as: 'cropType',
|
||||||
|
foreignKey: 'cropTypeId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsToMany(db.growth_habits, {
|
||||||
|
as: 'growthHabits',
|
||||||
|
through: 'trials_growth_habits',
|
||||||
|
foreignKey: 'trialId',
|
||||||
|
otherKey: 'growthHabitId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsTo(db.containers, {
|
||||||
|
as: 'container',
|
||||||
|
foreignKey: 'containerId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsTo(db.file, {
|
||||||
|
as: 'cultureSheet',
|
||||||
|
foreignKey: 'cultureSheetId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsTo(db.programs, {
|
||||||
|
as: 'program',
|
||||||
|
foreignKey: 'programId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsTo(db.sites, {
|
||||||
|
as: 'site',
|
||||||
|
foreignKey: 'siteId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsTo(db.locations, {
|
||||||
|
as: 'location',
|
||||||
|
foreignKey: 'locationId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsToMany(db.retailers, {
|
||||||
|
as: 'retailers',
|
||||||
|
through: 'trials_retailers',
|
||||||
|
foreignKey: 'trialId',
|
||||||
|
otherKey: 'retailerId',
|
||||||
|
});
|
||||||
|
|
||||||
|
db.trials.belongsToMany(db.groups, {
|
||||||
|
as: 'groups',
|
||||||
|
through: 'trials_groups',
|
||||||
|
foreignKey: 'trialId',
|
||||||
|
otherKey: 'groupId',
|
||||||
|
});
|
||||||
|
|
||||||
db.trials.hasMany(db.tracking_activities, {
|
db.trials.hasMany(db.tracking_activities, {
|
||||||
as: 'tracking_activities_trial',
|
as: 'tracking_activities_trial',
|
||||||
@ -208,16 +208,6 @@ notes: {
|
|||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.trials.belongsTo(db.tenants, {
|
db.trials.belongsTo(db.tenants, {
|
||||||
as: 'tenant',
|
as: 'tenant',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
@ -242,9 +232,6 @@ notes: {
|
|||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.trials.belongsTo(db.users, {
|
db.trials.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
@ -254,9 +241,5 @@ notes: {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return trials;
|
return trials;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
67
backend/src/db/seeders/20260216000000-dataset-permissions.js
Normal file
67
backend/src/db/seeders/20260216000000-dataset-permissions.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
const { v4: uuid } = require("uuid");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async up(queryInterface) {
|
||||||
|
const createdAt = new Date();
|
||||||
|
const updatedAt = new Date();
|
||||||
|
|
||||||
|
const entities = [
|
||||||
|
"propagation_types",
|
||||||
|
"growth_habits",
|
||||||
|
"crop_types",
|
||||||
|
"containers",
|
||||||
|
"retailer_programs"
|
||||||
|
];
|
||||||
|
|
||||||
|
const permissions = [];
|
||||||
|
for (const entity of entities) {
|
||||||
|
const upperName = entity.toUpperCase();
|
||||||
|
permissions.push(
|
||||||
|
{ id: uuid(), name: `CREATE_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `READ_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `UPDATE_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `DELETE_${upperName}`, createdAt, updatedAt }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await queryInterface.bulkInsert("permissions", permissions);
|
||||||
|
|
||||||
|
const [roles] = await queryInterface.sequelize.query(
|
||||||
|
`SELECT id, name FROM roles WHERE name IN ('Super Administrator', 'Administrator', 'Platform Owner', 'Tenant Owner', 'Trial Manager');`
|
||||||
|
);
|
||||||
|
|
||||||
|
const [dbPermissions] = await queryInterface.sequelize.query(
|
||||||
|
`SELECT id, name FROM permissions WHERE name LIKE 'CREATE_%' OR name LIKE 'READ_%' OR name LIKE 'UPDATE_%' OR name LIKE 'DELETE_%';`
|
||||||
|
);
|
||||||
|
|
||||||
|
const rolesPermissions = [];
|
||||||
|
for (const role of roles) {
|
||||||
|
for (const permission of dbPermissions) {
|
||||||
|
// Only add if it's one of our new permissions
|
||||||
|
const isNewPermission = entities.some(e => permission.name.includes(e.toUpperCase()));
|
||||||
|
if (isNewPermission) {
|
||||||
|
rolesPermissions.push({
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
roles_permissionsId: role.id,
|
||||||
|
permissionId: permission.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a try-catch or IGNORE to avoid duplicates if seeder runs multiple times
|
||||||
|
for (const rp of rolesPermissions) {
|
||||||
|
try {
|
||||||
|
await queryInterface.bulkInsert("rolesPermissionsPermissions", [rp]);
|
||||||
|
} catch (e) {
|
||||||
|
// Likely duplicate key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(queryInterface) {
|
||||||
|
// Optional: cleanup
|
||||||
|
}
|
||||||
|
};
|
||||||
51
backend/src/db/seeders/20260216000001-breeder-permissions.js
Normal file
51
backend/src/db/seeders/20260216000001-breeder-permissions.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
const { v4: uuid } = require("uuid");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async up(queryInterface) {
|
||||||
|
const createdAt = new Date();
|
||||||
|
const updatedAt = new Date();
|
||||||
|
|
||||||
|
const entity = "breeders";
|
||||||
|
const upperName = entity.toUpperCase();
|
||||||
|
|
||||||
|
const permissions = [
|
||||||
|
{ id: uuid(), name: `CREATE_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `READ_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `UPDATE_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `DELETE_${upperName}`, createdAt, updatedAt }
|
||||||
|
];
|
||||||
|
|
||||||
|
await queryInterface.bulkInsert("permissions", permissions);
|
||||||
|
|
||||||
|
const [roles] = await queryInterface.sequelize.query(
|
||||||
|
`SELECT id, name FROM roles WHERE name IN ('Super Administrator', 'Administrator', 'Platform Owner', 'Tenant Owner', 'Trial Manager');`
|
||||||
|
);
|
||||||
|
|
||||||
|
const [dbPermissions] = await queryInterface.sequelize.query(
|
||||||
|
`SELECT id, name FROM permissions WHERE name LIKE '%BREEDERS%';`
|
||||||
|
);
|
||||||
|
|
||||||
|
const rolesPermissions = [];
|
||||||
|
for (const role of roles) {
|
||||||
|
for (const permission of dbPermissions) {
|
||||||
|
rolesPermissions.push({
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
roles_permissionsId: role.id,
|
||||||
|
permissionId: permission.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const rp of rolesPermissions) {
|
||||||
|
try {
|
||||||
|
await queryInterface.bulkInsert("rolesPermissionsPermissions", [rp]);
|
||||||
|
} catch (e) {
|
||||||
|
// Likely duplicate key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(queryInterface) {
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
const { v4: uuid } = require("uuid");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async up(queryInterface) {
|
||||||
|
const createdAt = new Date();
|
||||||
|
const updatedAt = new Date();
|
||||||
|
|
||||||
|
const entities = ["retailers", "programs", "groups", "sites", "locations"];
|
||||||
|
|
||||||
|
for (const entity of entities) {
|
||||||
|
const upperName = entity.toUpperCase();
|
||||||
|
|
||||||
|
const permissions = [
|
||||||
|
{ id: uuid(), name: `CREATE_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `READ_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `UPDATE_${upperName}`, createdAt, updatedAt },
|
||||||
|
{ id: uuid(), name: `DELETE_${upperName}`, createdAt, updatedAt }
|
||||||
|
];
|
||||||
|
|
||||||
|
await queryInterface.bulkInsert("permissions", permissions);
|
||||||
|
|
||||||
|
const [roles] = await queryInterface.sequelize.query(
|
||||||
|
`SELECT id, name FROM roles WHERE name IN ('Super Administrator', 'Administrator', 'Platform Owner', 'Tenant Owner', 'Trial Manager');`
|
||||||
|
);
|
||||||
|
|
||||||
|
const [dbPermissions] = await queryInterface.sequelize.query(
|
||||||
|
`SELECT id, name FROM permissions WHERE name LIKE '%${upperName}%';`
|
||||||
|
);
|
||||||
|
|
||||||
|
const rolesPermissions = [];
|
||||||
|
for (const role of roles) {
|
||||||
|
for (const permission of dbPermissions) {
|
||||||
|
rolesPermissions.push({
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
roles_permissionsId: role.id,
|
||||||
|
permissionId: permission.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const rp of rolesPermissions) {
|
||||||
|
try {
|
||||||
|
await queryInterface.bulkInsert("rolesPermissionsPermissions", [rp]);
|
||||||
|
} catch (e) {
|
||||||
|
// Likely duplicate key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(queryInterface) {
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const app = express();
|
const app = express();
|
||||||
@ -16,41 +15,36 @@ const fileRoutes = require('./routes/file');
|
|||||||
const searchRoutes = require('./routes/search');
|
const searchRoutes = require('./routes/search');
|
||||||
const sqlRoutes = require('./routes/sql');
|
const sqlRoutes = require('./routes/sql');
|
||||||
const pexelsRoutes = require('./routes/pexels');
|
const pexelsRoutes = require('./routes/pexels');
|
||||||
|
|
||||||
const organizationForAuthRoutes = require('./routes/organizationLogin');
|
const organizationForAuthRoutes = require('./routes/organizationLogin');
|
||||||
|
|
||||||
const openaiRoutes = require('./routes/openai');
|
const openaiRoutes = require('./routes/openai');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const usersRoutes = require('./routes/users');
|
const usersRoutes = require('./routes/users');
|
||||||
|
|
||||||
const rolesRoutes = require('./routes/roles');
|
const rolesRoutes = require('./routes/roles');
|
||||||
|
|
||||||
const permissionsRoutes = require('./routes/permissions');
|
const permissionsRoutes = require('./routes/permissions');
|
||||||
|
|
||||||
const organizationsRoutes = require('./routes/organizations');
|
const organizationsRoutes = require('./routes/organizations');
|
||||||
|
|
||||||
const tenantsRoutes = require('./routes/tenants');
|
const tenantsRoutes = require('./routes/tenants');
|
||||||
|
|
||||||
const impersonation_sessionsRoutes = require('./routes/impersonation_sessions');
|
const impersonation_sessionsRoutes = require('./routes/impersonation_sessions');
|
||||||
|
|
||||||
const projectsRoutes = require('./routes/projects');
|
const projectsRoutes = require('./routes/projects');
|
||||||
|
|
||||||
const trialsRoutes = require('./routes/trials');
|
const trialsRoutes = require('./routes/trials');
|
||||||
|
|
||||||
const tracking_activity_typesRoutes = require('./routes/tracking_activity_types');
|
const tracking_activity_typesRoutes = require('./routes/tracking_activity_types');
|
||||||
|
|
||||||
const tracking_activitiesRoutes = require('./routes/tracking_activities');
|
const tracking_activitiesRoutes = require('./routes/tracking_activities');
|
||||||
|
|
||||||
const dashboardsRoutes = require('./routes/dashboards');
|
const dashboardsRoutes = require('./routes/dashboards');
|
||||||
|
|
||||||
const report_definitionsRoutes = require('./routes/report_definitions');
|
const report_definitionsRoutes = require('./routes/report_definitions');
|
||||||
|
|
||||||
const report_runsRoutes = require('./routes/report_runs');
|
const report_runsRoutes = require('./routes/report_runs');
|
||||||
|
|
||||||
const tenant_settingsRoutes = require('./routes/tenant_settings');
|
const tenant_settingsRoutes = require('./routes/tenant_settings');
|
||||||
|
|
||||||
|
const propagation_typesRoutes = require('./routes/propagation_types');
|
||||||
|
const growth_habitsRoutes = require('./routes/growth_habits');
|
||||||
|
const crop_typesRoutes = require('./routes/crop_types');
|
||||||
|
const containersRoutes = require('./routes/containers');
|
||||||
|
const retailer_programsRoutes = require('./routes/retailer_programs');
|
||||||
|
const breedersRoutes = require('./routes/breeders');
|
||||||
|
const retailersRoutes = require('./routes/retailers');
|
||||||
|
const programsRoutes = require('./routes/programs');
|
||||||
|
const groupsRoutes = require('./routes/groups');
|
||||||
|
const sitesRoutes = require('./routes/sites');
|
||||||
|
const locationsRoutes = require('./routes/locations');
|
||||||
|
|
||||||
|
|
||||||
const getBaseUrl = (url) => {
|
const getBaseUrl = (url) => {
|
||||||
if (!url) return '';
|
if (!url) return '';
|
||||||
@ -110,33 +104,32 @@ app.enable('trust proxy');
|
|||||||
|
|
||||||
|
|
||||||
app.use('/api/users', passport.authenticate('jwt', {session: false}), usersRoutes);
|
app.use('/api/users', passport.authenticate('jwt', {session: false}), usersRoutes);
|
||||||
|
|
||||||
app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoutes);
|
app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoutes);
|
||||||
|
|
||||||
app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes);
|
app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes);
|
||||||
|
|
||||||
app.use('/api/organizations', passport.authenticate('jwt', {session: false}), organizationsRoutes);
|
app.use('/api/organizations', passport.authenticate('jwt', {session: false}), organizationsRoutes);
|
||||||
|
|
||||||
app.use('/api/tenants', passport.authenticate('jwt', {session: false}), tenantsRoutes);
|
app.use('/api/tenants', passport.authenticate('jwt', {session: false}), tenantsRoutes);
|
||||||
|
|
||||||
app.use('/api/impersonation_sessions', passport.authenticate('jwt', {session: false}), impersonation_sessionsRoutes);
|
app.use('/api/impersonation_sessions', passport.authenticate('jwt', {session: false}), impersonation_sessionsRoutes);
|
||||||
|
|
||||||
app.use('/api/projects', passport.authenticate('jwt', {session: false}), projectsRoutes);
|
app.use('/api/projects', passport.authenticate('jwt', {session: false}), projectsRoutes);
|
||||||
|
|
||||||
app.use('/api/trials', passport.authenticate('jwt', {session: false}), trialsRoutes);
|
app.use('/api/trials', passport.authenticate('jwt', {session: false}), trialsRoutes);
|
||||||
|
|
||||||
app.use('/api/tracking_activity_types', passport.authenticate('jwt', {session: false}), tracking_activity_typesRoutes);
|
app.use('/api/tracking_activity_types', passport.authenticate('jwt', {session: false}), tracking_activity_typesRoutes);
|
||||||
|
|
||||||
app.use('/api/tracking_activities', passport.authenticate('jwt', {session: false}), tracking_activitiesRoutes);
|
app.use('/api/tracking_activities', passport.authenticate('jwt', {session: false}), tracking_activitiesRoutes);
|
||||||
|
|
||||||
app.use('/api/dashboards', passport.authenticate('jwt', {session: false}), dashboardsRoutes);
|
app.use('/api/dashboards', passport.authenticate('jwt', {session: false}), dashboardsRoutes);
|
||||||
|
|
||||||
app.use('/api/report_definitions', passport.authenticate('jwt', {session: false}), report_definitionsRoutes);
|
app.use('/api/report_definitions', passport.authenticate('jwt', {session: false}), report_definitionsRoutes);
|
||||||
|
|
||||||
app.use('/api/report_runs', passport.authenticate('jwt', {session: false}), report_runsRoutes);
|
app.use('/api/report_runs', passport.authenticate('jwt', {session: false}), report_runsRoutes);
|
||||||
|
|
||||||
app.use('/api/tenant_settings', passport.authenticate('jwt', {session: false}), tenant_settingsRoutes);
|
app.use('/api/tenant_settings', passport.authenticate('jwt', {session: false}), tenant_settingsRoutes);
|
||||||
|
|
||||||
|
app.use('/api/propagation_types', passport.authenticate('jwt', {session: false}), propagation_typesRoutes);
|
||||||
|
app.use('/api/growth_habits', passport.authenticate('jwt', {session: false}), growth_habitsRoutes);
|
||||||
|
app.use('/api/crop_types', passport.authenticate('jwt', {session: false}), crop_typesRoutes);
|
||||||
|
app.use('/api/containers', passport.authenticate('jwt', {session: false}), containersRoutes);
|
||||||
|
app.use('/api/retailer_programs', passport.authenticate('jwt', {session: false}), retailer_programsRoutes);
|
||||||
|
app.use('/api/breeders', passport.authenticate('jwt', {session: false}), breedersRoutes);
|
||||||
|
app.use('/api/retailers', passport.authenticate('jwt', {session: false}), retailersRoutes);
|
||||||
|
app.use('/api/programs', passport.authenticate('jwt', {session: false}), programsRoutes);
|
||||||
|
app.use('/api/groups', passport.authenticate('jwt', {session: false}), groupsRoutes);
|
||||||
|
app.use('/api/sites', passport.authenticate('jwt', {session: false}), sitesRoutes);
|
||||||
|
app.use('/api/locations', passport.authenticate('jwt', {session: false}), locationsRoutes);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
'/api/openai',
|
'/api/openai',
|
||||||
passport.authenticate('jwt', { session: false }),
|
passport.authenticate('jwt', { session: false }),
|
||||||
|
|||||||
53
backend/src/routes/breeders.js
Normal file
53
backend/src/routes/breeders.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const BreedersService = require('../services/breeders');
|
||||||
|
const BreedersDBApi = require('../db/api/breeders');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('breeders'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await BreedersService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await BreedersService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await BreedersService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await BreedersService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await BreedersDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await BreedersDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await BreedersDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
54
backend/src/routes/containers.js
Normal file
54
backend/src/routes/containers.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const ContainersService = require('../services/containers');
|
||||||
|
const ContainersDBApi = require('../db/api/containers');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('containers'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await ContainersService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await ContainersService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await ContainersService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await ContainersService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await ContainersDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await ContainersDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await ContainersDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
54
backend/src/routes/crop_types.js
Normal file
54
backend/src/routes/crop_types.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const Crop_typesService = require('../services/crop_types');
|
||||||
|
const Crop_typesDBApi = require('../db/api/crop_types');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('crop_types'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await Crop_typesService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await Crop_typesService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await Crop_typesService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Crop_typesService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Crop_typesDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await Crop_typesDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await Crop_typesDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
53
backend/src/routes/groups.js
Normal file
53
backend/src/routes/groups.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const groupsService = require('../services/groups');
|
||||||
|
const groupsDBApi = require('../db/api/groups');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('groups'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await groupsService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await groupsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await groupsService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await groupsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await groupsDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await groupsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await groupsDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
54
backend/src/routes/growth_habits.js
Normal file
54
backend/src/routes/growth_habits.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const Growth_habitsService = require('../services/growth_habits');
|
||||||
|
const Growth_habitsDBApi = require('../db/api/growth_habits');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('growth_habits'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await Growth_habitsService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await Growth_habitsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await Growth_habitsService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Growth_habitsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Growth_habitsDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await Growth_habitsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await Growth_habitsDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
53
backend/src/routes/locations.js
Normal file
53
backend/src/routes/locations.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const locationsService = require('../services/locations');
|
||||||
|
const locationsDBApi = require('../db/api/locations');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('locations'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await locationsService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await locationsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await locationsService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await locationsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await locationsDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await locationsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await locationsDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
53
backend/src/routes/programs.js
Normal file
53
backend/src/routes/programs.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const programsService = require('../services/programs');
|
||||||
|
const programsDBApi = require('../db/api/programs');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('programs'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await programsService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await programsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await programsService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await programsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await programsDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await programsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await programsDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
54
backend/src/routes/propagation_types.js
Normal file
54
backend/src/routes/propagation_types.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const Propagation_typesService = require('../services/propagation_types');
|
||||||
|
const Propagation_typesDBApi = require('../db/api/propagation_types');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('propagation_types'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await Propagation_typesService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await Propagation_typesService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await Propagation_typesService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Propagation_typesService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Propagation_typesDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await Propagation_typesDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await Propagation_typesDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
54
backend/src/routes/retailer_programs.js
Normal file
54
backend/src/routes/retailer_programs.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const Retailer_programsService = require('../services/retailer_programs');
|
||||||
|
const Retailer_programsDBApi = require('../db/api/retailer_programs');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('retailer_programs'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await Retailer_programsService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await Retailer_programsService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await Retailer_programsService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await Retailer_programsService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await Retailer_programsDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await Retailer_programsDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await Retailer_programsDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
53
backend/src/routes/retailers.js
Normal file
53
backend/src/routes/retailers.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const retailersService = require('../services/retailers');
|
||||||
|
const retailersDBApi = require('../db/api/retailers');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('retailers'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await retailersService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await retailersService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await retailersService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await retailersService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await retailersDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await retailersDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await retailersDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
53
backend/src/routes/sites.js
Normal file
53
backend/src/routes/sites.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const sitesService = require('../services/sites');
|
||||||
|
const sitesDBApi = require('../db/api/sites');
|
||||||
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
const router = express.Router();
|
||||||
|
const { checkCrudPermissions } = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
|
router.use(checkCrudPermissions('sites'));
|
||||||
|
|
||||||
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
|
await sitesService.create(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await sitesService.update(req.body.data, req.body.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
|
await sitesService.remove(req.params.id, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
|
await sitesService.deleteByIds(req.body.data, req.currentUser);
|
||||||
|
res.status(200).send(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const currentUser = req.currentUser;
|
||||||
|
const payload = await sitesDBApi.findAll(req.query, globalAccess, { currentUser });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/autocomplete', wrapAsync(async (req, res) => {
|
||||||
|
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||||
|
const organizationId = req.currentUser.organizationsId;
|
||||||
|
const payload = await sitesDBApi.findAllAutocomplete(
|
||||||
|
req.query.query, req.query.limit, req.query.offset, globalAccess, organizationId
|
||||||
|
);
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
|
const payload = await sitesDBApi.findBy({ id: req.params.id });
|
||||||
|
res.status(200).send(payload);
|
||||||
|
}));
|
||||||
|
|
||||||
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
52
backend/src/services/breeders.js
Normal file
52
backend/src/services/breeders.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const BreedersDBApi = require('../db/api/breeders');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class BreedersService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await BreedersDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await BreedersDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('breedersNotFound');
|
||||||
|
const updated = await BreedersDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await BreedersDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await BreedersDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
53
backend/src/services/containers.js
Normal file
53
backend/src/services/containers.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
const db = require('../db/models');
|
||||||
|
const ContainersDBApi = require('../db/api/containers');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class ContainersService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await ContainersDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await ContainersDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('containersNotFound');
|
||||||
|
const updated = await ContainersDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await ContainersDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await ContainersDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
53
backend/src/services/crop_types.js
Normal file
53
backend/src/services/crop_types.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
const db = require('../db/models');
|
||||||
|
const Crop_typesDBApi = require('../db/api/crop_types');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class Crop_typesService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Crop_typesDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await Crop_typesDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('crop_typesNotFound');
|
||||||
|
const updated = await Crop_typesDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Crop_typesDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Crop_typesDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
52
backend/src/services/groups.js
Normal file
52
backend/src/services/groups.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const groupsDBApi = require('../db/api/groups');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class groupsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await groupsDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await groupsDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('groupsNotFound');
|
||||||
|
const updated = await groupsDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await groupsDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await groupsDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
53
backend/src/services/growth_habits.js
Normal file
53
backend/src/services/growth_habits.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
const db = require('../db/models');
|
||||||
|
const Growth_habitsDBApi = require('../db/api/growth_habits');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class Growth_habitsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Growth_habitsDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await Growth_habitsDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('growth_habitsNotFound');
|
||||||
|
const updated = await Growth_habitsDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Growth_habitsDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Growth_habitsDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
52
backend/src/services/locations.js
Normal file
52
backend/src/services/locations.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const locationsDBApi = require('../db/api/locations');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class locationsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await locationsDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await locationsDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('locationsNotFound');
|
||||||
|
const updated = await locationsDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await locationsDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await locationsDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
52
backend/src/services/programs.js
Normal file
52
backend/src/services/programs.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const programsDBApi = require('../db/api/programs');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class programsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await programsDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await programsDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('programsNotFound');
|
||||||
|
const updated = await programsDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await programsDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await programsDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
53
backend/src/services/propagation_types.js
Normal file
53
backend/src/services/propagation_types.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
const db = require('../db/models');
|
||||||
|
const Propagation_typesDBApi = require('../db/api/propagation_types');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class Propagation_typesService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Propagation_typesDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await Propagation_typesDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('propagation_typesNotFound');
|
||||||
|
const updated = await Propagation_typesDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Propagation_typesDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Propagation_typesDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
53
backend/src/services/retailer_programs.js
Normal file
53
backend/src/services/retailer_programs.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
const db = require('../db/models');
|
||||||
|
const Retailer_programsDBApi = require('../db/api/retailer_programs');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class Retailer_programsService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Retailer_programsDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await Retailer_programsDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('retailer_programsNotFound');
|
||||||
|
const updated = await Retailer_programsDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Retailer_programsDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await Retailer_programsDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
52
backend/src/services/retailers.js
Normal file
52
backend/src/services/retailers.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const retailersDBApi = require('../db/api/retailers');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class retailersService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await retailersDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await retailersDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('retailersNotFound');
|
||||||
|
const updated = await retailersDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await retailersDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await retailersDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
52
backend/src/services/sites.js
Normal file
52
backend/src/services/sites.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const db = require('../db/models');
|
||||||
|
const sitesDBApi = require('../db/api/sites');
|
||||||
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
|
|
||||||
|
module.exports = class sitesService {
|
||||||
|
static async create(data, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await sitesDBApi.create(data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(data, id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
const record = await sitesDBApi.findBy({ id }, { transaction });
|
||||||
|
if (!record) throw new ValidationError('sitesNotFound');
|
||||||
|
const updated = await sitesDBApi.update(id, data, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
return updated;
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteByIds(ids, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await sitesDBApi.deleteByIds(ids, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(id, currentUser) {
|
||||||
|
const transaction = await db.sequelize.transaction();
|
||||||
|
try {
|
||||||
|
await sitesDBApi.remove(id, { currentUser, transaction });
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -3,10 +3,9 @@ import { mdiLogout, mdiClose } from '@mdi/js'
|
|||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
import AsideMenuList from './AsideMenuList'
|
import AsideMenuList from './AsideMenuList'
|
||||||
import { MenuAsideItem } from '../interfaces'
|
import { MenuAsideItem } from '../interfaces'
|
||||||
import { useAppSelector } from '../stores/hooks'
|
import { useAppSelector, useAppDispatch } from '../stores/hooks'
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { useAppDispatch } from '../stores/hooks';
|
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
|
|||||||
78
frontend/src/components/Breeders/TableBreeders.tsx
Normal file
78
frontend/src/components/Breeders/TableBreeders.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/breeders/breedersSlice'
|
||||||
|
import { loadColumns } from "./configureBreedersCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableBreeders = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { breeders, loading, count, refetch } = useAppSelector((state) => state.breeders);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
color="danger"
|
||||||
|
label="Delete selected"
|
||||||
|
icon={mdiDelete}
|
||||||
|
onClick={() => setIsDeleteModalActive(true)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={breeders || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableBreeders;
|
||||||
33
frontend/src/components/Breeders/configureBreedersCols.tsx
Normal file
33
frontend/src/components/Breeders/configureBreedersCols.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_BREEDERS')
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
headerName: 'Name',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
itemId={params?.row?.id}
|
||||||
|
pathEdit={`/breeders/breeders-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/breeders/breeders-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
onDelete={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
|
}
|
||||||
73
frontend/src/components/Containers/TableContainers.tsx
Normal file
73
frontend/src/components/Containers/TableContainers.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/containers/containersSlice'
|
||||||
|
import { loadColumns } from "./configureContainersCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableContainers = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { containers, loading, count, refetch } = useAppSelector((state) => state.containers);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton color="danger" label="Delete selected" icon={mdiDelete} onClick={() => setIsDeleteModalActive(true)} />,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={containers || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableContainers;
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_CONTAINERS')
|
||||||
|
const hasDeletePermission = hasPermission(user, 'DELETE_CONTAINERS')
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{ field: 'name', headerName: 'Name', flex: 1 },
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
params={params}
|
||||||
|
pathEdit={`/containers/containers-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/containers/containers-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
hasDeletePermission={hasDeletePermission}
|
||||||
|
deleteHandler={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return columns
|
||||||
|
}
|
||||||
73
frontend/src/components/Crop_types/TableCrop_types.tsx
Normal file
73
frontend/src/components/Crop_types/TableCrop_types.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/crop_types/crop_typesSlice'
|
||||||
|
import { loadColumns } from "./configureCrop_typesCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableCrop_types = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { crop_types, loading, count, refetch } = useAppSelector((state) => state.crop_types);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton color="danger" label="Delete selected" icon={mdiDelete} onClick={() => setIsDeleteModalActive(true)} />,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={crop_types || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableCrop_types;
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_CROP_TYPES')
|
||||||
|
const hasDeletePermission = hasPermission(user, 'DELETE_CROP_TYPES')
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{ field: 'name', headerName: 'Name', flex: 1 },
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
params={params}
|
||||||
|
pathEdit={`/crop_types/crop_types-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/crop_types/crop_types-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
hasDeletePermission={hasDeletePermission}
|
||||||
|
deleteHandler={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return columns
|
||||||
|
}
|
||||||
78
frontend/src/components/Groups/TableGroups.tsx
Normal file
78
frontend/src/components/Groups/TableGroups.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/groups/groupsSlice'
|
||||||
|
import { loadColumns } from "./configureGroupsCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableGroups = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { groups, loading, count, refetch } = useAppSelector((state) => state.groups);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
color="danger"
|
||||||
|
label="Delete selected"
|
||||||
|
icon={mdiDelete}
|
||||||
|
onClick={() => setIsDeleteModalActive(true)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={groups || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableGroups;
|
||||||
33
frontend/src/components/Groups/configureGroupsCols.tsx
Normal file
33
frontend/src/components/Groups/configureGroupsCols.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_GROUPS')
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
headerName: 'Name',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
itemId={params?.row?.id}
|
||||||
|
pathEdit={`/groups/groups-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/groups/groups-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
onDelete={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
|
}
|
||||||
73
frontend/src/components/Growth_habits/TableGrowth_habits.tsx
Normal file
73
frontend/src/components/Growth_habits/TableGrowth_habits.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/growth_habits/growth_habitsSlice'
|
||||||
|
import { loadColumns } from "./configureGrowth_habitsCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableGrowth_habits = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { growth_habits, loading, count, refetch } = useAppSelector((state) => state.growth_habits);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton color="danger" label="Delete selected" icon={mdiDelete} onClick={() => setIsDeleteModalActive(true)} />,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={growth_habits || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableGrowth_habits;
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_GROWTH_HABITS')
|
||||||
|
const hasDeletePermission = hasPermission(user, 'DELETE_GROWTH_HABITS')
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{ field: 'name', headerName: 'Name', flex: 1 },
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
params={params}
|
||||||
|
pathEdit={`/growth_habits/growth_habits-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/growth_habits/growth_habits-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
hasDeletePermission={hasDeletePermission}
|
||||||
|
deleteHandler={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return columns
|
||||||
|
}
|
||||||
78
frontend/src/components/Locations/TableLocations.tsx
Normal file
78
frontend/src/components/Locations/TableLocations.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/locations/locationsSlice'
|
||||||
|
import { loadColumns } from "./configureLocationsCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableLocations = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { locations, loading, count, refetch } = useAppSelector((state) => state.locations);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
color="danger"
|
||||||
|
label="Delete selected"
|
||||||
|
icon={mdiDelete}
|
||||||
|
onClick={() => setIsDeleteModalActive(true)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={locations || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableLocations;
|
||||||
33
frontend/src/components/Locations/configureLocationsCols.tsx
Normal file
33
frontend/src/components/Locations/configureLocationsCols.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_LOCATIONS')
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
headerName: 'Name',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
itemId={params?.row?.id}
|
||||||
|
pathEdit={`/locations/locations-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/locations/locations-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
onDelete={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
|
}
|
||||||
@ -9,11 +9,12 @@ import { useAppSelector } from '../stores/hooks';
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
menu: MenuNavBarItem[]
|
menu: MenuNavBarItem[]
|
||||||
|
leftMenu?: MenuNavBarItem[]
|
||||||
className: string
|
className: string
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavBar({ menu, className = '', children }: Props) {
|
export default function NavBar({ menu, leftMenu = [], className = '', children }: Props) {
|
||||||
const [isMenuNavBarActive, setIsMenuNavBarActive] = useState(false)
|
const [isMenuNavBarActive, setIsMenuNavBarActive] = useState(false)
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
@ -38,7 +39,12 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
className={`${className} top-0 inset-x-0 fixed ${bgColor} h-14 z-30 transition-position w-screen lg:w-auto dark:bg-dark-800`}
|
className={`${className} top-0 inset-x-0 fixed ${bgColor} h-14 z-30 transition-position w-screen lg:w-auto dark:bg-dark-800`}
|
||||||
>
|
>
|
||||||
<div className={`flex lg:items-stretch ${containerMaxW} ${isScrolled && `border-b border-pavitra-400 dark:border-dark-700`}`}>
|
<div className={`flex lg:items-stretch ${containerMaxW} ${isScrolled && `border-b border-pavitra-400 dark:border-dark-700`}`}>
|
||||||
<div className="flex flex-1 items-stretch h-14">{children}</div>
|
<div className="flex flex-1 items-stretch h-14">
|
||||||
|
<div className="hidden lg:flex items-stretch overflow-x-auto aside-scrollbars">
|
||||||
|
<NavBarMenuList menu={leftMenu} />
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
<div className="flex-none items-stretch flex h-14 lg:hidden">
|
<div className="flex-none items-stretch flex h-14 lg:hidden">
|
||||||
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
||||||
<BaseIcon path={isMenuNavBarActive ? mdiClose : mdiDotsVertical} size="24" />
|
<BaseIcon path={isMenuNavBarActive ? mdiClose : mdiDotsVertical} size="24" />
|
||||||
@ -49,6 +55,9 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
isMenuNavBarActive ? 'block' : 'hidden'
|
isMenuNavBarActive ? 'block' : 'hidden'
|
||||||
} flex items-center max-h-screen-menu overflow-y-auto lg:overflow-visible absolute w-screen top-14 left-0 ${bgColor} shadow-lg lg:w-auto lg:flex lg:static lg:shadow-none dark:bg-dark-800`}
|
} flex items-center max-h-screen-menu overflow-y-auto lg:overflow-visible absolute w-screen top-14 left-0 ${bgColor} shadow-lg lg:w-auto lg:flex lg:static lg:shadow-none dark:bg-dark-800`}
|
||||||
>
|
>
|
||||||
|
<div className="lg:hidden">
|
||||||
|
<NavBarMenuList menu={leftMenu} />
|
||||||
|
</div>
|
||||||
<NavBarMenuList menu={menu} />
|
<NavBarMenuList menu={menu} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import React, {useEffect, useRef} from 'react'
|
import React, {useEffect, useRef, useState} from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useState } from 'react'
|
|
||||||
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
||||||
import BaseDivider from './BaseDivider'
|
import BaseDivider from './BaseDivider'
|
||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
@ -39,17 +38,21 @@ export default function NavBarItem({ item }: Props) {
|
|||||||
}, [router.pathname]);
|
}, [router.pathname]);
|
||||||
|
|
||||||
const componentClass = [
|
const componentClass = [
|
||||||
'block lg:flex items-center relative cursor-pointer',
|
'block lg:flex items-center relative',
|
||||||
|
item.isHeader ? 'cursor-default' : 'cursor-pointer',
|
||||||
isDropdownActive
|
isDropdownActive
|
||||||
? `${navBarItemLabelActiveColorStyle} dark:text-slate-400`
|
? `${navBarItemLabelActiveColorStyle} dark:text-slate-400`
|
||||||
: `${navBarItemLabelStyle} dark:text-white dark:hover:text-slate-400 ${navBarItemLabelHoverStyle}`,
|
: `${navBarItemLabelStyle} dark:text-white dark:hover:text-slate-400 ${item.isHeader ? '' : navBarItemLabelHoverStyle}`,
|
||||||
item.menu ? 'lg:py-2 lg:px-3' : 'py-2 px-3',
|
item.menu ? 'lg:py-2 lg:px-3' : 'py-2 px-3',
|
||||||
item.isDesktopNoLabel ? 'lg:w-16 lg:justify-center' : '',
|
item.isDesktopNoLabel ? 'lg:w-16 lg:justify-center' : '',
|
||||||
|
item.isHeader ? 'text-xs uppercase font-bold text-gray-400 dark:text-slate-500 mt-2' : ''
|
||||||
].join(' ')
|
].join(' ')
|
||||||
|
|
||||||
const itemLabel = item.isCurrentUser ? userName : item.label
|
const itemLabel = item.isCurrentUser ? userName : item.label
|
||||||
|
|
||||||
const handleMenuClick = () => {
|
const handleMenuClick = () => {
|
||||||
|
if (item.isHeader) return
|
||||||
|
|
||||||
if (item.menu) {
|
if (item.menu) {
|
||||||
setIsDropdownActive(!isDropdownActive)
|
setIsDropdownActive(!isDropdownActive)
|
||||||
}
|
}
|
||||||
@ -86,9 +89,9 @@ export default function NavBarItem({ item }: Props) {
|
|||||||
}`}
|
}`}
|
||||||
onClick={handleMenuClick}
|
onClick={handleMenuClick}
|
||||||
>
|
>
|
||||||
{item.icon && <BaseIcon path={item.icon} size={22} className="transition-colors" />}
|
{item.icon && <BaseIcon path={item.icon} size={item.isHeader ? 16 : 22} className="transition-colors" />}
|
||||||
<span
|
<span
|
||||||
className={`px-2 transition-colors w-40 grow ${
|
className={`px-2 transition-colors ${
|
||||||
item.isDesktopNoLabel && item.icon ? 'lg:hidden' : ''
|
item.isDesktopNoLabel && item.icon ? 'lg:hidden' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -120,7 +123,7 @@ export default function NavBarItem({ item }: Props) {
|
|||||||
return <BaseDivider navBar />
|
return <BaseDivider navBar />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.href) {
|
if (item.href && !item.isHeader) {
|
||||||
return (
|
return (
|
||||||
<Link href={item.href} target={item.target} className={componentClass}>
|
<Link href={item.href} target={item.target} className={componentClass}>
|
||||||
{NavBarItemComponentContents}
|
{NavBarItemComponentContents}
|
||||||
|
|||||||
@ -1,19 +1,29 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { MenuNavBarItem } from '../interfaces'
|
import { MenuNavBarItem } from '../interfaces'
|
||||||
import NavBarItem from './NavBarItem'
|
import NavBarItem from './NavBarItem'
|
||||||
|
import { useAppSelector } from '../stores/hooks'
|
||||||
|
import { hasPermission } from '../helpers/userPermissions'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
menu: MenuNavBarItem[]
|
menu: MenuNavBarItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavBarMenuList({ menu }: Props) {
|
export default function NavBarMenuList({ menu }: Props) {
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth)
|
||||||
|
|
||||||
|
if (!currentUser) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{menu.map((item, index) => (
|
{menu.map((item, index) => {
|
||||||
|
if (!hasPermission(currentUser, item.permissions)) return null
|
||||||
|
|
||||||
|
return (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<NavBarItem item={item} />
|
<NavBarItem item={item} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
78
frontend/src/components/Programs/TablePrograms.tsx
Normal file
78
frontend/src/components/Programs/TablePrograms.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/programs/programsSlice'
|
||||||
|
import { loadColumns } from "./configureProgramsCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TablePrograms = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { programs, loading, count, refetch } = useAppSelector((state) => state.programs);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
color="danger"
|
||||||
|
label="Delete selected"
|
||||||
|
icon={mdiDelete}
|
||||||
|
onClick={() => setIsDeleteModalActive(true)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={programs || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TablePrograms;
|
||||||
33
frontend/src/components/Programs/configureProgramsCols.tsx
Normal file
33
frontend/src/components/Programs/configureProgramsCols.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_PROGRAMS')
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
headerName: 'Name',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
itemId={params?.row?.id}
|
||||||
|
pathEdit={`/programs/programs-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/programs/programs-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
onDelete={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/propagation_types/propagation_typesSlice'
|
||||||
|
import { loadColumns } from "./configurePropagation_typesCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TablePropagation_types = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { propagation_types, loading, count, refetch } = useAppSelector((state) => state.propagation_types);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
color="danger"
|
||||||
|
label="Delete selected"
|
||||||
|
icon={mdiDelete}
|
||||||
|
onClick={() => setIsDeleteModalActive(true)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={propagation_types || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TablePropagation_types;
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_PROPAGATION_TYPES')
|
||||||
|
const hasDeletePermission = hasPermission(user, 'DELETE_PROPAGATION_TYPES')
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
headerName: 'Name',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
params={params}
|
||||||
|
pathEdit={`/propagation_types/propagation_types-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/propagation_types/propagation_types-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
hasDeletePermission={hasDeletePermission}
|
||||||
|
deleteHandler={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/retailer_programs/retailer_programsSlice'
|
||||||
|
import { loadColumns } from "./configureRetailer_programsCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableRetailer_programs = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { retailer_programs, loading, count, refetch } = useAppSelector((state) => state.retailer_programs);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton color="danger" label="Delete selected" icon={mdiDelete} onClick={() => setIsDeleteModalActive(true)} />,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={retailer_programs || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableRetailer_programs;
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_RETAILER_PROGRAMS')
|
||||||
|
const hasDeletePermission = hasPermission(user, 'DELETE_RETAILER_PROGRAMS')
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{ field: 'name', headerName: 'Name', flex: 1 },
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
params={params}
|
||||||
|
pathEdit={`/retailer_programs/retailer_programs-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/retailer_programs/retailer_programs-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
hasDeletePermission={hasDeletePermission}
|
||||||
|
deleteHandler={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return columns
|
||||||
|
}
|
||||||
78
frontend/src/components/Retailers/TableRetailers.tsx
Normal file
78
frontend/src/components/Retailers/TableRetailers.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/retailers/retailersSlice'
|
||||||
|
import { loadColumns } from "./configureRetailersCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableRetailers = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { retailers, loading, count, refetch } = useAppSelector((state) => state.retailers);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
color="danger"
|
||||||
|
label="Delete selected"
|
||||||
|
icon={mdiDelete}
|
||||||
|
onClick={() => setIsDeleteModalActive(true)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={retailers || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableRetailers;
|
||||||
33
frontend/src/components/Retailers/configureRetailersCols.tsx
Normal file
33
frontend/src/components/Retailers/configureRetailersCols.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_RETAILERS')
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
headerName: 'Name',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
itemId={params?.row?.id}
|
||||||
|
pathEdit={`/retailers/retailers-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/retailers/retailers-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
onDelete={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
|
}
|
||||||
78
frontend/src/components/Sites/TableSites.tsx
Normal file
78
frontend/src/components/Sites/TableSites.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
||||||
|
import { fetch, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/sites/sitesSlice'
|
||||||
|
import { loadColumns } from "./configureSitesCols";
|
||||||
|
import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import { mdiDelete } from '@mdi/js';
|
||||||
|
import CardBoxModal from '../CardBoxModal';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
const TableSites = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { sites, loading, count, refetch } = useAppSelector((state) => state.sites);
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const [paginationModel, setPaginationModel] = useState({ pageSize: 10, page: 0 });
|
||||||
|
const [selectionModel, setSelectionModel] = useState<GridRowSelectionModel>([]);
|
||||||
|
const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = `?limit=${paginationModel.pageSize}&page=${paginationModel.page}`;
|
||||||
|
dispatch(fetch({ query }));
|
||||||
|
if (refetch) dispatch(setRefetch(false));
|
||||||
|
}, [dispatch, paginationModel, refetch]);
|
||||||
|
|
||||||
|
const deleteHandler = (id: string) => {
|
||||||
|
dispatch(deleteItem(id)).then(() => dispatch(setRefetch(true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteByIds = () => {
|
||||||
|
dispatch(deleteItemsByIds(selectionModel)).then(() => {
|
||||||
|
dispatch(setRefetch(true));
|
||||||
|
setIsDeleteModalActive(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = loadColumns(currentUser, deleteHandler);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
{selectionModel.length > 0 && typeof document !== 'undefined' && document.getElementById('delete-rows-button') &&
|
||||||
|
createPortal(
|
||||||
|
<BaseButton
|
||||||
|
color="danger"
|
||||||
|
label="Delete selected"
|
||||||
|
icon={mdiDelete}
|
||||||
|
onClick={() => setIsDeleteModalActive(true)}
|
||||||
|
/>,
|
||||||
|
document.getElementById('delete-rows-button') as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<DataGrid
|
||||||
|
rows={sites || []}
|
||||||
|
columns={columns}
|
||||||
|
rowCount={count}
|
||||||
|
loading={loading}
|
||||||
|
paginationMode="server"
|
||||||
|
paginationModel={paginationModel}
|
||||||
|
onPaginationModelChange={setPaginationModel}
|
||||||
|
checkboxSelection
|
||||||
|
onRowSelectionModelChange={(newSelectionModel) => setSelectionModel(newSelectionModel)}
|
||||||
|
rowSelectionModel={selectionModel}
|
||||||
|
/>
|
||||||
|
<CardBoxModal
|
||||||
|
title="Delete items"
|
||||||
|
buttonColor="danger"
|
||||||
|
buttonLabel="Confirm"
|
||||||
|
isActive={isDeleteModalActive}
|
||||||
|
onConfirm={handleDeleteByIds}
|
||||||
|
onCancel={() => setIsDeleteModalActive(false)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to delete {selectionModel.length} items?</p>
|
||||||
|
</CardBoxModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableSites;
|
||||||
33
frontend/src/components/Sites/configureSitesCols.tsx
Normal file
33
frontend/src/components/Sites/configureSitesCols.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { GridColDef } from '@mui/x-data-grid'
|
||||||
|
import React from 'react'
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
import ListActionsPopover from "../ListActionsPopover";
|
||||||
|
|
||||||
|
export const loadColumns = (user: any, deleteHandler: any) => {
|
||||||
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_SITES')
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
headerName: 'Name',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'actions',
|
||||||
|
headerName: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
width: 100,
|
||||||
|
renderCell: (params) => (
|
||||||
|
<ListActionsPopover
|
||||||
|
itemId={params?.row?.id}
|
||||||
|
pathEdit={`/sites/sites-edit/?id=${params?.row?.id}`}
|
||||||
|
pathView={`/sites/sites-view/?id=${params?.row?.id}`}
|
||||||
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
onDelete={deleteHandler}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import { Field, Form, Formik } from "formik";
|
|||||||
import {
|
import {
|
||||||
DataGrid,
|
DataGrid,
|
||||||
GridColDef,
|
GridColDef,
|
||||||
|
GridToolbar,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import {loadColumns} from "./configureTrialsCols";
|
import {loadColumns} from "./configureTrialsCols";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@ -233,6 +234,9 @@ const TableSampleTrials = ({ filterItems, setFilterItems, filters, showGrid }) =
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
slots={{
|
||||||
|
toolbar: GridToolbar,
|
||||||
|
}}
|
||||||
disableRowSelectionOnClick
|
disableRowSelectionOnClick
|
||||||
onProcessRowUpdateError={(params) => {
|
onProcessRowUpdateError={(params) => {
|
||||||
console.log('Error', params);
|
console.log('Error', params);
|
||||||
|
|||||||
@ -1,18 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import {
|
import {
|
||||||
GridActionsCellItem,
|
|
||||||
GridRowParams,
|
GridRowParams,
|
||||||
GridValueGetterParams,
|
GridValueGetterParams,
|
||||||
} from '@mui/x-data-grid';
|
} from '@mui/x-data-grid';
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
import dataFormatter from '../../helpers/dataFormatter'
|
||||||
import DataGridMultiSelect from "../DataGridMultiSelect";
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
import ListActionsPopover from '../ListActionsPopover';
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
import {hasPermission} from "../../helpers/userPermissions";
|
||||||
|
|
||||||
type Params = (id: string) => void;
|
type Params = (id: string) => void;
|
||||||
@ -20,345 +13,160 @@ type Params = (id: string) => void;
|
|||||||
export const loadColumns = async (
|
export const loadColumns = async (
|
||||||
onDelete: Params,
|
onDelete: Params,
|
||||||
entityName: string,
|
entityName: string,
|
||||||
|
|
||||||
user
|
user
|
||||||
|
|
||||||
) => {
|
) => {
|
||||||
async function callOptionsApi(entityName: string) {
|
async function callOptionsApi(entityName: string) {
|
||||||
|
|
||||||
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
||||||
return data.data;
|
return data.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_TRIALS')
|
const hasUpdatePermission = hasPermission(user, 'UPDATE_TRIALS')
|
||||||
|
const weeksOptions = Array.from({ length: 53 }, (_, i) => ({ id: i + 1, label: `Week ${i + 1}` }));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
|
||||||
field: 'tenant',
|
|
||||||
headerName: 'Tenant',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
sortable: false,
|
|
||||||
type: 'singleSelect',
|
|
||||||
getOptionValue: (value: any) => value?.id,
|
|
||||||
getOptionLabel: (value: any) => value?.label,
|
|
||||||
valueOptions: await callOptionsApi('tenants'),
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
params?.value?.id ?? params?.value,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'project',
|
|
||||||
headerName: 'Project',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
sortable: false,
|
|
||||||
type: 'singleSelect',
|
|
||||||
getOptionValue: (value: any) => value?.id,
|
|
||||||
getOptionLabel: (value: any) => value?.label,
|
|
||||||
valueOptions: await callOptionsApi('projects'),
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
params?.value?.id ?? params?.value,
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
headerName: 'TrialName',
|
headerName: 'Trial Name',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 150,
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
headerClassName: 'datagrid--header',
|
||||||
cellClassName: 'datagrid--cell',
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'variety_name',
|
field: 'trial_code',
|
||||||
headerName: 'VarietyName',
|
headerName: 'Trial Code',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
headerClassName: 'datagrid--header',
|
||||||
cellClassName: 'datagrid--cell',
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'breeder',
|
field: 'breeder',
|
||||||
headerName: 'Breeder',
|
headerName: 'Breeder',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 150,
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
headerClassName: 'datagrid--header',
|
||||||
cellClassName: 'datagrid--cell',
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
type: 'singleSelect',
|
||||||
|
getOptionValue: (value: any) => value?.id,
|
||||||
|
getOptionLabel: (value: any) => value?.label,
|
||||||
|
valueOptions: await callOptionsApi('breeders'),
|
||||||
|
valueGetter: (params: GridValueGetterParams) => params?.row?.breeder?.name || params?.row?.breeder?.id || params?.value?.id || params?.value,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'batch_code',
|
field: 'variety_name',
|
||||||
headerName: 'BatchCode',
|
headerName: 'Variety Name',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 150,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'container',
|
||||||
|
headerName: 'Container',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 150,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
type: 'singleSelect',
|
||||||
|
getOptionValue: (value: any) => value?.id,
|
||||||
|
getOptionLabel: (value: any) => value?.label,
|
||||||
|
valueOptions: await callOptionsApi('containers'),
|
||||||
|
valueGetter: (params: GridValueGetterParams) => params?.row?.container?.name || params?.row?.container?.id || params?.value?.id || params?.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'quantity',
|
||||||
|
headerName: 'Quantity',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 100,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'site',
|
||||||
|
headerName: 'Site',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 150,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
type: 'singleSelect',
|
||||||
|
getOptionValue: (value: any) => value?.id,
|
||||||
|
getOptionLabel: (value: any) => value?.label,
|
||||||
|
valueOptions: await callOptionsApi('sites'),
|
||||||
|
valueGetter: (params: GridValueGetterParams) => params?.row?.site?.name || params?.row?.site?.id || params?.value?.id || params?.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'location',
|
||||||
|
headerName: 'Location',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 150,
|
||||||
|
headerClassName: 'datagrid--header',
|
||||||
|
cellClassName: 'datagrid--cell',
|
||||||
|
editable: hasUpdatePermission,
|
||||||
|
type: 'singleSelect',
|
||||||
|
getOptionValue: (value: any) => value?.id,
|
||||||
|
getOptionLabel: (value: any) => value?.label,
|
||||||
|
valueOptions: await callOptionsApi('locations'),
|
||||||
|
valueGetter: (params: GridValueGetterParams) => params?.row?.location?.name || params?.row?.location?.id || params?.value?.id || params?.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'receiveWeek',
|
||||||
|
headerName: 'Receive Week',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
headerClassName: 'datagrid--header',
|
||||||
cellClassName: 'datagrid--cell',
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
type: 'singleSelect',
|
||||||
|
getOptionValue: (value: any) => value?.id,
|
||||||
|
getOptionLabel: (value: any) => value?.label,
|
||||||
|
valueOptions: weeksOptions,
|
||||||
|
valueFormatter: (params) => params.value ? `Week ${params.value}` : '',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'trial_type',
|
field: 'finishWeek',
|
||||||
headerName: 'TrialType',
|
headerName: 'Finish Week',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
headerClassName: 'datagrid--header',
|
||||||
cellClassName: 'datagrid--cell',
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
type: 'singleSelect',
|
||||||
|
getOptionValue: (value: any) => value?.id,
|
||||||
|
getOptionLabel: (value: any) => value?.label,
|
||||||
|
valueOptions: weeksOptions,
|
||||||
|
valueFormatter: (params) => params.value ? `Week ${params.value}` : '',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'status',
|
||||||
headerName: 'Status',
|
headerName: 'Status',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
headerClassName: 'datagrid--header',
|
||||||
cellClassName: 'datagrid--cell',
|
cellClassName: 'datagrid--cell',
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
editable: hasUpdatePermission,
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
field: 'planted_at',
|
|
||||||
headerName: 'PlantedAt',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'dateTime',
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
new Date(params.row.planted_at),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'harvested_at',
|
|
||||||
headerName: 'HarvestedAt',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'dateTime',
|
|
||||||
valueGetter: (params: GridValueGetterParams) =>
|
|
||||||
new Date(params.row.harvested_at),
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'greenhouse',
|
|
||||||
headerName: 'Greenhouse',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'zone',
|
|
||||||
headerName: 'Zone',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'bench',
|
|
||||||
headerName: 'Bench',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'plants_count',
|
|
||||||
headerName: 'PlantsCount',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'target_temperature_c',
|
|
||||||
headerName: 'TargetTemperature(C)',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'target_humidity_percent',
|
|
||||||
headerName: 'TargetHumidity(%)',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'target_ec',
|
|
||||||
headerName: 'TargetEC',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'target_ph',
|
|
||||||
headerName: 'TargetpH',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
type: 'number',
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'notes',
|
|
||||||
headerName: 'Notes',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 120,
|
|
||||||
filterable: false,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
|
|
||||||
|
|
||||||
editable: hasUpdatePermission,
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
field: 'actions',
|
field: 'actions',
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
minWidth: 30,
|
minWidth: 80,
|
||||||
headerClassName: 'datagrid--header',
|
headerClassName: 'datagrid--header',
|
||||||
cellClassName: 'datagrid--cell',
|
cellClassName: 'datagrid--cell',
|
||||||
getActions: (params: GridRowParams) => {
|
getActions: (params: GridRowParams) => {
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<div key={params?.row?.id}>
|
<div key={params?.row?.id}>
|
||||||
<ListActionsPopover
|
<ListActionsPopover
|
||||||
@ -366,9 +174,7 @@ export const loadColumns = async (
|
|||||||
itemId={params?.row?.id}
|
itemId={params?.row?.id}
|
||||||
pathEdit={`/trials/trials-edit/?id=${params?.row?.id}`}
|
pathEdit={`/trials/trials-edit/?id=${params?.row?.id}`}
|
||||||
pathView={`/trials/trials-view/?id=${params?.row?.id}`}
|
pathView={`/trials/trials-view/?id=${params?.row?.id}`}
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
hasUpdatePermission={hasUpdatePermission}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>,
|
</div>,
|
||||||
]
|
]
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export type MenuAsideItem = {
|
|||||||
target?: string
|
target?: string
|
||||||
color?: ColorButtonKey
|
color?: ColorButtonKey
|
||||||
isLogout?: boolean
|
isLogout?: boolean
|
||||||
|
isHeader?: boolean
|
||||||
withDevider?: boolean;
|
withDevider?: boolean;
|
||||||
menu?: MenuAsideItem[]
|
menu?: MenuAsideItem[]
|
||||||
permissions?: string | string[]
|
permissions?: string | string[]
|
||||||
@ -22,11 +23,13 @@ export type MenuNavBarItem = {
|
|||||||
href?: string
|
href?: string
|
||||||
target?: string
|
target?: string
|
||||||
isDivider?: boolean
|
isDivider?: boolean
|
||||||
|
isHeader?: boolean
|
||||||
isLogout?: boolean
|
isLogout?: boolean
|
||||||
isDesktopNoLabel?: boolean
|
isDesktopNoLabel?: boolean
|
||||||
isToggleLightDark?: boolean
|
isToggleLightDark?: boolean
|
||||||
isCurrentUser?: boolean
|
isCurrentUser?: boolean
|
||||||
menu?: MenuNavBarItem[]
|
menu?: MenuNavBarItem[]
|
||||||
|
permissions?: string | string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ColorKey = 'white' | 'light' | 'contrast' | 'success' | 'danger' | 'warning' | 'info'
|
export type ColorKey = 'white' | 'light' | 'contrast' | 'success' | 'danger' | 'warning' | 'info'
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
import React, { ReactNode, useEffect } from 'react'
|
import React, { ReactNode, useEffect } from 'react'
|
||||||
import { useState } from 'react'
|
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
|
||||||
import menuAside from '../menuAside'
|
import menuAside from '../menuAside'
|
||||||
import menuNavBar from '../menuNavBar'
|
import menuNavBar from '../menuNavBar'
|
||||||
import BaseIcon from '../components/BaseIcon'
|
|
||||||
import NavBar from '../components/NavBar'
|
import NavBar from '../components/NavBar'
|
||||||
import NavBarItemPlain from '../components/NavBarItemPlain'
|
import NavBarItemPlain from '../components/NavBarItemPlain'
|
||||||
import AsideMenu from '../components/AsideMenu'
|
import NavBarMenuList from '../components/NavBarMenuList'
|
||||||
import FooterBar from '../components/FooterBar'
|
import FooterBar from '../components/FooterBar'
|
||||||
import { useAppDispatch, useAppSelector } from '../stores/hooks'
|
import { useAppDispatch, useAppSelector } from '../stores/hooks'
|
||||||
import Search from '../components/Search';
|
import Search from '../components/Search';
|
||||||
@ -67,60 +64,33 @@ export default function LayoutAuthenticated({
|
|||||||
|
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode)
|
const darkMode = useAppSelector((state) => state.style.darkMode)
|
||||||
|
|
||||||
const [isAsideMobileExpanded, setIsAsideMobileExpanded] = useState(false)
|
|
||||||
const [isAsideLgActive, setIsAsideLgActive] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleRouteChangeStart = () => {
|
const handleRouteChangeStart = () => {
|
||||||
setIsAsideMobileExpanded(false)
|
// Logic for route change start
|
||||||
setIsAsideLgActive(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
router.events.on('routeChangeStart', handleRouteChangeStart)
|
router.events.on('routeChangeStart', handleRouteChangeStart)
|
||||||
|
|
||||||
// If the component is unmounted, unsubscribe
|
|
||||||
// from the event with the `off` method:
|
|
||||||
return () => {
|
return () => {
|
||||||
router.events.off('routeChangeStart', handleRouteChangeStart)
|
router.events.off('routeChangeStart', handleRouteChangeStart)
|
||||||
}
|
}
|
||||||
}, [router.events, dispatch])
|
}, [router.events, dispatch])
|
||||||
|
|
||||||
|
|
||||||
const layoutAsidePadding = 'xl:pl-60'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${darkMode ? 'dark' : ''} overflow-hidden lg:overflow-visible`}>
|
<div className={`${darkMode ? 'dark' : ''} overflow-hidden lg:overflow-visible`}>
|
||||||
<div
|
<div
|
||||||
className={`${layoutAsidePadding} ${
|
className={`pt-14 min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-dark-800 dark:text-slate-100`}
|
||||||
isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''
|
|
||||||
} pt-14 min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-dark-800 dark:text-slate-100`}
|
|
||||||
>
|
>
|
||||||
<NavBar
|
<NavBar
|
||||||
menu={menuNavBar}
|
menu={menuNavBar}
|
||||||
className={`${layoutAsidePadding} ${isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''}`}
|
leftMenu={menuAside}
|
||||||
|
className={``}
|
||||||
>
|
>
|
||||||
<NavBarItemPlain
|
|
||||||
display="flex lg:hidden"
|
|
||||||
onClick={() => setIsAsideMobileExpanded(!isAsideMobileExpanded)}
|
|
||||||
>
|
|
||||||
<BaseIcon path={isAsideMobileExpanded ? mdiBackburger : mdiForwardburger} size="24" />
|
|
||||||
</NavBarItemPlain>
|
|
||||||
<NavBarItemPlain
|
|
||||||
display="hidden lg:flex xl:hidden"
|
|
||||||
onClick={() => setIsAsideLgActive(true)}
|
|
||||||
>
|
|
||||||
<BaseIcon path={mdiMenu} size="24" />
|
|
||||||
</NavBarItemPlain>
|
|
||||||
<NavBarItemPlain useMargin>
|
<NavBarItemPlain useMargin>
|
||||||
<Search />
|
<Search />
|
||||||
</NavBarItemPlain>
|
</NavBarItemPlain>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
<AsideMenu
|
|
||||||
isAsideMobileExpanded={isAsideMobileExpanded}
|
|
||||||
isAsideLgActive={isAsideLgActive}
|
|
||||||
menu={menuAside}
|
|
||||||
onAsideLgClose={() => setIsAsideLgActive(false)}
|
|
||||||
/>
|
|
||||||
{children}
|
{children}
|
||||||
<FooterBar>Hand-crafted & Made with ❤️</FooterBar>
|
<FooterBar>Hand-crafted & Made with ❤️</FooterBar>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,61 +7,12 @@ const menuAside: MenuAsideItem[] = [
|
|||||||
icon: icon.mdiViewDashboardOutline,
|
icon: icon.mdiViewDashboardOutline,
|
||||||
label: 'Dashboard',
|
label: 'Dashboard',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
href: '/users/users-list',
|
|
||||||
label: 'Users',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: icon.mdiAccountGroup ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_USERS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/roles/roles-list',
|
|
||||||
label: 'Roles',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: icon.mdiShieldAccountVariantOutline ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_ROLES'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/permissions/permissions-list',
|
|
||||||
label: 'Permissions',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: icon.mdiShieldAccountOutline ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_PERMISSIONS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/organizations/organizations-list',
|
|
||||||
label: 'Organizations',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_ORGANIZATIONS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/tenants/tenants-list',
|
|
||||||
label: 'Tenants',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: 'mdiDomain' in icon ? icon['mdiDomain' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_TENANTS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/impersonation_sessions/impersonation_sessions-list',
|
|
||||||
label: 'Impersonation sessions',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: 'mdiAccountSwitch' in icon ? icon['mdiAccountSwitch' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_IMPERSONATION_SESSIONS'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
href: '/projects/projects-list',
|
href: '/projects/projects-list',
|
||||||
label: 'Projects',
|
label: 'Projects',
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
icon: 'mdiBriefcaseOutline' in icon ? icon['mdiBriefcaseOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
icon: 'mdiBriefcaseOutline' in icon ? icon['mdiBriefcaseOutline' as keyof typeof icon] : icon.mdiTable,
|
||||||
permissions: 'READ_PROJECTS'
|
permissions: 'READ_PROJECTS'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -69,64 +20,187 @@ const menuAside: MenuAsideItem[] = [
|
|||||||
label: 'Trials',
|
label: 'Trials',
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
icon: 'mdiSprout' in icon ? icon['mdiSprout' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
icon: 'mdiSprout' in icon ? icon['mdiSprout' as keyof typeof icon] : icon.mdiTable,
|
||||||
permissions: 'READ_TRIALS'
|
permissions: 'READ_TRIALS'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/tracking_activity_types/tracking_activity_types-list',
|
label: 'Tracking',
|
||||||
label: 'Tracking activity types',
|
icon: icon.mdiTimelineTextOutline,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
menu: [
|
||||||
// @ts-ignore
|
{
|
||||||
icon: 'mdiFormatListBulletedType' in icon ? icon['mdiFormatListBulletedType' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
href: '/tracking_activities/tracking_activities-list',
|
||||||
permissions: 'READ_TRACKING_ACTIVITY_TYPES'
|
label: 'Activities',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_TRACKING_ACTIVITIES'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/tracking_activity_types/tracking_activity_types-list',
|
||||||
|
label: 'Activity Types',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_TRACKING_ACTIVITY_TYPES'
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/tracking_activities/tracking_activities-list',
|
label: 'Reports',
|
||||||
label: 'Tracking activities',
|
icon: icon.mdiFileChartOutline,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
menu: [
|
||||||
// @ts-ignore
|
{
|
||||||
icon: 'mdiTimelineTextOutline' in icon ? icon['mdiTimelineTextOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
href: '/report_definitions/report_definitions-list',
|
||||||
permissions: 'READ_TRACKING_ACTIVITIES'
|
label: 'Definitions',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_REPORT_DEFINITIONS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/report_runs/report_runs-list',
|
||||||
|
label: 'Runs',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_REPORT_RUNS'
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/dashboards/dashboards-list',
|
label: 'Settings',
|
||||||
label: 'Dashboards',
|
icon: icon.mdiCogOutline,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
menu: [
|
||||||
// @ts-ignore
|
{
|
||||||
icon: 'mdiViewDashboardOutline' in icon ? icon['mdiViewDashboardOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
label: 'Data Sets',
|
||||||
permissions: 'READ_DASHBOARDS'
|
icon: icon.mdiDatabaseOutline,
|
||||||
|
menu: [
|
||||||
|
{
|
||||||
|
label: 'Trials',
|
||||||
|
isHeader: true,
|
||||||
|
icon: icon.mdiSprout
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/breeders/breeders-list',
|
||||||
|
label: 'Breeder',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_BREEDERS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/propagation_types/propagation_types-list',
|
||||||
|
label: 'Propagation Type',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_PROPAGATION_TYPES'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/growth_habits/growth_habits-list',
|
||||||
|
label: 'Growth Habit',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_GROWTH_HABITS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/crop_types/crop_types-list',
|
||||||
|
label: 'Crop Type',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_CROP_TYPES'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/containers/containers-list',
|
||||||
|
label: 'Container',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_CONTAINERS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/retailer_programs/retailer_programs-list',
|
||||||
|
label: 'Retailer Program',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_RETAILER_PROGRAMS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/retailers/retailers-list',
|
||||||
|
label: 'Retailer',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_RETAILERS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/programs/programs-list',
|
||||||
|
label: 'Program',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_PROGRAMS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/groups/groups-list',
|
||||||
|
label: 'Group',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_GROUPS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/sites/sites-list',
|
||||||
|
label: 'Site',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_SITES'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/locations/locations-list',
|
||||||
|
label: 'Default Location',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_LOCATIONS'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/report_definitions/report_definitions-list',
|
label: 'System',
|
||||||
label: 'Report definitions',
|
icon: icon.mdiCogOutline,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
menu: [
|
||||||
// @ts-ignore
|
{
|
||||||
icon: 'mdiFileChartOutline' in icon ? icon['mdiFileChartOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
href: '/users/users-list',
|
||||||
permissions: 'READ_REPORT_DEFINITIONS'
|
label: 'Users',
|
||||||
},
|
icon: icon.mdiCircleSmall,
|
||||||
{
|
permissions: 'READ_USERS'
|
||||||
href: '/report_runs/report_runs-list',
|
},
|
||||||
label: 'Report runs',
|
{
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
href: '/roles/roles-list',
|
||||||
// @ts-ignore
|
label: 'Roles',
|
||||||
icon: 'mdiProgressDownload' in icon ? icon['mdiProgressDownload' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
icon: icon.mdiCircleSmall,
|
||||||
permissions: 'READ_REPORT_RUNS'
|
permissions: 'READ_ROLES'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/tenant_settings/tenant_settings-list',
|
href: '/permissions/permissions-list',
|
||||||
label: 'Tenant settings',
|
label: 'Permissions',
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
icon: icon.mdiCircleSmall,
|
||||||
// @ts-ignore
|
permissions: 'READ_PERMISSIONS'
|
||||||
icon: 'mdiCogOutline' in icon ? icon['mdiCogOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
},
|
||||||
permissions: 'READ_TENANT_SETTINGS'
|
{
|
||||||
|
href: '/organizations/organizations-list',
|
||||||
|
label: 'Organizations',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_ORGANIZATIONS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/tenants/tenants-list',
|
||||||
|
label: 'Tenants',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_TENANTS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/impersonation_sessions/impersonation_sessions-list',
|
||||||
|
label: 'Impersonation Sessions',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_IMPERSONATION_SESSIONS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/tenant_settings/tenant_settings-list',
|
||||||
|
label: 'Tenant Settings',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_TENANT_SETTINGS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/dashboards/dashboards-list',
|
||||||
|
label: 'Dashboard Layouts',
|
||||||
|
icon: icon.mdiCircleSmall,
|
||||||
|
permissions: 'READ_DASHBOARDS'
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/profile',
|
href: '/profile',
|
||||||
label: 'Profile',
|
label: 'Profile',
|
||||||
icon: icon.mdiAccountCircle,
|
icon: icon.mdiAccountCircle,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
href: '/api-docs',
|
href: '/api-docs',
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
|
|||||||
72
frontend/src/pages/breeders/breeders-edit.tsx
Normal file
72
frontend/src/pages/breeders/breeders-edit.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Formik, Form, Field } from 'formik'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import FormField from '../../components/FormField'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import BaseButtons from '../../components/BaseButtons'
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../stores/hooks"
|
||||||
|
import { update, fetch } from '../../stores/breeders/breedersSlice'
|
||||||
|
|
||||||
|
const BreedersEdit = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
const { id } = router.query;
|
||||||
|
const { breeders } = useAppSelector(state => state.breeders);
|
||||||
|
const [initialValues, setInitialValues] = useState({ name: '' });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
dispatch(fetch({ id }));
|
||||||
|
}
|
||||||
|
}, [dispatch, id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (breeders && !Array.isArray(breeders)) {
|
||||||
|
setInitialValues({ name: breeders.name || '' });
|
||||||
|
}
|
||||||
|
}, [breeders]);
|
||||||
|
|
||||||
|
const handleSubmit = async (values: any) => {
|
||||||
|
await dispatch(update({ id, data: values }));
|
||||||
|
router.push('/breeders/breeders-list');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Edit Propagation Type')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Edit Propagation Type" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<Formik initialValues={initialValues} onSubmit={handleSubmit} enableReinitialize>
|
||||||
|
<Form>
|
||||||
|
<FormField label="Name" help="Please enter the name">
|
||||||
|
<Field name="name" placeholder="Name" className="w-full h-10 px-3 border rounded-md" required />
|
||||||
|
</FormField>
|
||||||
|
<BaseButtons>
|
||||||
|
<BaseButton type="submit" color="info" label="Submit" />
|
||||||
|
<BaseButton type="button" color="info" outline label="Cancel" onClick={() => router.push('/breeders/breeders-list')} />
|
||||||
|
</BaseButtons>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BreedersEdit.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'UPDATE_PROPAGATION_TYPES'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BreedersEdit
|
||||||
44
frontend/src/pages/breeders/breeders-list.tsx
Normal file
44
frontend/src/pages/breeders/breeders-list.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import TableBreeders from '../../components/Breeders/TableBreeders'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import { useAppSelector } from "../../stores/hooks";
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
|
||||||
|
const BreedersListPage = () => {
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const hasCreatePermission = currentUser && hasPermission(currentUser, 'CREATE_PROPAGATION_TYPES');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Propagation Types')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Propagation Types" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<div className="mb-6 flex flex-wrap gap-3">
|
||||||
|
{hasCreatePermission && <BaseButton href={'/breeders/breeders-new'} color='info' label='New Item'/>}
|
||||||
|
<div id='delete-rows-button'></div>
|
||||||
|
</div>
|
||||||
|
<TableBreeders filterItems={[]} setFilterItems={() => undefined} filters={[]} showGrid={false} />
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BreedersListPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return (
|
||||||
|
<LayoutAuthenticated permission={'READ_PROPAGATION_TYPES'}>
|
||||||
|
{page}
|
||||||
|
</LayoutAuthenticated>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BreedersListPage
|
||||||
59
frontend/src/pages/breeders/breeders-new.tsx
Normal file
59
frontend/src/pages/breeders/breeders-new.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Formik, Form, Field } from 'formik'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import FormField from '../../components/FormField'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import BaseButtons from '../../components/BaseButtons'
|
||||||
|
import { useAppDispatch } from "../../stores/hooks"
|
||||||
|
import { create } from '../../stores/breeders/breedersSlice'
|
||||||
|
|
||||||
|
const BreedersNew = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const initialValues = { name: '' };
|
||||||
|
|
||||||
|
const handleSubmit = async (values: any) => {
|
||||||
|
await dispatch(create(values));
|
||||||
|
router.push('/breeders/breeders-list');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('New Propagation Type')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="New Propagation Type" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||||
|
<Form>
|
||||||
|
<FormField label="Name" help="Please enter the name">
|
||||||
|
<Field name="name" placeholder="Name" className="w-full h-10 px-3 border rounded-md" required />
|
||||||
|
</FormField>
|
||||||
|
<BaseButtons>
|
||||||
|
<BaseButton type="submit" color="info" label="Submit" />
|
||||||
|
<BaseButton type="button" color="info" outline label="Cancel" onClick={() => router.push('/breeders/breeders-list')} />
|
||||||
|
</BaseButtons>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BreedersNew.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'CREATE_PROPAGATION_TYPES'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BreedersNew
|
||||||
52
frontend/src/pages/breeders/breeders-view.tsx
Normal file
52
frontend/src/pages/breeders/breeders-view.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../stores/hooks"
|
||||||
|
import { fetch } from '../../stores/breeders/breedersSlice'
|
||||||
|
|
||||||
|
const BreedersView = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
const { id } = router.query;
|
||||||
|
const { breeders } = useAppSelector(state => state.breeders);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
dispatch(fetch({ id }));
|
||||||
|
}
|
||||||
|
}, [dispatch, id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('View Propagation Type')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="View Propagation Type" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="font-bold">Name:</label>
|
||||||
|
<p>{breeders?.name || 'N/A'}</p>
|
||||||
|
</div>
|
||||||
|
<BaseButton type="button" color="info" outline label="Back" onClick={() => router.push('/breeders/breeders-list')} />
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BreedersView.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'READ_PROPAGATION_TYPES'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BreedersView
|
||||||
72
frontend/src/pages/containers/containers-edit.tsx
Normal file
72
frontend/src/pages/containers/containers-edit.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Formik, Form, Field } from 'formik'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import FormField from '../../components/FormField'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import BaseButtons from '../../components/BaseButtons'
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../stores/hooks"
|
||||||
|
import { update, fetch } from '../../stores/containers/containersSlice'
|
||||||
|
|
||||||
|
const ContainersEdit = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
const { id } = router.query;
|
||||||
|
const { containers } = useAppSelector(state => state.containers);
|
||||||
|
const [initialValues, setInitialValues] = useState({ name: '' });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
dispatch(fetch({ id }));
|
||||||
|
}
|
||||||
|
}, [dispatch, id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (containers && !Array.isArray(containers)) {
|
||||||
|
setInitialValues({ name: containers.name || '' });
|
||||||
|
}
|
||||||
|
}, [containers]);
|
||||||
|
|
||||||
|
const handleSubmit = async (values: any) => {
|
||||||
|
await dispatch(update({ id, data: values }));
|
||||||
|
router.push('/containers/containers-list');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Edit Container')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Edit Container" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<Formik initialValues={initialValues} onSubmit={handleSubmit} enableReinitialize>
|
||||||
|
<Form>
|
||||||
|
<FormField label="Name" help="Please enter the name">
|
||||||
|
<Field name="name" placeholder="Name" className="w-full h-10 px-3 border rounded-md" required />
|
||||||
|
</FormField>
|
||||||
|
<BaseButtons>
|
||||||
|
<BaseButton type="submit" color="info" label="Submit" />
|
||||||
|
<BaseButton type="button" color="info" outline label="Cancel" onClick={() => router.push('/containers/containers-list')} />
|
||||||
|
</BaseButtons>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainersEdit.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'UPDATE_CONTAINERS'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContainersEdit
|
||||||
44
frontend/src/pages/containers/containers-list.tsx
Normal file
44
frontend/src/pages/containers/containers-list.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import TableContainers from '../../components/Containers/TableContainers'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import { useAppSelector } from "../../stores/hooks";
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
|
||||||
|
const ContainersListPage = () => {
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const hasCreatePermission = currentUser && hasPermission(currentUser, 'CREATE_CONTAINERS');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Containers')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Containers" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<div className="mb-6 flex flex-wrap gap-3">
|
||||||
|
{hasCreatePermission && <BaseButton href={'/containers/containers-new'} color='info' label='New Item'/>}
|
||||||
|
<div id='delete-rows-button'></div>
|
||||||
|
</div>
|
||||||
|
<TableContainers filterItems={[]} setFilterItems={() => undefined} filters={[]} showGrid={false} />
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainersListPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return (
|
||||||
|
<LayoutAuthenticated permission={'READ_CONTAINERS'}>
|
||||||
|
{page}
|
||||||
|
</LayoutAuthenticated>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContainersListPage
|
||||||
59
frontend/src/pages/containers/containers-new.tsx
Normal file
59
frontend/src/pages/containers/containers-new.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Formik, Form, Field } from 'formik'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import FormField from '../../components/FormField'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import BaseButtons from '../../components/BaseButtons'
|
||||||
|
import { useAppDispatch } from "../../stores/hooks"
|
||||||
|
import { create } from '../../stores/containers/containersSlice'
|
||||||
|
|
||||||
|
const ContainersNew = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const initialValues = { name: '' };
|
||||||
|
|
||||||
|
const handleSubmit = async (values: any) => {
|
||||||
|
await dispatch(create(values));
|
||||||
|
router.push('/containers/containers-list');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('New Container')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="New Container" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||||
|
<Form>
|
||||||
|
<FormField label="Name" help="Please enter the name">
|
||||||
|
<Field name="name" placeholder="Name" className="w-full h-10 px-3 border rounded-md" required />
|
||||||
|
</FormField>
|
||||||
|
<BaseButtons>
|
||||||
|
<BaseButton type="submit" color="info" label="Submit" />
|
||||||
|
<BaseButton type="button" color="info" outline label="Cancel" onClick={() => router.push('/containers/containers-list')} />
|
||||||
|
</BaseButtons>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainersNew.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'CREATE_CONTAINERS'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContainersNew
|
||||||
37
frontend/src/pages/containers/containers-view.tsx
Normal file
37
frontend/src/pages/containers/containers-view.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../stores/hooks"
|
||||||
|
import { fetch } from '../../stores/containers/containersSlice'
|
||||||
|
|
||||||
|
const ContainersView = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
const { id } = router.query;
|
||||||
|
const { containers } = useAppSelector(state => state.containers);
|
||||||
|
useEffect(() => { if (id) dispatch(fetch({ id })); }, [dispatch, id]);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head><title>{getPageTitle('View Container')}</title></Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="View Container" main>{''}</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<div className="mb-4"><label className="font-bold">Name:</label><p>{containers?.name || 'N/A'}</p></div>
|
||||||
|
<BaseButton type="button" color="info" outline label="Back" onClick={() => router.push('/containers/containers-list')} />
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ContainersView.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'READ_CONTAINERS'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
export default ContainersView
|
||||||
72
frontend/src/pages/crop_types/crop_types-edit.tsx
Normal file
72
frontend/src/pages/crop_types/crop_types-edit.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Formik, Form, Field } from 'formik'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import FormField from '../../components/FormField'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import BaseButtons from '../../components/BaseButtons'
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../stores/hooks"
|
||||||
|
import { update, fetch } from '../../stores/crop_types/crop_typesSlice'
|
||||||
|
|
||||||
|
const Crop_typesEdit = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
const { id } = router.query;
|
||||||
|
const { crop_types } = useAppSelector(state => state.crop_types);
|
||||||
|
const [initialValues, setInitialValues] = useState({ name: '' });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
dispatch(fetch({ id }));
|
||||||
|
}
|
||||||
|
}, [dispatch, id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (crop_types && !Array.isArray(crop_types)) {
|
||||||
|
setInitialValues({ name: crop_types.name || '' });
|
||||||
|
}
|
||||||
|
}, [crop_types]);
|
||||||
|
|
||||||
|
const handleSubmit = async (values: any) => {
|
||||||
|
await dispatch(update({ id, data: values }));
|
||||||
|
router.push('/crop_types/crop_types-list');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Edit Crop Type')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Edit Crop Type" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<Formik initialValues={initialValues} onSubmit={handleSubmit} enableReinitialize>
|
||||||
|
<Form>
|
||||||
|
<FormField label="Name" help="Please enter the name">
|
||||||
|
<Field name="name" placeholder="Name" className="w-full h-10 px-3 border rounded-md" required />
|
||||||
|
</FormField>
|
||||||
|
<BaseButtons>
|
||||||
|
<BaseButton type="submit" color="info" label="Submit" />
|
||||||
|
<BaseButton type="button" color="info" outline label="Cancel" onClick={() => router.push('/crop_types/crop_types-list')} />
|
||||||
|
</BaseButtons>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Crop_typesEdit.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'UPDATE_CROP_TYPES'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Crop_typesEdit
|
||||||
44
frontend/src/pages/crop_types/crop_types-list.tsx
Normal file
44
frontend/src/pages/crop_types/crop_types-list.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import TableCrop_types from '../../components/Crop_types/TableCrop_types'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import { useAppSelector } from "../../stores/hooks";
|
||||||
|
import { hasPermission } from "../../helpers/userPermissions";
|
||||||
|
|
||||||
|
const Crop_typesListPage = () => {
|
||||||
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const hasCreatePermission = currentUser && hasPermission(currentUser, 'CREATE_CROP_TYPES');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Crop Types')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Crop Types" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<div className="mb-6 flex flex-wrap gap-3">
|
||||||
|
{hasCreatePermission && <BaseButton href={'/crop_types/crop_types-new'} color='info' label='New Item'/>}
|
||||||
|
<div id='delete-rows-button'></div>
|
||||||
|
</div>
|
||||||
|
<TableCrop_types filterItems={[]} setFilterItems={() => undefined} filters={[]} showGrid={false} />
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Crop_typesListPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return (
|
||||||
|
<LayoutAuthenticated permission={'READ_CROP_TYPES'}>
|
||||||
|
{page}
|
||||||
|
</LayoutAuthenticated>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Crop_typesListPage
|
||||||
59
frontend/src/pages/crop_types/crop_types-new.tsx
Normal file
59
frontend/src/pages/crop_types/crop_types-new.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Formik, Form, Field } from 'formik'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import FormField from '../../components/FormField'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import BaseButtons from '../../components/BaseButtons'
|
||||||
|
import { useAppDispatch } from "../../stores/hooks"
|
||||||
|
import { create } from '../../stores/crop_types/crop_typesSlice'
|
||||||
|
|
||||||
|
const Crop_typesNew = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const initialValues = { name: '' };
|
||||||
|
|
||||||
|
const handleSubmit = async (values: any) => {
|
||||||
|
await dispatch(create(values));
|
||||||
|
router.push('/crop_types/crop_types-list');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('New Crop Type')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="New Crop Type" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||||
|
<Form>
|
||||||
|
<FormField label="Name" help="Please enter the name">
|
||||||
|
<Field name="name" placeholder="Name" className="w-full h-10 px-3 border rounded-md" required />
|
||||||
|
</FormField>
|
||||||
|
<BaseButtons>
|
||||||
|
<BaseButton type="submit" color="info" label="Submit" />
|
||||||
|
<BaseButton type="button" color="info" outline label="Cancel" onClick={() => router.push('/crop_types/crop_types-list')} />
|
||||||
|
</BaseButtons>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Crop_typesNew.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'CREATE_CROP_TYPES'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Crop_typesNew
|
||||||
37
frontend/src/pages/crop_types/crop_types-view.tsx
Normal file
37
frontend/src/pages/crop_types/crop_types-view.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../stores/hooks"
|
||||||
|
import { fetch } from '../../stores/crop_types/crop_typesSlice'
|
||||||
|
|
||||||
|
const Crop_typesView = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
const { id } = router.query;
|
||||||
|
const { crop_types } = useAppSelector(state => state.crop_types);
|
||||||
|
useEffect(() => { if (id) dispatch(fetch({ id })); }, [dispatch, id]);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head><title>{getPageTitle('View Crop Type')}</title></Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="View Crop Type" main>{''}</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<div className="mb-4"><label className="font-bold">Name:</label><p>{crop_types?.name || 'N/A'}</p></div>
|
||||||
|
<BaseButton type="button" color="info" outline label="Back" onClick={() => router.push('/crop_types/crop_types-list')} />
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Crop_typesView.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'READ_CROP_TYPES'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
export default Crop_typesView
|
||||||
@ -9,13 +9,18 @@ import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton
|
|||||||
import BaseIcon from "../components/BaseIcon";
|
import BaseIcon from "../components/BaseIcon";
|
||||||
import { getPageTitle } from '../config'
|
import { getPageTitle } from '../config'
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
import { hasPermission } from "../helpers/userPermissions";
|
import { hasPermission } from "../helpers/userPermissions";
|
||||||
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
||||||
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
||||||
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
||||||
|
import { fetch as fetchActivities } from '../stores/tracking_activities/tracking_activitiesSlice';
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||||
|
import CardBox from '../components/CardBox';
|
||||||
|
import UserAvatar from '../components/UserAvatar';
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const iconsColor = useAppSelector((state) => state.style.iconsColor);
|
const iconsColor = useAppSelector((state) => state.style.iconsColor);
|
||||||
@ -24,534 +29,214 @@ const Dashboard = () => {
|
|||||||
|
|
||||||
const loadingMessage = 'Loading...';
|
const loadingMessage = 'Loading...';
|
||||||
|
|
||||||
|
|
||||||
const [users, setUsers] = React.useState(loadingMessage);
|
const [users, setUsers] = React.useState(loadingMessage);
|
||||||
const [roles, setRoles] = React.useState(loadingMessage);
|
const [projectsCount, setProjectsCount] = React.useState(loadingMessage);
|
||||||
const [permissions, setPermissions] = React.useState(loadingMessage);
|
const [trialsCount, setTrialsCount] = React.useState(loadingMessage);
|
||||||
const [organizations, setOrganizations] = React.useState(loadingMessage);
|
const [activitiesCount, setActivitiesCount] = React.useState(loadingMessage);
|
||||||
const [tenants, setTenants] = React.useState(loadingMessage);
|
|
||||||
const [impersonation_sessions, setImpersonation_sessions] = React.useState(loadingMessage);
|
|
||||||
const [projects, setProjects] = React.useState(loadingMessage);
|
|
||||||
const [trials, setTrials] = React.useState(loadingMessage);
|
|
||||||
const [tracking_activity_types, setTracking_activity_types] = React.useState(loadingMessage);
|
|
||||||
const [tracking_activities, setTracking_activities] = React.useState(loadingMessage);
|
|
||||||
const [dashboards, setDashboards] = React.useState(loadingMessage);
|
|
||||||
const [report_definitions, setReport_definitions] = React.useState(loadingMessage);
|
|
||||||
const [report_runs, setReport_runs] = React.useState(loadingMessage);
|
|
||||||
const [tenant_settings, setTenant_settings] = React.useState(loadingMessage);
|
|
||||||
|
|
||||||
|
|
||||||
const [widgetsRole, setWidgetsRole] = React.useState({
|
|
||||||
role: { value: '', label: '' },
|
|
||||||
});
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
const { isFetchingQuery } = useAppSelector((state) => state.openAi);
|
const { isFetchingQuery } = useAppSelector((state) => state.openAi);
|
||||||
|
const { rolesWidgets, loading: widgetsLoading } = useAppSelector((state) => state.roles);
|
||||||
|
const { tracking_activities, loading: activitiesLoading } = useAppSelector((state) => state.tracking_activities);
|
||||||
|
|
||||||
const { rolesWidgets, loading } = useAppSelector((state) => state.roles);
|
async function loadCounts() {
|
||||||
|
const entities = ['users', 'projects', 'trials', 'tracking_activities'];
|
||||||
|
const setters = [setUsers, setProjectsCount, setTrialsCount, setActivitiesCount];
|
||||||
const organizationId = currentUser?.organizations?.id;
|
|
||||||
|
|
||||||
async function loadData() {
|
|
||||||
const entities = ['users','roles','permissions','organizations','tenants','impersonation_sessions','projects','trials','tracking_activity_types','tracking_activities','dashboards','report_definitions','report_runs','tenant_settings',];
|
|
||||||
const fns = [setUsers,setRoles,setPermissions,setOrganizations,setTenants,setImpersonation_sessions,setProjects,setTrials,setTracking_activity_types,setTracking_activities,setDashboards,setReport_definitions,setReport_runs,setTenant_settings,];
|
|
||||||
|
|
||||||
const requests = entities.map((entity, index) => {
|
const requests = entities.map((entity, index) => {
|
||||||
|
if (hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) {
|
||||||
if(hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) {
|
return axios.get(`/${entity.toLowerCase()}/count`);
|
||||||
return axios.get(`/${entity.toLowerCase()}/count`);
|
} else {
|
||||||
} else {
|
setters[index](null);
|
||||||
fns[index](null);
|
return Promise.resolve({ data: { count: null } });
|
||||||
return Promise.resolve({data: {count: null}});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Promise.allSettled(requests).then((results) => {
|
Promise.allSettled(requests).then((results) => {
|
||||||
results.forEach((result, i) => {
|
results.forEach((result, i) => {
|
||||||
if (result.status === 'fulfilled') {
|
if (result.status === 'fulfilled') {
|
||||||
fns[i](result.value.data.count);
|
setters[i](result.value.data.count);
|
||||||
} else {
|
} else {
|
||||||
fns[i](result.reason.message);
|
setters[i](result.reason.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getWidgets(roleId) {
|
|
||||||
await dispatch(fetchWidgets(roleId));
|
|
||||||
}
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
loadData().then();
|
loadCounts().then();
|
||||||
setWidgetsRole({ role: { value: currentUser?.app_role?.id, label: currentUser?.app_role?.name } });
|
if (hasPermission(currentUser, 'READ_TRACKING_ACTIVITIES')) {
|
||||||
}, [currentUser]);
|
dispatch(fetchActivities({ query: '?limit=10' }));
|
||||||
|
}
|
||||||
|
dispatch(fetchWidgets(currentUser?.app_role?.id || '')).then();
|
||||||
|
}, [currentUser, dispatch]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
const getSeverityColor = (severity: string) => {
|
||||||
if (!currentUser || !widgetsRole?.role?.value) return;
|
switch (severity) {
|
||||||
getWidgets(widgetsRole?.role?.value || '').then();
|
case 'critical': return 'text-red-600';
|
||||||
}, [widgetsRole?.role?.value]);
|
case 'high': return 'text-orange-600';
|
||||||
|
case 'medium': return 'text-yellow-600';
|
||||||
|
case 'low': return 'text-blue-600';
|
||||||
|
default: return 'text-gray-600';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>
|
<title>{getPageTitle('Dashboard')}</title>
|
||||||
{getPageTitle('Overview')}
|
</Head>
|
||||||
</title>
|
<SectionMain>
|
||||||
</Head>
|
<SectionTitleLineWithButton
|
||||||
<SectionMain>
|
icon={icon.mdiViewDashboardOutline}
|
||||||
<SectionTitleLineWithButton
|
title='Trial Tracker Dashboard'
|
||||||
icon={icon.mdiChartTimelineVariant}
|
main
|
||||||
title='Overview'
|
>
|
||||||
main>
|
{''}
|
||||||
{''}
|
</SectionTitleLineWithButton>
|
||||||
</SectionTitleLineWithButton>
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'CREATE_ROLES') && <WidgetCreator
|
{/* Quick Stats */}
|
||||||
currentUser={currentUser}
|
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-6'>
|
||||||
isFetchingQuery={isFetchingQuery}
|
{hasPermission(currentUser, 'READ_PROJECTS') && (
|
||||||
setWidgetsRole={setWidgetsRole}
|
<CardBox className={cardsStyle}>
|
||||||
widgetsRole={widgetsRole}
|
<div className="flex justify-between items-center">
|
||||||
/>}
|
<div>
|
||||||
{!!rolesWidgets.length &&
|
<p className="text-gray-500 dark:text-gray-400">Projects</p>
|
||||||
hasPermission(currentUser, 'CREATE_ROLES') && (
|
<h3 className="text-2xl font-bold">{projectsCount}</h3>
|
||||||
<p className=' text-gray-500 dark:text-gray-400 mb-4'>
|
</div>
|
||||||
{`${widgetsRole?.role?.label || 'Users'}'s widgets`}
|
<BaseIcon path={icon.mdiBriefcaseOutline} size={32} className={iconsColor} />
|
||||||
</p>
|
</div>
|
||||||
)}
|
</CardBox>
|
||||||
|
)}
|
||||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-6 grid-flow-dense'>
|
{hasPermission(currentUser, 'READ_TRIALS') && (
|
||||||
{(isFetchingQuery || loading) && (
|
<CardBox className={cardsStyle}>
|
||||||
<div className={` ${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 text-lg leading-tight text-gray-500 flex items-center ${cardsStyle} dark:border-dark-700 p-6`}>
|
<div className="flex justify-between items-center">
|
||||||
<BaseIcon
|
<div>
|
||||||
className={`${iconsColor} animate-spin mr-5`}
|
<p className="text-gray-500 dark:text-gray-400">Active Trials</p>
|
||||||
w='w-16'
|
<h3 className="text-2xl font-bold">{trialsCount}</h3>
|
||||||
h='h-16'
|
</div>
|
||||||
size={48}
|
<BaseIcon path={icon.mdiSprout} size={32} className={iconsColor} />
|
||||||
path={icon.mdiLoading}
|
</div>
|
||||||
/>{' '}
|
</CardBox>
|
||||||
Loading widgets...
|
)}
|
||||||
|
{hasPermission(currentUser, 'READ_TRACKING_ACTIVITIES') && (
|
||||||
|
<CardBox className={cardsStyle}>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<p className="text-gray-500 dark:text-gray-400">Total Activities</p>
|
||||||
|
<h3 className="text-2xl font-bold">{activitiesCount}</h3>
|
||||||
|
</div>
|
||||||
|
<BaseIcon path={icon.mdiTimelineTextOutline} size={32} className={iconsColor} />
|
||||||
|
</div>
|
||||||
|
</CardBox>
|
||||||
|
)}
|
||||||
|
{hasPermission(currentUser, 'READ_USERS') && (
|
||||||
|
<CardBox className={cardsStyle}>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<p className="text-gray-500 dark:text-gray-400">Team Members</p>
|
||||||
|
<h3 className="text-2xl font-bold">{users}</h3>
|
||||||
|
</div>
|
||||||
|
<BaseIcon path={icon.mdiAccountGroup} size={32} className={iconsColor} />
|
||||||
|
</div>
|
||||||
|
</CardBox>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{ rolesWidgets &&
|
{hasPermission(currentUser, 'CREATE_ROLES') && (
|
||||||
rolesWidgets.map((widget) => (
|
<WidgetCreator
|
||||||
<SmartWidget
|
currentUser={currentUser}
|
||||||
key={widget.id}
|
isFetchingQuery={isFetchingQuery}
|
||||||
userId={currentUser?.id}
|
setWidgetsRole={() => {
|
||||||
widget={widget}
|
// No-op for dashboard feed view
|
||||||
roleId={widgetsRole?.role?.value || ''}
|
}}
|
||||||
admin={hasPermission(currentUser, 'CREATE_ROLES')}
|
widgetsRole={{ role: { value: currentUser?.app_role?.id, label: currentUser?.app_role?.name } }}
|
||||||
/>
|
/>
|
||||||
))}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
{!!rolesWidgets.length && <hr className='my-6 ' />}
|
<div className='grid grid-cols-1 gap-6 lg:grid-cols-3'>
|
||||||
|
{/* Activity Feed */}
|
||||||
|
<div className="lg:col-span-2 space-y-4">
|
||||||
|
<SectionTitleLineWithButton icon={icon.mdiHistory} title="Recent Activities" />
|
||||||
|
|
||||||
<div id="dashboard" className='grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6'>
|
{activitiesLoading ? (
|
||||||
|
<CardBox className="flex items-center justify-center p-12">
|
||||||
|
<BaseIcon path={icon.mdiLoading} size={32} className="animate-spin" />
|
||||||
|
<span className="ml-3">Loading activities...</span>
|
||||||
|
</CardBox>
|
||||||
|
) : tracking_activities && tracking_activities.length > 0 ? (
|
||||||
|
tracking_activities.map((activity: any) => (
|
||||||
|
<CardBox key={activity.id} className={`${cardsStyle} hover:shadow-md transition-shadow`}>
|
||||||
|
<div className="flex items-start space-x-4">
|
||||||
|
<UserAvatar username={activity.recorded_by?.firstName || 'Staff'} className="w-10 h-10" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex justify-between items-start">
|
||||||
|
<div>
|
||||||
|
<span className="font-bold">{activity.recorded_by?.firstName} {activity.recorded_by?.lastName}</span>
|
||||||
|
<span className="text-gray-500 ml-2">logged an activity for</span>
|
||||||
|
<Link href={`/trials/trials-edit?id=${activity.trial?.id}`} className="ml-1 text-green-600 hover:underline font-semibold">
|
||||||
|
{activity.trial?.name}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-400">
|
||||||
|
{moment(activity.occurred_at || activity.createdAt).fromNow()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span className="text-sm font-medium bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded">
|
||||||
|
{activity.activity_type?.name}
|
||||||
|
</span>
|
||||||
|
{activity.severity && activity.severity !== 'none' && (
|
||||||
|
<span className={`text-xs font-bold uppercase ${getSeverityColor(activity.severity)}`}>
|
||||||
|
{activity.severity}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="mt-2 text-gray-700 dark:text-gray-300 font-medium">
|
||||||
|
{activity.summary}
|
||||||
|
</p>
|
||||||
|
{activity.details && (
|
||||||
|
<p className="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
{activity.details}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardBox>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<CardBox className="p-12 text-center text-gray-500">
|
||||||
|
No recent activities found. Start tracking to see them here!
|
||||||
|
</CardBox>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right Column: Widgets or Quick Info */}
|
||||||
{hasPermission(currentUser, 'READ_USERS') && <Link href={'/users/users-list'}>
|
<div className="space-y-6">
|
||||||
<div
|
<SectionTitleLineWithButton icon={icon.mdiChartPie} title="Distribution" />
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
<div className='grid grid-cols-1 gap-6'>
|
||||||
>
|
{rolesWidgets && rolesWidgets.map((widget) => (
|
||||||
<div className="flex justify-between align-center">
|
<SmartWidget
|
||||||
<div>
|
key={widget.id}
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
userId={currentUser?.id}
|
||||||
Users
|
widget={widget}
|
||||||
</div>
|
roleId={currentUser?.app_role?.id || ''}
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
admin={hasPermission(currentUser, 'CREATE_ROLES')}
|
||||||
{users}
|
/>
|
||||||
</div>
|
))}
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={icon.mdiAccountGroup || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>}
|
</SectionMain>
|
||||||
|
</>
|
||||||
{hasPermission(currentUser, 'READ_ROLES') && <Link href={'/roles/roles-list'}>
|
)
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Roles
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{roles}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={icon.mdiShieldAccountVariantOutline || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_PERMISSIONS') && <Link href={'/permissions/permissions-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Permissions
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{permissions}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={icon.mdiShieldAccountOutline || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_ORGANIZATIONS') && <Link href={'/organizations/organizations-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Organizations
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{organizations}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_TENANTS') && <Link href={'/tenants/tenants-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Tenants
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{tenants}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiDomain' in icon ? icon['mdiDomain' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_IMPERSONATION_SESSIONS') && <Link href={'/impersonation_sessions/impersonation_sessions-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Impersonation sessions
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{impersonation_sessions}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiAccountSwitch' in icon ? icon['mdiAccountSwitch' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_PROJECTS') && <Link href={'/projects/projects-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Projects
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{projects}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiBriefcaseOutline' in icon ? icon['mdiBriefcaseOutline' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_TRIALS') && <Link href={'/trials/trials-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Trials
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{trials}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiSprout' in icon ? icon['mdiSprout' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_TRACKING_ACTIVITY_TYPES') && <Link href={'/tracking_activity_types/tracking_activity_types-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Tracking activity types
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{tracking_activity_types}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiFormatListBulletedType' in icon ? icon['mdiFormatListBulletedType' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_TRACKING_ACTIVITIES') && <Link href={'/tracking_activities/tracking_activities-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Tracking activities
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{tracking_activities}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiTimelineTextOutline' in icon ? icon['mdiTimelineTextOutline' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_DASHBOARDS') && <Link href={'/dashboards/dashboards-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Dashboards
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{dashboards}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiViewDashboardOutline' in icon ? icon['mdiViewDashboardOutline' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_REPORT_DEFINITIONS') && <Link href={'/report_definitions/report_definitions-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Report definitions
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{report_definitions}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiFileChartOutline' in icon ? icon['mdiFileChartOutline' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_REPORT_RUNS') && <Link href={'/report_runs/report_runs-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Report runs
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{report_runs}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiProgressDownload' in icon ? icon['mdiProgressDownload' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
{hasPermission(currentUser, 'READ_TENANT_SETTINGS') && <Link href={'/tenant_settings/tenant_settings-list'}>
|
|
||||||
<div
|
|
||||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
|
||||||
>
|
|
||||||
<div className="flex justify-between align-center">
|
|
||||||
<div>
|
|
||||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
|
||||||
Tenant settings
|
|
||||||
</div>
|
|
||||||
<div className="text-3xl leading-tight font-semibold">
|
|
||||||
{tenant_settings}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w="w-16"
|
|
||||||
h="h-16"
|
|
||||||
size={48}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
path={'mdiCogOutline' in icon ? icon['mdiCogOutline' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</SectionMain>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dashboard.getLayout = function getLayout(page: ReactElement) {
|
Dashboard.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
|
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Dashboard
|
export default Dashboard
|
||||||
|
|||||||
72
frontend/src/pages/groups/groups-edit.tsx
Normal file
72
frontend/src/pages/groups/groups-edit.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { mdiChartTimelineVariant } from '@mdi/js'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Formik, Form, Field } from 'formik'
|
||||||
|
import CardBox from '../../components/CardBox'
|
||||||
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
||||||
|
import SectionMain from '../../components/SectionMain'
|
||||||
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
||||||
|
import { getPageTitle } from '../../config'
|
||||||
|
import FormField from '../../components/FormField'
|
||||||
|
import BaseButton from '../../components/BaseButton'
|
||||||
|
import BaseButtons from '../../components/BaseButtons'
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../stores/hooks"
|
||||||
|
import { update, fetch } from '../../stores/groups/groupsSlice'
|
||||||
|
|
||||||
|
const GroupsEdit = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
const { id } = router.query;
|
||||||
|
const { groups } = useAppSelector(state => state.groups);
|
||||||
|
const [initialValues, setInitialValues] = useState({ name: '' });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
dispatch(fetch({ id }));
|
||||||
|
}
|
||||||
|
}, [dispatch, id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (groups && !Array.isArray(groups)) {
|
||||||
|
setInitialValues({ name: groups.name || '' });
|
||||||
|
}
|
||||||
|
}, [groups]);
|
||||||
|
|
||||||
|
const handleSubmit = async (values: any) => {
|
||||||
|
await dispatch(update({ id, data: values }));
|
||||||
|
router.push('/groups/groups-list');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Edit Propagation Type')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Edit Propagation Type" main>
|
||||||
|
{''}
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
<CardBox>
|
||||||
|
<Formik initialValues={initialValues} onSubmit={handleSubmit} enableReinitialize>
|
||||||
|
<Form>
|
||||||
|
<FormField label="Name" help="Please enter the name">
|
||||||
|
<Field name="name" placeholder="Name" className="w-full h-10 px-3 border rounded-md" required />
|
||||||
|
</FormField>
|
||||||
|
<BaseButtons>
|
||||||
|
<BaseButton type="submit" color="info" label="Submit" />
|
||||||
|
<BaseButton type="button" color="info" outline label="Cancel" onClick={() => router.push('/groups/groups-list')} />
|
||||||
|
</BaseButtons>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBox>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupsEdit.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated permission={'UPDATE_PROPAGATION_TYPES'}>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GroupsEdit
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user