Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 = {
|
||||
production: {
|
||||
@ -12,11 +12,12 @@ module.exports = {
|
||||
seederStorage: 'sequelize',
|
||||
},
|
||||
development: {
|
||||
username: 'postgres',
|
||||
dialect: 'postgres',
|
||||
password: '',
|
||||
database: 'db_trial_tracker',
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
username: process.env.DB_USER,
|
||||
password: process.env.DB_PASS,
|
||||
database: process.env.DB_NAME,
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
logging: console.log,
|
||||
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 providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const trials = sequelize.define(
|
||||
@ -13,165 +9,122 @@ module.exports = function(sequelize, DataTypes) {
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true,
|
||||
},
|
||||
|
||||
name: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
trial_code: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
variety_name: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
breeder: {
|
||||
genus: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
species: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
colour: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
batch_code: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
trial_type: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
|
||||
|
||||
values: [
|
||||
|
||||
"new_variety",
|
||||
|
||||
|
||||
"performance",
|
||||
|
||||
|
||||
"disease_resistance",
|
||||
|
||||
|
||||
"yield",
|
||||
|
||||
|
||||
"quality",
|
||||
|
||||
|
||||
"other"
|
||||
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
status: {
|
||||
type: DataTypes.ENUM,
|
||||
|
||||
|
||||
|
||||
values: [
|
||||
|
||||
"setup",
|
||||
|
||||
|
||||
"in_progress",
|
||||
|
||||
|
||||
"paused",
|
||||
|
||||
|
||||
"completed",
|
||||
|
||||
|
||||
"archived"
|
||||
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
planted_at: {
|
||||
type: DataTypes.DATE,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
harvested_at: {
|
||||
type: DataTypes.DATE,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
greenhouse: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
zone: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
bench: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
plants_count: {
|
||||
type: DataTypes.INTEGER,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
target_temperature_c: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
target_humidity_percent: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
target_ec: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
target_ph: {
|
||||
type: DataTypes.DECIMAL,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
notes: {
|
||||
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: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
@ -186,19 +139,66 @@ notes: {
|
||||
);
|
||||
|
||||
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, {
|
||||
as: 'tracking_activities_trial',
|
||||
@ -208,16 +208,6 @@ notes: {
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//end loop
|
||||
|
||||
|
||||
|
||||
db.trials.belongsTo(db.tenants, {
|
||||
as: 'tenant',
|
||||
foreignKey: {
|
||||
@ -242,9 +232,6 @@ notes: {
|
||||
constraints: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
db.trials.belongsTo(db.users, {
|
||||
as: 'createdBy',
|
||||
});
|
||||
@ -254,9 +241,5 @@ notes: {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
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 cors = require('cors');
|
||||
const app = express();
|
||||
@ -16,41 +15,36 @@ const fileRoutes = require('./routes/file');
|
||||
const searchRoutes = require('./routes/search');
|
||||
const sqlRoutes = require('./routes/sql');
|
||||
const pexelsRoutes = require('./routes/pexels');
|
||||
|
||||
const organizationForAuthRoutes = require('./routes/organizationLogin');
|
||||
|
||||
const openaiRoutes = require('./routes/openai');
|
||||
|
||||
|
||||
|
||||
const usersRoutes = require('./routes/users');
|
||||
|
||||
const rolesRoutes = require('./routes/roles');
|
||||
|
||||
const permissionsRoutes = require('./routes/permissions');
|
||||
|
||||
const organizationsRoutes = require('./routes/organizations');
|
||||
|
||||
const tenantsRoutes = require('./routes/tenants');
|
||||
|
||||
const impersonation_sessionsRoutes = require('./routes/impersonation_sessions');
|
||||
|
||||
const projectsRoutes = require('./routes/projects');
|
||||
|
||||
const trialsRoutes = require('./routes/trials');
|
||||
|
||||
const tracking_activity_typesRoutes = require('./routes/tracking_activity_types');
|
||||
|
||||
const tracking_activitiesRoutes = require('./routes/tracking_activities');
|
||||
|
||||
const dashboardsRoutes = require('./routes/dashboards');
|
||||
|
||||
const report_definitionsRoutes = require('./routes/report_definitions');
|
||||
|
||||
const report_runsRoutes = require('./routes/report_runs');
|
||||
|
||||
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) => {
|
||||
if (!url) return '';
|
||||
@ -110,33 +104,32 @@ app.enable('trust proxy');
|
||||
|
||||
|
||||
app.use('/api/users', passport.authenticate('jwt', {session: false}), usersRoutes);
|
||||
|
||||
app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoutes);
|
||||
|
||||
app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes);
|
||||
|
||||
app.use('/api/organizations', passport.authenticate('jwt', {session: false}), organizationsRoutes);
|
||||
|
||||
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/projects', passport.authenticate('jwt', {session: false}), projectsRoutes);
|
||||
|
||||
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_activities', passport.authenticate('jwt', {session: false}), tracking_activitiesRoutes);
|
||||
|
||||
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_runs', passport.authenticate('jwt', {session: false}), report_runsRoutes);
|
||||
|
||||
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(
|
||||
'/api/openai',
|
||||
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 AsideMenuList from './AsideMenuList'
|
||||
import { MenuAsideItem } from '../interfaces'
|
||||
import { useAppSelector } from '../stores/hooks'
|
||||
import { useAppSelector, useAppDispatch } from '../stores/hooks'
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useAppDispatch } from '../stores/hooks';
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
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 = {
|
||||
menu: MenuNavBarItem[]
|
||||
leftMenu?: MenuNavBarItem[]
|
||||
className: string
|
||||
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 [isScrolled, setIsScrolled] = useState(false);
|
||||
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`}
|
||||
>
|
||||
<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">
|
||||
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
||||
<BaseIcon path={isMenuNavBarActive ? mdiClose : mdiDotsVertical} size="24" />
|
||||
@ -49,6 +55,9 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
||||
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`}
|
||||
>
|
||||
<div className="lg:hidden">
|
||||
<NavBarMenuList menu={leftMenu} />
|
||||
</div>
|
||||
<NavBarMenuList menu={menu} />
|
||||
</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 { useState } from 'react'
|
||||
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
||||
import BaseDivider from './BaseDivider'
|
||||
import BaseIcon from './BaseIcon'
|
||||
@ -39,17 +38,21 @@ export default function NavBarItem({ item }: Props) {
|
||||
}, [router.pathname]);
|
||||
|
||||
const componentClass = [
|
||||
'block lg:flex items-center relative cursor-pointer',
|
||||
'block lg:flex items-center relative',
|
||||
item.isHeader ? 'cursor-default' : 'cursor-pointer',
|
||||
isDropdownActive
|
||||
? `${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.isDesktopNoLabel ? 'lg:w-16 lg:justify-center' : '',
|
||||
item.isHeader ? 'text-xs uppercase font-bold text-gray-400 dark:text-slate-500 mt-2' : ''
|
||||
].join(' ')
|
||||
|
||||
const itemLabel = item.isCurrentUser ? userName : item.label
|
||||
|
||||
const handleMenuClick = () => {
|
||||
if (item.isHeader) return
|
||||
|
||||
if (item.menu) {
|
||||
setIsDropdownActive(!isDropdownActive)
|
||||
}
|
||||
@ -86,9 +89,9 @@ export default function NavBarItem({ item }: Props) {
|
||||
}`}
|
||||
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
|
||||
className={`px-2 transition-colors w-40 grow ${
|
||||
className={`px-2 transition-colors ${
|
||||
item.isDesktopNoLabel && item.icon ? 'lg:hidden' : ''
|
||||
}`}
|
||||
>
|
||||
@ -120,7 +123,7 @@ export default function NavBarItem({ item }: Props) {
|
||||
return <BaseDivider navBar />
|
||||
}
|
||||
|
||||
if (item.href) {
|
||||
if (item.href && !item.isHeader) {
|
||||
return (
|
||||
<Link href={item.href} target={item.target} className={componentClass}>
|
||||
{NavBarItemComponentContents}
|
||||
|
||||
@ -1,19 +1,29 @@
|
||||
import React from 'react'
|
||||
import { MenuNavBarItem } from '../interfaces'
|
||||
import NavBarItem from './NavBarItem'
|
||||
import { useAppSelector } from '../stores/hooks'
|
||||
import { hasPermission } from '../helpers/userPermissions'
|
||||
|
||||
type Props = {
|
||||
menu: MenuNavBarItem[]
|
||||
}
|
||||
|
||||
export default function NavBarMenuList({ menu }: Props) {
|
||||
const { currentUser } = useAppSelector((state) => state.auth)
|
||||
|
||||
if (!currentUser) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
{menu.map((item, index) => (
|
||||
{menu.map((item, index) => {
|
||||
if (!hasPermission(currentUser, item.permissions)) return null
|
||||
|
||||
return (
|
||||
<div key={index}>
|
||||
<NavBarItem item={item} />
|
||||
</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 {
|
||||
DataGrid,
|
||||
GridColDef,
|
||||
GridToolbar,
|
||||
} from '@mui/x-data-grid';
|
||||
import {loadColumns} from "./configureTrialsCols";
|
||||
import _ from 'lodash';
|
||||
@ -233,6 +234,9 @@ const TableSampleTrials = ({ filterItems, setFilterItems, filters, showGrid }) =
|
||||
},
|
||||
},
|
||||
}}
|
||||
slots={{
|
||||
toolbar: GridToolbar,
|
||||
}}
|
||||
disableRowSelectionOnClick
|
||||
onProcessRowUpdateError={(params) => {
|
||||
console.log('Error', params);
|
||||
|
||||
@ -1,18 +1,11 @@
|
||||
import React from 'react';
|
||||
import BaseIcon from '../BaseIcon';
|
||||
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
GridActionsCellItem,
|
||||
GridRowParams,
|
||||
GridValueGetterParams,
|
||||
} from '@mui/x-data-grid';
|
||||
import ImageField from '../ImageField';
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter'
|
||||
import DataGridMultiSelect from "../DataGridMultiSelect";
|
||||
import ListActionsPopover from '../ListActionsPopover';
|
||||
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
type Params = (id: string) => void;
|
||||
@ -20,14 +13,10 @@ type Params = (id: string) => void;
|
||||
export const loadColumns = async (
|
||||
onDelete: Params,
|
||||
entityName: string,
|
||||
|
||||
user
|
||||
|
||||
) => {
|
||||
async function callOptionsApi(entityName: string) {
|
||||
|
||||
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
||||
|
||||
try {
|
||||
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
||||
return data.data;
|
||||
@ -38,327 +27,146 @@ export const loadColumns = async (
|
||||
}
|
||||
|
||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_TRIALS')
|
||||
const weeksOptions = Array.from({ length: 53 }, (_, i) => ({ id: i + 1, label: `Week ${i + 1}` }));
|
||||
|
||||
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',
|
||||
headerName: 'Trial Name',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
minWidth: 150,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'variety_name',
|
||||
headerName: 'VarietyName',
|
||||
field: 'trial_code',
|
||||
headerName: 'Trial Code',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'breeder',
|
||||
headerName: 'Breeder',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
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('breeders'),
|
||||
valueGetter: (params: GridValueGetterParams) => params?.row?.breeder?.name || params?.row?.breeder?.id || params?.value?.id || params?.value,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'batch_code',
|
||||
headerName: 'BatchCode',
|
||||
field: 'variety_name',
|
||||
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,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
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',
|
||||
headerName: 'TrialType',
|
||||
field: 'finishWeek',
|
||||
headerName: 'Finish Week',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
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',
|
||||
headerName: 'Status',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
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',
|
||||
type: 'actions',
|
||||
minWidth: 30,
|
||||
minWidth: 80,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
getActions: (params: GridRowParams) => {
|
||||
|
||||
return [
|
||||
<div key={params?.row?.id}>
|
||||
<ListActionsPopover
|
||||
@ -366,9 +174,7 @@ export const loadColumns = async (
|
||||
itemId={params?.row?.id}
|
||||
pathEdit={`/trials/trials-edit/?id=${params?.row?.id}`}
|
||||
pathView={`/trials/trials-view/?id=${params?.row?.id}`}
|
||||
|
||||
hasUpdatePermission={hasUpdatePermission}
|
||||
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
|
||||
@ -11,6 +11,7 @@ export type MenuAsideItem = {
|
||||
target?: string
|
||||
color?: ColorButtonKey
|
||||
isLogout?: boolean
|
||||
isHeader?: boolean
|
||||
withDevider?: boolean;
|
||||
menu?: MenuAsideItem[]
|
||||
permissions?: string | string[]
|
||||
@ -22,11 +23,13 @@ export type MenuNavBarItem = {
|
||||
href?: string
|
||||
target?: string
|
||||
isDivider?: boolean
|
||||
isHeader?: boolean
|
||||
isLogout?: boolean
|
||||
isDesktopNoLabel?: boolean
|
||||
isToggleLightDark?: boolean
|
||||
isCurrentUser?: boolean
|
||||
menu?: MenuNavBarItem[]
|
||||
permissions?: string | string[]
|
||||
}
|
||||
|
||||
export type ColorKey = 'white' | 'light' | 'contrast' | 'success' | 'danger' | 'warning' | 'info'
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
import React, { ReactNode, useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
||||
import menuAside from '../menuAside'
|
||||
import menuNavBar from '../menuNavBar'
|
||||
import BaseIcon from '../components/BaseIcon'
|
||||
import NavBar from '../components/NavBar'
|
||||
import NavBarItemPlain from '../components/NavBarItemPlain'
|
||||
import AsideMenu from '../components/AsideMenu'
|
||||
import NavBarMenuList from '../components/NavBarMenuList'
|
||||
import FooterBar from '../components/FooterBar'
|
||||
import { useAppDispatch, useAppSelector } from '../stores/hooks'
|
||||
import Search from '../components/Search';
|
||||
@ -67,60 +64,33 @@ export default function LayoutAuthenticated({
|
||||
|
||||
const darkMode = useAppSelector((state) => state.style.darkMode)
|
||||
|
||||
const [isAsideMobileExpanded, setIsAsideMobileExpanded] = useState(false)
|
||||
const [isAsideLgActive, setIsAsideLgActive] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleRouteChangeStart = () => {
|
||||
setIsAsideMobileExpanded(false)
|
||||
setIsAsideLgActive(false)
|
||||
// Logic for route change start
|
||||
}
|
||||
|
||||
router.events.on('routeChangeStart', handleRouteChangeStart)
|
||||
|
||||
// If the component is unmounted, unsubscribe
|
||||
// from the event with the `off` method:
|
||||
return () => {
|
||||
router.events.off('routeChangeStart', handleRouteChangeStart)
|
||||
}
|
||||
}, [router.events, dispatch])
|
||||
|
||||
|
||||
const layoutAsidePadding = 'xl:pl-60'
|
||||
|
||||
return (
|
||||
<div className={`${darkMode ? 'dark' : ''} overflow-hidden lg:overflow-visible`}>
|
||||
<div
|
||||
className={`${layoutAsidePadding} ${
|
||||
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`}
|
||||
className={`pt-14 min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-dark-800 dark:text-slate-100`}
|
||||
>
|
||||
<NavBar
|
||||
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>
|
||||
<Search />
|
||||
</NavBarItemPlain>
|
||||
</NavBar>
|
||||
<AsideMenu
|
||||
isAsideMobileExpanded={isAsideMobileExpanded}
|
||||
isAsideLgActive={isAsideLgActive}
|
||||
menu={menuAside}
|
||||
onAsideLgClose={() => setIsAsideLgActive(false)}
|
||||
/>
|
||||
|
||||
{children}
|
||||
<FooterBar>Hand-crafted & Made with ❤️</FooterBar>
|
||||
</div>
|
||||
|
||||
@ -7,61 +7,12 @@ const menuAside: MenuAsideItem[] = [
|
||||
icon: icon.mdiViewDashboardOutline,
|
||||
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',
|
||||
label: 'Projects',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @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'
|
||||
},
|
||||
{
|
||||
@ -69,64 +20,187 @@ const menuAside: MenuAsideItem[] = [
|
||||
label: 'Trials',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @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'
|
||||
},
|
||||
{
|
||||
href: '/tracking_activity_types/tracking_activity_types-list',
|
||||
label: 'Tracking activity types',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFormatListBulletedType' in icon ? icon['mdiFormatListBulletedType' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_TRACKING_ACTIVITY_TYPES'
|
||||
},
|
||||
label: 'Tracking',
|
||||
icon: icon.mdiTimelineTextOutline,
|
||||
menu: [
|
||||
{
|
||||
href: '/tracking_activities/tracking_activities-list',
|
||||
label: 'Tracking activities',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiTimelineTextOutline' in icon ? icon['mdiTimelineTextOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
label: 'Activities',
|
||||
icon: icon.mdiCircleSmall,
|
||||
permissions: 'READ_TRACKING_ACTIVITIES'
|
||||
},
|
||||
{
|
||||
href: '/dashboards/dashboards-list',
|
||||
label: 'Dashboards',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiViewDashboardOutline' in icon ? icon['mdiViewDashboardOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'READ_DASHBOARDS'
|
||||
href: '/tracking_activity_types/tracking_activity_types-list',
|
||||
label: 'Activity Types',
|
||||
icon: icon.mdiCircleSmall,
|
||||
permissions: 'READ_TRACKING_ACTIVITY_TYPES'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Reports',
|
||||
icon: icon.mdiFileChartOutline,
|
||||
menu: [
|
||||
{
|
||||
href: '/report_definitions/report_definitions-list',
|
||||
label: 'Report definitions',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFileChartOutline' in icon ? icon['mdiFileChartOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
label: 'Definitions',
|
||||
icon: icon.mdiCircleSmall,
|
||||
permissions: 'READ_REPORT_DEFINITIONS'
|
||||
},
|
||||
{
|
||||
href: '/report_runs/report_runs-list',
|
||||
label: 'Report runs',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiProgressDownload' in icon ? icon['mdiProgressDownload' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
label: 'Runs',
|
||||
icon: icon.mdiCircleSmall,
|
||||
permissions: 'READ_REPORT_RUNS'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: icon.mdiCogOutline,
|
||||
menu: [
|
||||
{
|
||||
label: 'Data Sets',
|
||||
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'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'System',
|
||||
icon: icon.mdiCogOutline,
|
||||
menu: [
|
||||
{
|
||||
href: '/users/users-list',
|
||||
label: 'Users',
|
||||
icon: icon.mdiCircleSmall,
|
||||
permissions: 'READ_USERS'
|
||||
},
|
||||
{
|
||||
href: '/roles/roles-list',
|
||||
label: 'Roles',
|
||||
icon: icon.mdiCircleSmall,
|
||||
permissions: 'READ_ROLES'
|
||||
},
|
||||
{
|
||||
href: '/permissions/permissions-list',
|
||||
label: 'Permissions',
|
||||
icon: icon.mdiCircleSmall,
|
||||
permissions: 'READ_PERMISSIONS'
|
||||
},
|
||||
{
|
||||
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',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiCogOutline' in icon ? icon['mdiCogOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
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',
|
||||
label: 'Profile',
|
||||
icon: icon.mdiAccountCircle,
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
href: '/api-docs',
|
||||
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 { getPageTitle } from '../config'
|
||||
import Link from "next/link";
|
||||
import moment from 'moment';
|
||||
|
||||
import { hasPermission } from "../helpers/userPermissions";
|
||||
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
||||
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
||||
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
||||
import { fetch as fetchActivities } from '../stores/tracking_activities/tracking_activitiesSlice';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||
import CardBox from '../components/CardBox';
|
||||
import UserAvatar from '../components/UserAvatar';
|
||||
|
||||
const Dashboard = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const iconsColor = useAppSelector((state) => state.style.iconsColor);
|
||||
@ -24,526 +29,206 @@ const Dashboard = () => {
|
||||
|
||||
const loadingMessage = 'Loading...';
|
||||
|
||||
|
||||
const [users, setUsers] = React.useState(loadingMessage);
|
||||
const [roles, setRoles] = React.useState(loadingMessage);
|
||||
const [permissions, setPermissions] = React.useState(loadingMessage);
|
||||
const [organizations, setOrganizations] = 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 [projectsCount, setProjectsCount] = React.useState(loadingMessage);
|
||||
const [trialsCount, setTrialsCount] = React.useState(loadingMessage);
|
||||
const [activitiesCount, setActivitiesCount] = React.useState(loadingMessage);
|
||||
|
||||
|
||||
const [widgetsRole, setWidgetsRole] = React.useState({
|
||||
role: { value: '', label: '' },
|
||||
});
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
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);
|
||||
|
||||
|
||||
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,];
|
||||
async function loadCounts() {
|
||||
const entities = ['users', 'projects', 'trials', 'tracking_activities'];
|
||||
const setters = [setUsers, setProjectsCount, setTrialsCount, setActivitiesCount];
|
||||
|
||||
const requests = entities.map((entity, index) => {
|
||||
|
||||
if (hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) {
|
||||
return axios.get(`/${entity.toLowerCase()}/count`);
|
||||
} else {
|
||||
fns[index](null);
|
||||
setters[index](null);
|
||||
return Promise.resolve({ data: { count: null } });
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Promise.allSettled(requests).then((results) => {
|
||||
results.forEach((result, i) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
fns[i](result.value.data.count);
|
||||
setters[i](result.value.data.count);
|
||||
} else {
|
||||
fns[i](result.reason.message);
|
||||
setters[i](result.reason.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function getWidgets(roleId) {
|
||||
await dispatch(fetchWidgets(roleId));
|
||||
}
|
||||
React.useEffect(() => {
|
||||
if (!currentUser) return;
|
||||
loadData().then();
|
||||
setWidgetsRole({ role: { value: currentUser?.app_role?.id, label: currentUser?.app_role?.name } });
|
||||
}, [currentUser]);
|
||||
loadCounts().then();
|
||||
if (hasPermission(currentUser, 'READ_TRACKING_ACTIVITIES')) {
|
||||
dispatch(fetchActivities({ query: '?limit=10' }));
|
||||
}
|
||||
dispatch(fetchWidgets(currentUser?.app_role?.id || '')).then();
|
||||
}, [currentUser, dispatch]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!currentUser || !widgetsRole?.role?.value) return;
|
||||
getWidgets(widgetsRole?.role?.value || '').then();
|
||||
}, [widgetsRole?.role?.value]);
|
||||
const getSeverityColor = (severity: string) => {
|
||||
switch (severity) {
|
||||
case 'critical': return 'text-red-600';
|
||||
case 'high': return 'text-orange-600';
|
||||
case 'medium': return 'text-yellow-600';
|
||||
case 'low': return 'text-blue-600';
|
||||
default: return 'text-gray-600';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>
|
||||
{getPageTitle('Overview')}
|
||||
</title>
|
||||
<title>{getPageTitle('Dashboard')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={icon.mdiChartTimelineVariant}
|
||||
title='Overview'
|
||||
main>
|
||||
icon={icon.mdiViewDashboardOutline}
|
||||
title='Trial Tracker Dashboard'
|
||||
main
|
||||
>
|
||||
{''}
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
{hasPermission(currentUser, 'CREATE_ROLES') && <WidgetCreator
|
||||
{/* Quick Stats */}
|
||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-6'>
|
||||
{hasPermission(currentUser, 'READ_PROJECTS') && (
|
||||
<CardBox className={cardsStyle}>
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<p className="text-gray-500 dark:text-gray-400">Projects</p>
|
||||
<h3 className="text-2xl font-bold">{projectsCount}</h3>
|
||||
</div>
|
||||
<BaseIcon path={icon.mdiBriefcaseOutline} size={32} className={iconsColor} />
|
||||
</div>
|
||||
</CardBox>
|
||||
)}
|
||||
{hasPermission(currentUser, 'READ_TRIALS') && (
|
||||
<CardBox className={cardsStyle}>
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<p className="text-gray-500 dark:text-gray-400">Active Trials</p>
|
||||
<h3 className="text-2xl font-bold">{trialsCount}</h3>
|
||||
</div>
|
||||
<BaseIcon path={icon.mdiSprout} size={32} className={iconsColor} />
|
||||
</div>
|
||||
</CardBox>
|
||||
)}
|
||||
{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>
|
||||
|
||||
{hasPermission(currentUser, 'CREATE_ROLES') && (
|
||||
<WidgetCreator
|
||||
currentUser={currentUser}
|
||||
isFetchingQuery={isFetchingQuery}
|
||||
setWidgetsRole={setWidgetsRole}
|
||||
widgetsRole={widgetsRole}
|
||||
/>}
|
||||
{!!rolesWidgets.length &&
|
||||
hasPermission(currentUser, 'CREATE_ROLES') && (
|
||||
<p className=' text-gray-500 dark:text-gray-400 mb-4'>
|
||||
{`${widgetsRole?.role?.label || 'Users'}'s widgets`}
|
||||
setWidgetsRole={() => {
|
||||
// No-op for dashboard feed view
|
||||
}}
|
||||
widgetsRole={{ role: { value: currentUser?.app_role?.id, label: currentUser?.app_role?.name } }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<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" />
|
||||
|
||||
{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 className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-6 grid-flow-dense'>
|
||||
{(isFetchingQuery || loading) && (
|
||||
<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`}>
|
||||
<BaseIcon
|
||||
className={`${iconsColor} animate-spin mr-5`}
|
||||
w='w-16'
|
||||
h='h-16'
|
||||
size={48}
|
||||
path={icon.mdiLoading}
|
||||
/>{' '}
|
||||
Loading widgets...
|
||||
</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>
|
||||
|
||||
{ rolesWidgets &&
|
||||
rolesWidgets.map((widget) => (
|
||||
{/* Right Column: Widgets or Quick Info */}
|
||||
<div className="space-y-6">
|
||||
<SectionTitleLineWithButton icon={icon.mdiChartPie} title="Distribution" />
|
||||
<div className='grid grid-cols-1 gap-6'>
|
||||
{rolesWidgets && rolesWidgets.map((widget) => (
|
||||
<SmartWidget
|
||||
key={widget.id}
|
||||
userId={currentUser?.id}
|
||||
widget={widget}
|
||||
roleId={widgetsRole?.role?.value || ''}
|
||||
roleId={currentUser?.app_role?.id || ''}
|
||||
admin={hasPermission(currentUser, 'CREATE_ROLES')}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{!!rolesWidgets.length && <hr className='my-6 ' />}
|
||||
|
||||
<div id="dashboard" className='grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6'>
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_USERS') && <Link href={'/users/users-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">
|
||||
Users
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{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>
|
||||
</Link>}
|
||||
|
||||
{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>
|
||||
</>
|
||||
|
||||
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