diff --git a/packages/backend/src/controllers/demandeSejour/get.js b/packages/backend/src/controllers/demandeSejour/get.js index a1e26bf88..d11836477 100644 --- a/packages/backend/src/controllers/demandeSejour/get.js +++ b/packages/backend/src/controllers/demandeSejour/get.js @@ -30,7 +30,7 @@ module.exports = async function get(req, res, next) { total: demandes.total, }); } catch (error) { - console.log(error); + log.w(error); log.w("DONE with error"); return next(error); } diff --git a/packages/backend/src/controllers/territoire/list.js b/packages/backend/src/controllers/territoire/list.js index 9cfe767ce..9731bab3c 100644 --- a/packages/backend/src/controllers/territoire/list.js +++ b/packages/backend/src/controllers/territoire/list.js @@ -1,12 +1,20 @@ const Territoire = require("../../services/Territoire"); - const logger = require("../../utils/logger"); const log = logger(module.filename); -module.exports = async function list(_req, res) { +module.exports = async function list(req, res, next) { log.i("IN"); - const territoires = await Territoire.fetch(); - log.i("DONE"); - return res.json({ territoires }); + try { + const territoires = await Territoire.fetch(req.query); + log.d(territoires); + return res.status(200).json({ + territoires: territoires.rows, + total: territoires.total, + }); + } catch (error) { + log.w(error); + log.w("DONE with error"); + return next(error); + } }; diff --git a/packages/backend/src/helpers/__tests__/queryParams.test.js b/packages/backend/src/helpers/__tests__/queryParams.test.js index f2b8995bf..8a583f127 100644 --- a/packages/backend/src/helpers/__tests__/queryParams.test.js +++ b/packages/backend/src/helpers/__tests__/queryParams.test.js @@ -10,14 +10,15 @@ describe("queryParams", () => { it("should return the original query and params if no filters are provided", () => { const query = "SELECT * FROM users"; const result = applyFilters(query, [], {}); - expect(result.query).toBe(query); + expect(result.query.trim()).toBe(query); expect(result.params).toEqual([]); }); it("should apply filters to the query", () => { const query = "SELECT * FROM users"; const filterParams = { age: "30", name: "John" }; - const result = applyFilters(query, [], filterParams); + const groupBy = ""; + const result = applyFilters(query, [], filterParams, groupBy); // remove return at line to fix space pb expect(result.query.replace(/\s+/g, " ").trim()).toContain( "unaccent(age::text) ILIKE '%' || unaccent($1) || '%'", @@ -31,14 +32,15 @@ describe("queryParams", () => { it("should handle array filters with ANY", () => { const query = "SELECT * FROM users"; const filterParams = { roles: ["admin", "user"] }; - const result = applyFilters(query, [], filterParams); + const groupBy = ""; + const result = applyFilters(query, [], filterParams, groupBy); expect(result.query).toContain("roles = ANY($1)"); expect(result.params).toEqual([["admin", "user"]]); }); it("should return the original query if no parameters are provided", () => { const result = applyFilters(); - expect(result.query).toBe(""); + expect(result.query.trim()).toBe(""); expect(result.params).toEqual([]); }); diff --git a/packages/backend/src/helpers/queryParams.js b/packages/backend/src/helpers/queryParams.js index f90229efe..dcf951243 100644 --- a/packages/backend/src/helpers/queryParams.js +++ b/packages/backend/src/helpers/queryParams.js @@ -4,7 +4,7 @@ module.exports = { if (Object.keys(filterParams).length === 0) { return { params: initialParams, - query, + query: `${query}`, }; } const filters = Object.entries(filterParams) @@ -21,6 +21,19 @@ module.exports = { query: `${query} AND ${filters}`, }; }, + applyGroupBy: (queryInitial, groupByParams = []) => { + if (Object.keys(groupByParams).length === 0) { + return `${queryInitial}`; + } + console.log("groupByParams", groupByParams); + const group = Object.entries(groupByParams) + .map(([key, value]) => { + return value; + }) + .join(", "); + return `${queryInitial} GROUP BY ${group}`; + }, + applyPagination: ( query, params, @@ -39,7 +52,6 @@ module.exports = { const countQuery = ` SELECT COUNT(*) AS total FROM (${query}) AS subquery; `; - return { countQuery, countQueryParams: params, @@ -47,7 +59,7 @@ module.exports = { query: paginatedQuery, }; }, - sanityzeFiltersParams: (queryParams, availableParams) => + sanityzeFiltersParams: (queryParams, availableParams) => Object.entries(availableParams).reduce((acc, [key, value]) => { if (queryParams[key]) { acc[value] = queryParams[key]; diff --git a/packages/backend/src/services/Territoire.js b/packages/backend/src/services/Territoire.js index 4f451bdaa..949c3caae 100644 --- a/packages/backend/src/services/Territoire.js +++ b/packages/backend/src/services/Territoire.js @@ -3,8 +3,20 @@ const logger = require("../utils/logger"); const pool = require("../utils/pgpool").getPool(); const log = logger(module.filename); +const { + sanityzePaginationParams, + sanityzeFiltersParams, + applyFilters, + applyPagination, + applyGroupBy, +} = require("../helpers/queryParams"); const query = { + getFicheIdByTerCode: ` + select + fte.id AS id + FROM back.fiche_territoire fte + WHERE ter_code = $1`, getOne: ` select fte.id AS territoire_id, @@ -23,31 +35,25 @@ const query = { FROM back.fiche_territoire fte INNER JOIN geo.territoires ter ON fte.ter_code = ter.code WHERE fte.id = $1`, - getFicheIdByTerCode: ` - select - fte.id AS id - FROM back.fiche_territoire fte - WHERE ter_code = $1`, - select: ` + select: () => + ` select - fte.id AS territoire_id, + fte.id AS "territoireId", CASE (ter.code ~ '[0-9]') WHEN true THEN 'DEP' ELSE 'REG' END AS type, - ter.code AS value, - ter.label AS text, - fte.service_telephone AS service_telephone, - fte.corresp_vao_nom AS corresp_vao_nom, - fte.corresp_vao_prenom AS corresp_vao_prenom, - fte.service_mail as service_mail, - COUNT(distinct(usr.id)) as nbusersbo + ter.code AS code, + ter.label AS label, + fte.corresp_vao_prenom AS "correspVaoPrenom", + fte.corresp_vao_nom AS "correspVaoNom", + fte.service_mail AS "serviceMail", + fte.service_telephone AS "serviceTelephone", + COUNT(distinct(usr.id)) as "nbUsersBo" FROM geo.territoires ter INNER JOIN back.fiche_territoire fte ON fte.ter_code = ter.code LEFT JOIN back.users usr ON usr.ter_code = ter.code - WHERE code <> 'FRA' - GROUP BY territoire_id,type,value,text,service_telephone,corresp_vao_nom,corresp_vao_prenom,service_mail - ORDER BY type, text ASC`, + WHERE ter.code <> 'FRA'`, update: ` UPDATE back.fiche_territoire SET @@ -62,17 +68,51 @@ const query = { `, }; -module.exports.fetch = async (criterias = {}) => { - log.i("fetch - IN"); - const { rows } = await pool.query(query.select); +module.exports.fetch = async (queryParams) => { + const titles = { + label: "ter.label", + code: "ter.code", + }; + const groupBy = [ + "fte.id", + "type", + "code", + "label", + "fte.service_telephone", + "fte.corresp_vao_nom", + "fte.corresp_vao_prenom", + "fte.service_mail", + ]; - return rows.filter((territoire) => { - return Object.entries(criterias).every( - ([key, value]) => territoire[key] == value, - ); - }); -}; + const { limit, offset, sortBy, sortDirection } = sanityzePaginationParams( + queryParams, + { + sortBy: titles, + }, + ); + const filterParams = sanityzeFiltersParams(queryParams, titles); + const queryGet = query.select(); + const filterQuery = applyFilters(queryGet, [], filterParams, groupBy); + filterQuery.query = applyGroupBy(filterQuery.query, groupBy); + const paginatedQuery = applyPagination( + filterQuery.query, + filterQuery.params, + limit, + offset, + sortBy, + sortDirection, + ); + console.log("paginatedQuery", paginatedQuery.query); + const result = await Promise.all([ + pool.query(paginatedQuery.query, paginatedQuery.params), + pool.query(paginatedQuery.countQuery, paginatedQuery.countQueryParams), + ]); + return { + rows: result[0].rows, + total: parseInt(result[1].rows[0].total, 10), + }; +}; module.exports.readFicheIdByTerCode = async (territoireCode) => { log.i("readFicheIdByTerCode - IN"); diff --git a/packages/frontend-bo/src/components/demandes-sejour/DefaultTable.vue b/packages/frontend-bo/src/components/demandes-sejour/DefaultTable.vue index c5adfc4ad..81440f8db 100644 --- a/packages/frontend-bo/src/components/demandes-sejour/DefaultTable.vue +++ b/packages/frontend-bo/src/components/demandes-sejour/DefaultTable.vue @@ -174,7 +174,7 @@ const updateData = () => { ...(isValidParams(sortDirection.value) ? { sortDirection: sortDirection.value.toUpperCase() } : {}), - search: getSearchParams(), + ...getSearchParams(), }; demandeSejourStore.fetchDemandes(query); diff --git a/packages/frontend-bo/src/components/territoires/DefaultTable.vue b/packages/frontend-bo/src/components/territoires/DefaultTable.vue new file mode 100644 index 000000000..fa7f648a1 --- /dev/null +++ b/packages/frontend-bo/src/components/territoires/DefaultTable.vue @@ -0,0 +1,178 @@ + + + diff --git a/packages/frontend-bo/src/components/territoires/DefaultTableFilters.vue b/packages/frontend-bo/src/components/territoires/DefaultTableFilters.vue new file mode 100644 index 000000000..57690241f --- /dev/null +++ b/packages/frontend-bo/src/components/territoires/DefaultTableFilters.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/frontend-bo/src/pages/territoires/liste.vue b/packages/frontend-bo/src/pages/territoires/liste.vue index c8bfe1779..2b2725b93 100644 --- a/packages/frontend-bo/src/pages/territoires/liste.vue +++ b/packages/frontend-bo/src/pages/territoires/liste.vue @@ -1,98 +1,12 @@ diff --git a/packages/frontend-bo/src/stores/territoire.js b/packages/frontend-bo/src/stores/territoire.js index ddec36f5e..95f677090 100644 --- a/packages/frontend-bo/src/stores/territoire.js +++ b/packages/frontend-bo/src/stores/territoire.js @@ -7,34 +7,28 @@ export const useTerritoireStore = defineStore("territoire", { state: () => ({ territoires: [], territoire: {}, + total: null, }), actions: { - async fetch() { + async fetch(params) { log.i("fetch - IN"); try { - const { territoires } = await $fetchBackend("/territoire/list", { + const { territoires, total } = await $fetchBackend("/territoire/list", { method: "GET", credentials: "include", + params, }); if (territoires) { - this.territoires = territoires.map((territoire) => ({ - territoireId: territoire.territoire_id, - text: territoire.text, - value: territoire.value, - parent: territoire.parent, - typeTerritoire: territoire.type, - serviceMail: territoire.service_mail, - serviceTelephone: territoire.service_telephone, - correspVaoNom: territoire.corresp_vao_nom, - correspVaoPrenom: territoire.corresp_vao_prenom, - nbUsersBO: territoire.nbusersbo, - })); + log.i("fetch - DONE"); + this.territoires = territoires; + this.total = total; } } catch (err) { log.w("fetch - DONE with error", err); + this.territoires = []; + this.total = 0; throw err; } - log.i("fetch - DONE"); }, async get(idTerritoire) { log.i("get - IN");