Compare commits

..

No commits in common. "ai-dev" and "master" have entirely different histories.

23 changed files with 4 additions and 1910 deletions

View File

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

View File

@ -1,211 +0,0 @@
const db = require('../models');
const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
module.exports = class QuartiersDBApi {
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const record = await db.quartiers.create(
{
id: data.id || undefined,
nom: data.nom || null,
notes: data.notes || null,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
);
await record.setVillage(data.village || data.villageId || null, { transaction });
return record;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const rows = data.map((item, index) => ({
id: item.id || undefined,
nom: item.nom || null,
notes: item.notes || null,
villageId: item.villageId || item.village || null,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
return db.quartiers.bulkCreate(rows, { transaction });
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const record = await db.quartiers.findByPk(id, { transaction });
const payload = {
updatedById: currentUser.id,
};
if (data.nom !== undefined) payload.nom = data.nom;
if (data.notes !== undefined) payload.notes = data.notes;
await record.update(payload, { transaction });
if (data.village !== undefined || data.villageId !== undefined) {
await record.setVillage(data.village || data.villageId || null, { transaction });
}
return record;
}
static async deleteByIds(ids, options) {
const transaction = (options && options.transaction) || undefined;
const records = await db.quartiers.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.quartiers.findByPk(id, { transaction });
await record.destroy({ transaction });
return record;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const record = await db.quartiers.findOne({
where,
include: [
{
model: db.villages,
as: 'village',
attributes: ['id', 'nom'],
},
],
transaction,
});
if (!record) {
return record;
}
return record.get({ plain: true });
}
static async findAll(filter, options) {
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
const offset = currentPage * limit;
let where = {};
if (filter.id) {
where.id = Utils.uuid(filter.id);
}
if (filter.nom) {
where = {
...where,
[Op.and]: Utils.ilike('quartiers', 'nom', filter.nom),
};
}
if (filter.notes) {
where = {
...where,
[Op.and]: Utils.ilike('quartiers', 'notes', filter.notes),
};
}
if (filter.villageId || filter.village) {
where.villageId = filter.villageId || filter.village;
}
const include = [
{
model: db.villages,
as: 'village',
attributes: ['id', 'nom'],
required: false,
},
];
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['nom', 'asc']],
transaction: options?.transaction,
logging: console.log,
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
const { rows, count } = await db.quartiers.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count,
};
}
static async findAllAutocomplete(query, limit, offset) {
let where = {};
if (query) {
where = {
[Op.or]: [
{ id: Utils.uuid(query) },
Utils.ilike('quartiers', 'nom', query),
],
};
}
const records = await db.quartiers.findAll({
attributes: ['id', 'nom'],
include: [
{
model: db.villages,
as: 'village',
attributes: ['nom'],
required: false,
},
],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
order: [['nom', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.village?.nom ? `${record.nom} (${record.village.nom})` : record.nom,
}));
}
};

View File

@ -1,207 +0,0 @@
const db = require('../models');
const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
module.exports = class Type_usagesDBApi {
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const type_usages = await db.type_usages.create(
{
id: data.id || undefined,
nom: data.nom || null,
tarif: data.tarif ?? 0,
actif: data.actif ?? true,
description: data.description || null,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
);
return type_usages;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const rows = data.map((item, index) => ({
id: item.id || undefined,
nom: item.nom || null,
tarif: item.tarif ?? 0,
actif: item.actif === undefined ? true : item.actif,
description: item.description || null,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
return db.type_usages.bulkCreate(rows, { transaction });
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const type_usages = await db.type_usages.findByPk(id, { transaction });
const payload = {
updatedById: currentUser.id,
};
if (data.nom !== undefined) payload.nom = data.nom;
if (data.tarif !== undefined) payload.tarif = data.tarif;
if (data.actif !== undefined) payload.actif = data.actif;
if (data.description !== undefined) payload.description = data.description;
await type_usages.update(payload, { transaction });
return type_usages;
}
static async deleteByIds(ids, options) {
const transaction = (options && options.transaction) || undefined;
const records = await db.type_usages.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.type_usages.findByPk(id, { transaction });
await record.destroy({ transaction });
return record;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const record = await db.type_usages.findOne({
where,
transaction,
});
if (!record) {
return record;
}
return record.get({ plain: true });
}
static async findAll(filter, options) {
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
const offset = currentPage * limit;
let where = {};
if (filter.id) {
where.id = Utils.uuid(filter.id);
}
if (filter.nom) {
where = {
...where,
[Op.and]: Utils.ilike('type_usages', 'nom', filter.nom),
};
}
if (filter.description) {
where = {
...where,
[Op.and]: Utils.ilike('type_usages', 'description', filter.description),
};
}
if (filter.actif !== undefined) {
where.actif = filter.actif === true || filter.actif === 'true';
}
if (filter.tarifRange) {
const values = Array.isArray(filter.tarifRange)
? filter.tarifRange
: [filter.tarifRange];
const [start, end] = values;
if (start !== undefined && start !== '') {
where.tarif = {
...where.tarif,
[Op.gte]: start,
};
}
if (end !== undefined && end !== '') {
where.tarif = {
...where.tarif,
[Op.lte]: end,
};
}
}
const queryOptions = {
where,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['nom', 'asc']],
transaction: options?.transaction,
logging: console.log,
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
const { rows, count } = await db.type_usages.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count,
};
}
static async findAllAutocomplete(query, limit, offset) {
let where = {};
if (query) {
where = {
[Op.or]: [
{ id: Utils.uuid(query) },
Utils.ilike('type_usages', 'nom', query),
],
};
}
const records = await db.type_usages.findAll({
attributes: ['id', 'nom'],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
order: [['nom', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.nom,
}));
}
};

View File

@ -1,226 +0,0 @@
const db = require('../models');
const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
module.exports = class VillagesDBApi {
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
return db.villages.create(
{
id: data.id || undefined,
nom: data.nom || null,
commune: data.commune || null,
arrondissement: data.arrondissement || null,
departement: data.departement || null,
region: data.region || null,
duree_periode_jours: data.duree_periode_jours || null,
notes: data.notes || null,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
);
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const rows = data.map((item, index) => ({
id: item.id || undefined,
nom: item.nom || null,
commune: item.commune || null,
arrondissement: item.arrondissement || null,
departement: item.departement || null,
region: item.region || null,
duree_periode_jours: item.duree_periode_jours || null,
notes: item.notes || null,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
return db.villages.bulkCreate(rows, { transaction });
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const record = await db.villages.findByPk(id, { transaction });
const payload = {
updatedById: currentUser.id,
};
if (data.nom !== undefined) payload.nom = data.nom;
if (data.commune !== undefined) payload.commune = data.commune;
if (data.arrondissement !== undefined) payload.arrondissement = data.arrondissement;
if (data.departement !== undefined) payload.departement = data.departement;
if (data.region !== undefined) payload.region = data.region;
if (data.duree_periode_jours !== undefined) payload.duree_periode_jours = data.duree_periode_jours;
if (data.notes !== undefined) payload.notes = data.notes;
await record.update(payload, { transaction });
return record;
}
static async deleteByIds(ids, options) {
const transaction = (options && options.transaction) || undefined;
const records = await db.villages.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.villages.findByPk(id, { transaction });
await record.destroy({ transaction });
return record;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const record = await db.villages.findOne({
where,
include: [
{
model: db.quartiers,
as: 'quartiers',
attributes: ['id', 'nom'],
},
],
transaction,
});
if (!record) {
return record;
}
return record.get({ plain: true });
}
static async findAll(filter, options) {
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
const offset = currentPage * limit;
let where = {};
if (filter.id) {
where.id = Utils.uuid(filter.id);
}
if (filter.nom) {
where = {
...where,
[Op.and]: Utils.ilike('villages', 'nom', filter.nom),
};
}
for (const field of ['commune', 'arrondissement', 'departement', 'region', 'notes']) {
if (filter[field]) {
where = {
...where,
[Op.and]: Utils.ilike('villages', field, filter[field]),
};
}
}
if (filter.duree_periode_joursRange) {
const values = Array.isArray(filter.duree_periode_joursRange)
? filter.duree_periode_joursRange
: [filter.duree_periode_joursRange];
const [start, end] = values;
if (start !== undefined && start !== '') {
where.duree_periode_jours = {
...where.duree_periode_jours,
[Op.gte]: start,
};
}
if (end !== undefined && end !== '') {
where.duree_periode_jours = {
...where.duree_periode_jours,
[Op.lte]: end,
};
}
}
const queryOptions = {
where,
include: [
{
model: db.quartiers,
as: 'quartiers',
attributes: ['id', 'nom'],
required: false,
},
],
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['nom', 'asc']],
transaction: options?.transaction,
logging: console.log,
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
const { rows, count } = await db.villages.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count,
};
}
static async findAllAutocomplete(query, limit, offset) {
let where = {};
if (query) {
where = {
[Op.or]: [
{ id: Utils.uuid(query) },
Utils.ilike('villages', 'nom', query),
],
};
}
const records = await db.villages.findAll({
attributes: ['id', 'nom'],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
order: [['nom', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.nom,
}));
}
};

View File

@ -1,214 +0,0 @@
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const villageTable = await queryInterface.sequelize.query(
"SELECT to_regclass('public.villages') AS table_name;",
{ transaction, type: Sequelize.QueryTypes.SELECT },
);
if (!villageTable[0]?.table_name) {
await queryInterface.createTable(
'type_usages',
{
id: {
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.DataTypes.UUIDV4,
primaryKey: true,
},
nom: {
type: Sequelize.DataTypes.STRING(50),
allowNull: false,
unique: true,
},
tarif: {
type: Sequelize.DataTypes.DECIMAL(10, 2),
allowNull: false,
defaultValue: 0,
},
actif: {
type: Sequelize.DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
},
description: {
type: Sequelize.DataTypes.TEXT,
},
createdById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
updatedById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
createdAt: { type: Sequelize.DataTypes.DATE },
updatedAt: { type: Sequelize.DataTypes.DATE },
deletedAt: { type: Sequelize.DataTypes.DATE },
importHash: {
type: Sequelize.DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{ transaction },
);
await queryInterface.addIndex('type_usages', ['actif'], {
name: 'type_usages_actif_idx',
transaction,
});
await queryInterface.createTable(
'villages',
{
id: {
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.DataTypes.UUIDV4,
primaryKey: true,
},
nom: {
type: Sequelize.DataTypes.STRING(100),
allowNull: false,
unique: true,
},
commune: {
type: Sequelize.DataTypes.STRING(100),
},
arrondissement: {
type: Sequelize.DataTypes.STRING(100),
},
departement: {
type: Sequelize.DataTypes.STRING(100),
},
region: {
type: Sequelize.DataTypes.STRING(100),
},
duree_periode_jours: {
type: Sequelize.DataTypes.INTEGER,
},
notes: {
type: Sequelize.DataTypes.TEXT,
},
createdById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
updatedById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
createdAt: { type: Sequelize.DataTypes.DATE },
updatedAt: { type: Sequelize.DataTypes.DATE },
deletedAt: { type: Sequelize.DataTypes.DATE },
importHash: {
type: Sequelize.DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{ transaction },
);
await queryInterface.createTable(
'quartiers',
{
id: {
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.DataTypes.UUIDV4,
primaryKey: true,
},
nom: {
type: Sequelize.DataTypes.STRING(100),
allowNull: false,
},
notes: {
type: Sequelize.DataTypes.TEXT,
},
villageId: {
type: Sequelize.DataTypes.UUID,
allowNull: false,
references: {
key: 'id',
model: 'villages',
},
onDelete: 'RESTRICT',
},
createdById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
updatedById: {
type: Sequelize.DataTypes.UUID,
references: {
key: 'id',
model: 'users',
},
},
createdAt: { type: Sequelize.DataTypes.DATE },
updatedAt: { type: Sequelize.DataTypes.DATE },
deletedAt: { type: Sequelize.DataTypes.DATE },
importHash: {
type: Sequelize.DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{ transaction },
);
await queryInterface.addConstraint('quartiers', {
fields: ['nom', 'villageId'],
type: 'unique',
name: 'quartiers_nom_village_unique',
transaction,
});
}
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const villageTable = await queryInterface.sequelize.query(
"SELECT to_regclass('public.villages') AS table_name;",
{ transaction, type: Sequelize.QueryTypes.SELECT },
);
if (!villageTable[0]?.table_name) {
await transaction.commit();
return;
}
await queryInterface.dropTable('quartiers', { transaction });
await queryInterface.dropTable('villages', { transaction });
await queryInterface.dropTable('type_usages', { transaction });
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
};

View File

@ -1,50 +0,0 @@
module.exports = function(sequelize, DataTypes) {
const quartiers = sequelize.define(
'quartiers',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
nom: {
type: DataTypes.STRING(100),
allowNull: false,
},
notes: {
type: DataTypes.TEXT,
},
importHash: {
type: DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{
timestamps: true,
paranoid: true,
freezeTableName: true,
},
);
quartiers.associate = (db) => {
db.quartiers.belongsTo(db.villages, {
as: 'village',
foreignKey: {
name: 'villageId',
allowNull: false,
},
constraints: false,
});
db.quartiers.belongsTo(db.users, {
as: 'createdBy',
});
db.quartiers.belongsTo(db.users, {
as: 'updatedBy',
});
};
return quartiers;
};

View File

@ -1,51 +0,0 @@
module.exports = function(sequelize, DataTypes) {
const type_usages = sequelize.define(
'type_usages',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
nom: {
type: DataTypes.STRING(50),
allowNull: false,
},
tarif: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
defaultValue: 0,
},
actif: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
},
description: {
type: DataTypes.TEXT,
},
importHash: {
type: DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{
timestamps: true,
paranoid: true,
freezeTableName: true,
},
);
type_usages.associate = (db) => {
db.type_usages.belongsTo(db.users, {
as: 'createdBy',
});
db.type_usages.belongsTo(db.users, {
as: 'updatedBy',
});
};
return type_usages;
};

View File

@ -1,62 +0,0 @@
module.exports = function(sequelize, DataTypes) {
const villages = sequelize.define(
'villages',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
nom: {
type: DataTypes.STRING(100),
allowNull: false,
},
commune: {
type: DataTypes.STRING(100),
},
arrondissement: {
type: DataTypes.STRING(100),
},
departement: {
type: DataTypes.STRING(100),
},
region: {
type: DataTypes.STRING(100),
},
duree_periode_jours: {
type: DataTypes.INTEGER,
},
notes: {
type: DataTypes.TEXT,
},
importHash: {
type: DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{
timestamps: true,
paranoid: true,
freezeTableName: true,
},
);
villages.associate = (db) => {
db.villages.hasMany(db.quartiers, {
as: 'quartiers',
foreignKey: 'villageId',
constraints: false,
});
db.villages.belongsTo(db.users, {
as: 'createdBy',
});
db.villages.belongsTo(db.users, {
as: 'updatedBy',
});
};
return villages;
};

View File

@ -1,115 +0,0 @@
const { QueryTypes } = require('sequelize');
const { v4: uuid } = require('uuid');
module.exports = {
async up(queryInterface) {
const transaction = await queryInterface.sequelize.transaction();
try {
const createdAt = new Date();
const updatedAt = new Date();
const entities = ['type_usages', 'villages', 'quartiers'];
const actions = ['CREATE', 'READ', 'UPDATE', 'DELETE'];
const roles = await queryInterface.sequelize.query(
'SELECT id, name FROM roles WHERE name IN (:roles);',
{
replacements: { roles: ['Administrator', 'Platform Owner', 'Product Manager'] },
type: QueryTypes.SELECT,
transaction,
},
);
for (const entity of entities) {
for (const action of actions) {
const name = `${action}_${entity.toUpperCase()}`;
const existing = await queryInterface.sequelize.query(
'SELECT id FROM permissions WHERE name = :name LIMIT 1;',
{
replacements: { name },
type: QueryTypes.SELECT,
transaction,
},
);
let permissionId = existing[0]?.id;
if (!permissionId) {
permissionId = uuid();
await queryInterface.bulkInsert(
'permissions',
[{ id: permissionId, name, createdAt, updatedAt }],
{ transaction },
);
}
for (const role of roles) {
const link = await queryInterface.sequelize.query(
'SELECT 1 FROM "rolesPermissionsPermissions" WHERE "roles_permissionsId" = :roleId AND "permissionId" = :permissionId LIMIT 1;',
{
replacements: { roleId: role.id, permissionId },
type: QueryTypes.SELECT,
transaction,
},
);
if (!link.length) {
await queryInterface.bulkInsert(
'rolesPermissionsPermissions',
[{
createdAt,
updatedAt,
roles_permissionsId: role.id,
permissionId,
}],
{ transaction },
);
}
}
}
}
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
async down(queryInterface) {
const transaction = await queryInterface.sequelize.transaction();
try {
const permissions = await queryInterface.sequelize.query(
'SELECT id FROM permissions WHERE name LIKE ANY (ARRAY[:patterns]);',
{
replacements: {
patterns: ['%TYPE_USAGES', '%VILLAGES', '%QUARTIERS'],
},
type: QueryTypes.SELECT,
transaction,
},
);
const ids = permissions.map((item) => item.id);
if (ids.length) {
await queryInterface.bulkDelete(
'rolesPermissionsPermissions',
{ permissionId: ids },
{ transaction },
);
await queryInterface.bulkDelete(
'permissions',
{ id: ids },
{ transaction },
);
}
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
};

View File

@ -36,9 +36,6 @@ const messagesRoutes = require('./routes/messages');
const audit_logsRoutes = require('./routes/audit_logs'); const audit_logsRoutes = require('./routes/audit_logs');
const app_settingsRoutes = require('./routes/app_settings'); const app_settingsRoutes = require('./routes/app_settings');
const villagesRoutes = require('./routes/villages');
const quartiersRoutes = require('./routes/quartiers');
const type_usagesRoutes = require('./routes/type_usages');
const getBaseUrl = (url) => { const getBaseUrl = (url) => {
@ -113,9 +110,6 @@ app.use('/api/messages', passport.authenticate('jwt', {session: false}), message
app.use('/api/audit_logs', passport.authenticate('jwt', {session: false}), audit_logsRoutes); app.use('/api/audit_logs', passport.authenticate('jwt', {session: false}), audit_logsRoutes);
app.use('/api/app_settings', passport.authenticate('jwt', {session: false}), app_settingsRoutes); app.use('/api/app_settings', passport.authenticate('jwt', {session: false}), app_settingsRoutes);
app.use('/api/villages', passport.authenticate('jwt', {session: false}), villagesRoutes);
app.use('/api/quartiers', passport.authenticate('jwt', {session: false}), quartiersRoutes);
app.use('/api/type_usages', passport.authenticate('jwt', {session: false}), type_usagesRoutes);
app.use( app.use(
'/api/openai', '/api/openai',

View File

@ -1,78 +0,0 @@
const express = require('express');
const QuartiersService = require('../services/quartiers');
const QuartiersDBApi = require('../db/api/quartiers');
const wrapAsync = require('../helpers').wrapAsync;
const { parse } = require('json2csv');
const { checkCrudPermissions } = require('../middlewares/check-permissions');
const router = express.Router();
router.use(checkCrudPermissions('quartiers'));
router.post('/', wrapAsync(async (req, res) => {
await QuartiersService.create(req.body.data, req.currentUser);
res.status(200).send(true);
}));
router.post('/bulk-import', wrapAsync(async (req, res) => {
await QuartiersService.bulkImport(req, res);
res.status(200).send(true);
}));
router.put('/:id', wrapAsync(async (req, res) => {
await QuartiersService.update(req.body.data, req.params.id, req.currentUser);
res.status(200).send(true);
}));
router.delete('/:id', wrapAsync(async (req, res) => {
await QuartiersService.remove(req.params.id, req.currentUser);
res.status(200).send(true);
}));
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await QuartiersService.deleteByIds(req.body.data, req.currentUser);
res.status(200).send(true);
}));
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype;
const payload = await QuartiersDBApi.findAll(req.query, { currentUser: req.currentUser });
if (filetype === 'csv') {
const fields = ['id', 'nom', 'notes', 'villageId'];
const csv = parse(payload.rows, { fields });
res.status(200).attachment(csv);
res.send(csv);
return;
}
res.status(200).send(payload);
}));
router.get('/count', wrapAsync(async (req, res) => {
const payload = await QuartiersDBApi.findAll(req.query, {
countOnly: true,
currentUser: req.currentUser,
});
res.status(200).send(payload);
}));
router.get('/autocomplete', wrapAsync(async (req, res) => {
const payload = await QuartiersDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
}));
router.get('/:id', wrapAsync(async (req, res) => {
const payload = await QuartiersDBApi.findBy({ id: req.params.id });
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,78 +0,0 @@
const express = require('express');
const Type_usagesService = require('../services/type_usages');
const Type_usagesDBApi = require('../db/api/type_usages');
const wrapAsync = require('../helpers').wrapAsync;
const { parse } = require('json2csv');
const { checkCrudPermissions } = require('../middlewares/check-permissions');
const router = express.Router();
router.use(checkCrudPermissions('type_usages'));
router.post('/', wrapAsync(async (req, res) => {
await Type_usagesService.create(req.body.data, req.currentUser);
res.status(200).send(true);
}));
router.post('/bulk-import', wrapAsync(async (req, res) => {
await Type_usagesService.bulkImport(req, res);
res.status(200).send(true);
}));
router.put('/:id', wrapAsync(async (req, res) => {
await Type_usagesService.update(req.body.data, req.params.id, req.currentUser);
res.status(200).send(true);
}));
router.delete('/:id', wrapAsync(async (req, res) => {
await Type_usagesService.remove(req.params.id, req.currentUser);
res.status(200).send(true);
}));
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Type_usagesService.deleteByIds(req.body.data, req.currentUser);
res.status(200).send(true);
}));
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype;
const payload = await Type_usagesDBApi.findAll(req.query, { currentUser: req.currentUser });
if (filetype === 'csv') {
const fields = ['id', 'nom', 'tarif', 'actif', 'description'];
const csv = parse(payload.rows, { fields });
res.status(200).attachment(csv);
res.send(csv);
return;
}
res.status(200).send(payload);
}));
router.get('/count', wrapAsync(async (req, res) => {
const payload = await Type_usagesDBApi.findAll(req.query, {
countOnly: true,
currentUser: req.currentUser,
});
res.status(200).send(payload);
}));
router.get('/autocomplete', wrapAsync(async (req, res) => {
const payload = await Type_usagesDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
}));
router.get('/:id', wrapAsync(async (req, res) => {
const payload = await Type_usagesDBApi.findBy({ id: req.params.id });
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,78 +0,0 @@
const express = require('express');
const VillagesService = require('../services/villages');
const VillagesDBApi = require('../db/api/villages');
const wrapAsync = require('../helpers').wrapAsync;
const { parse } = require('json2csv');
const { checkCrudPermissions } = require('../middlewares/check-permissions');
const router = express.Router();
router.use(checkCrudPermissions('villages'));
router.post('/', wrapAsync(async (req, res) => {
await VillagesService.create(req.body.data, req.currentUser);
res.status(200).send(true);
}));
router.post('/bulk-import', wrapAsync(async (req, res) => {
await VillagesService.bulkImport(req, res);
res.status(200).send(true);
}));
router.put('/:id', wrapAsync(async (req, res) => {
await VillagesService.update(req.body.data, req.params.id, req.currentUser);
res.status(200).send(true);
}));
router.delete('/:id', wrapAsync(async (req, res) => {
await VillagesService.remove(req.params.id, req.currentUser);
res.status(200).send(true);
}));
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await VillagesService.deleteByIds(req.body.data, req.currentUser);
res.status(200).send(true);
}));
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype;
const payload = await VillagesDBApi.findAll(req.query, { currentUser: req.currentUser });
if (filetype === 'csv') {
const fields = ['id', 'nom', 'commune', 'arrondissement', 'departement', 'region', 'duree_periode_jours', 'notes'];
const csv = parse(payload.rows, { fields });
res.status(200).attachment(csv);
res.send(csv);
return;
}
res.status(200).send(payload);
}));
router.get('/count', wrapAsync(async (req, res) => {
const payload = await VillagesDBApi.findAll(req.query, {
countOnly: true,
currentUser: req.currentUser,
});
res.status(200).send(payload);
}));
router.get('/autocomplete', wrapAsync(async (req, res) => {
const payload = await VillagesDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
}));
router.get('/:id', wrapAsync(async (req, res) => {
const payload = await VillagesDBApi.findBy({ id: req.params.id });
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,111 +0,0 @@
const db = require('../db/models');
const QuartiersDBApi = require('../db/api/quartiers');
const processFile = require('../middlewares/upload');
const ValidationError = require('./notifications/errors/validation');
const csv = require('csv-parser');
const stream = require('stream');
module.exports = class QuartiersService {
static async create(data, currentUser) {
const transaction = await db.sequelize.transaction();
try {
await QuartiersDBApi.create(data, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async bulkImport(req, res) {
const transaction = await db.sequelize.transaction();
try {
await processFile(req, res);
const bufferStream = new stream.PassThrough();
const results = [];
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8'));
await new Promise((resolve, reject) => {
bufferStream
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', async () => resolve())
.on('error', (error) => reject(error));
});
await QuartiersDBApi.bulkImport(results, {
transaction,
ignoreDuplicates: true,
validate: true,
currentUser: req.currentUser,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async update(data, id, currentUser) {
const transaction = await db.sequelize.transaction();
try {
const existing = await QuartiersDBApi.findBy({ id }, { transaction });
if (!existing) {
throw new ValidationError('quartiersNotFound');
}
const updated = await QuartiersDBApi.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 QuartiersDBApi.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 QuartiersDBApi.remove(id, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
};

View File

@ -1,111 +0,0 @@
const db = require('../db/models');
const Type_usagesDBApi = require('../db/api/type_usages');
const processFile = require('../middlewares/upload');
const ValidationError = require('./notifications/errors/validation');
const csv = require('csv-parser');
const stream = require('stream');
module.exports = class Type_usagesService {
static async create(data, currentUser) {
const transaction = await db.sequelize.transaction();
try {
await Type_usagesDBApi.create(data, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async bulkImport(req, res) {
const transaction = await db.sequelize.transaction();
try {
await processFile(req, res);
const bufferStream = new stream.PassThrough();
const results = [];
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8'));
await new Promise((resolve, reject) => {
bufferStream
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', async () => resolve())
.on('error', (error) => reject(error));
});
await Type_usagesDBApi.bulkImport(results, {
transaction,
ignoreDuplicates: true,
validate: true,
currentUser: req.currentUser,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async update(data, id, currentUser) {
const transaction = await db.sequelize.transaction();
try {
const existing = await Type_usagesDBApi.findBy({ id }, { transaction });
if (!existing) {
throw new ValidationError('type_usagesNotFound');
}
const updated = await Type_usagesDBApi.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 Type_usagesDBApi.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 Type_usagesDBApi.remove(id, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
};

View File

@ -1,111 +0,0 @@
const db = require('../db/models');
const VillagesDBApi = require('../db/api/villages');
const processFile = require('../middlewares/upload');
const ValidationError = require('./notifications/errors/validation');
const csv = require('csv-parser');
const stream = require('stream');
module.exports = class VillagesService {
static async create(data, currentUser) {
const transaction = await db.sequelize.transaction();
try {
await VillagesDBApi.create(data, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async bulkImport(req, res) {
const transaction = await db.sequelize.transaction();
try {
await processFile(req, res);
const bufferStream = new stream.PassThrough();
const results = [];
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8'));
await new Promise((resolve, reject) => {
bufferStream
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', async () => resolve())
.on('error', (error) => reject(error));
});
await VillagesDBApi.bulkImport(results, {
transaction,
ignoreDuplicates: true,
validate: true,
currentUser: req.currentUser,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async update(data, id, currentUser) {
const transaction = await db.sequelize.transaction();
try {
const existing = await VillagesDBApi.findBy({ id }, { transaction });
if (!existing) {
throw new ValidationError('villagesNotFound');
}
const updated = await VillagesDBApi.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 VillagesDBApi.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 VillagesDBApi.remove(id, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,5 +1,6 @@
import React, { useEffect, useRef, useState } from 'react' import React, {useEffect, useRef} from 'react'
import Link from 'next/link' import Link from 'next/link'
import { useState } from 'react'
import { mdiChevronUp, mdiChevronDown } from '@mdi/js' import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
import BaseDivider from './BaseDivider' import BaseDivider from './BaseDivider'
import BaseIcon from './BaseIcon' import BaseIcon from './BaseIcon'

View File

@ -1,4 +1,5 @@
import React, { ReactNode, useEffect, useState } from 'react' import React, { ReactNode, useEffect } from 'react'
import { useState } from 'react'
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js' import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
import menuAside from '../menuAside' import menuAside from '../menuAside'

View File

@ -72,14 +72,6 @@ const menuAside: MenuAsideItem[] = [
icon: 'mdiCogOutline' in icon ? icon['mdiCogOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, icon: 'mdiCogOutline' in icon ? icon['mdiCogOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_APP_SETTINGS' permissions: 'READ_APP_SETTINGS'
}, },
{
href: '/g-forage/referentials',
label: 'Référentiels forage',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: 'mdiDatabaseCogOutline' in icon ? icon['mdiDatabaseCogOutline' as keyof typeof icon] : icon.mdiTable,
permissions: 'READ_VILLAGES'
},
{ {
href: '/profile', href: '/profile',
label: 'Profile', label: 'Profile',

View File

@ -1,191 +0,0 @@
import { mdiTable } from '@mdi/js';
import Head from 'next/head';
import React, { ReactElement, useEffect, useState } from 'react';
import axios from 'axios';
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 { useAppSelector } from '../../stores/hooks';
type ItemRow = {
id: string;
nom: string;
commune?: string;
tarif?: number | string;
actif?: boolean;
village?: { nom?: string };
};
const ReferentialsPage = () => {
const { currentUser } = useAppSelector((state) => state.auth);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [stats, setStats] = useState({ villages: 0, quartiers: 0, typeUsages: 0 });
const [villages, setVillages] = useState<ItemRow[]>([]);
const [quartiers, setQuartiers] = useState<ItemRow[]>([]);
const [typeUsages, setTypeUsages] = useState<ItemRow[]>([]);
useEffect(() => {
if (!currentUser) return;
const loadData = async () => {
try {
setLoading(true);
setError('');
const [villagesCount, quartiersCount, typeUsagesCount, villagesRows, quartiersRows, typeUsagesRows] = await Promise.all([
axios.get('/villages/count'),
axios.get('/quartiers/count'),
axios.get('/type_usages/count'),
axios.get('/villages', { params: { page: 0, limit: 5 } }),
axios.get('/quartiers', { params: { page: 0, limit: 5 } }),
axios.get('/type_usages', { params: { page: 0, limit: 5 } }),
]);
setStats({
villages: villagesCount.data.count ?? 0,
quartiers: quartiersCount.data.count ?? 0,
typeUsages: typeUsagesCount.data.count ?? 0,
});
setVillages(villagesRows.data.rows ?? []);
setQuartiers(quartiersRows.data.rows ?? []);
setTypeUsages(typeUsagesRows.data.rows ?? []);
} catch (err) {
console.error('Failed to load G-Forage referentials:', err);
setError('Impossible de charger les référentiels G-Forage pour le moment.');
} finally {
setLoading(false);
}
};
loadData();
}, [currentUser]);
return (
<>
<Head>
<title>{getPageTitle('Référentiels G-Forage')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiTable}
title='Référentiels G-Forage'
main
>
{''}
</SectionTitleLineWithButton>
<div className='grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6'>
<CardBox>
<div className='text-sm text-gray-500 dark:text-gray-400'>Villages</div>
<div className='text-3xl font-semibold mt-2'>{loading ? '…' : stats.villages}</div>
</CardBox>
<CardBox>
<div className='text-sm text-gray-500 dark:text-gray-400'>Quartiers</div>
<div className='text-3xl font-semibold mt-2'>{loading ? '…' : stats.quartiers}</div>
</CardBox>
<CardBox>
<div className='text-sm text-gray-500 dark:text-gray-400'>Types dusage</div>
<div className='text-3xl font-semibold mt-2'>{loading ? '…' : stats.typeUsages}</div>
</CardBox>
</div>
{error && (
<CardBox className='mb-6'>
<p className='text-red-600 dark:text-red-400'>{error}</p>
</CardBox>
)}
<div className='grid grid-cols-1 gap-6 xl:grid-cols-3'>
<CardBox>
<h3 className='text-lg font-semibold mb-4'>Derniers villages</h3>
<table className='w-full text-sm'>
<thead>
<tr className='text-left border-b'>
<th className='py-2'>Nom</th>
<th className='py-2'>Commune</th>
</tr>
</thead>
<tbody>
{villages.map((item) => (
<tr key={item.id} className='border-b last:border-b-0'>
<td className='py-2'>{item.nom}</td>
<td className='py-2'>{item.commune || '—'}</td>
</tr>
))}
{!loading && villages.length === 0 && (
<tr>
<td className='py-2 text-gray-500' colSpan={2}>Aucun village pour le moment.</td>
</tr>
)}
</tbody>
</table>
</CardBox>
<CardBox>
<h3 className='text-lg font-semibold mb-4'>Derniers quartiers</h3>
<table className='w-full text-sm'>
<thead>
<tr className='text-left border-b'>
<th className='py-2'>Nom</th>
<th className='py-2'>Village</th>
</tr>
</thead>
<tbody>
{quartiers.map((item) => (
<tr key={item.id} className='border-b last:border-b-0'>
<td className='py-2'>{item.nom}</td>
<td className='py-2'>{item.village?.nom || '—'}</td>
</tr>
))}
{!loading && quartiers.length === 0 && (
<tr>
<td className='py-2 text-gray-500' colSpan={2}>Aucun quartier pour le moment.</td>
</tr>
)}
</tbody>
</table>
</CardBox>
<CardBox>
<h3 className='text-lg font-semibold mb-4'>Types dusage</h3>
<table className='w-full text-sm'>
<thead>
<tr className='text-left border-b'>
<th className='py-2'>Nom</th>
<th className='py-2'>Tarif</th>
</tr>
</thead>
<tbody>
{typeUsages.map((item) => (
<tr key={item.id} className='border-b last:border-b-0'>
<td className='py-2'>{item.nom}</td>
<td className='py-2'>{item.tarif ?? 0} F</td>
</tr>
))}
{!loading && typeUsages.length === 0 && (
<tr>
<td className='py-2 text-gray-500' colSpan={2}>Aucun type dusage pour le moment.</td>
</tr>
)}
</tbody>
</table>
</CardBox>
</div>
</SectionMain>
</>
);
};
ReferentialsPage.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated permission='READ_VILLAGES'>
{page}
</LayoutAuthenticated>
);
};
export default ReferentialsPage;