Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65dc763357 |
0
.perm_test_apache
Normal file
0
.perm_test_apache
Normal file
0
.perm_test_exec
Normal file
0
.perm_test_exec
Normal file
BIN
assets/pasted-20260329-051144-5bfe6900.png
Normal file
BIN
assets/pasted-20260329-051144-5bfe6900.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 168 KiB |
211
backend/src/db/api/quartiers.js
Normal file
211
backend/src/db/api/quartiers.js
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
207
backend/src/db/api/type_usages.js
Normal file
207
backend/src/db/api/type_usages.js
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
226
backend/src/db/api/villages.js
Normal file
226
backend/src/db/api/villages.js
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,214 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
50
backend/src/db/models/quartiers.js
Normal file
50
backend/src/db/models/quartiers.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
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;
|
||||||
|
};
|
||||||
51
backend/src/db/models/type_usages.js
Normal file
51
backend/src/db/models/type_usages.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
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;
|
||||||
|
};
|
||||||
62
backend/src/db/models/villages.js
Normal file
62
backend/src/db/models/villages.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
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;
|
||||||
|
};
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -36,6 +36,9 @@ 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) => {
|
||||||
@ -110,6 +113,9 @@ 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',
|
||||||
|
|||||||
78
backend/src/routes/quartiers.js
Normal file
78
backend/src/routes/quartiers.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
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;
|
||||||
78
backend/src/routes/type_usages.js
Normal file
78
backend/src/routes/type_usages.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
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;
|
||||||
78
backend/src/routes/villages.js
Normal file
78
backend/src/routes/villages.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
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;
|
||||||
111
backend/src/services/quartiers.js
Normal file
111
backend/src/services/quartiers.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
111
backend/src/services/type_usages.js
Normal file
111
backend/src/services/type_usages.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
111
backend/src/services/villages.js
Normal file
111
backend/src/services/villages.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
BIN
frontend/public/assets/vm-shot-2026-03-29T05-06-17-032Z.jpg
Normal file
BIN
frontend/public/assets/vm-shot-2026-03-29T05-06-17-032Z.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@ -1,6 +1,5 @@
|
|||||||
import React, {useEffect, useRef} from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useState } from 'react'
|
|
||||||
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
||||||
import BaseDivider from './BaseDivider'
|
import BaseDivider from './BaseDivider'
|
||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import React, { ReactNode, useEffect } from 'react'
|
import React, { ReactNode, useEffect, useState } 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'
|
||||||
|
|||||||
@ -72,6 +72,14 @@ 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',
|
||||||
|
|||||||
191
frontend/src/pages/g-forage/referentials.tsx
Normal file
191
frontend/src/pages/g-forage/referentials.tsx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
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 d’usage</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 d’usage</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 d’usage 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;
|
||||||
Loading…
x
Reference in New Issue
Block a user