From 8d30b1ebe785f98fad5adffc1e143aa9f681a636 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 12 Sep 2024 13:44:00 -0500 Subject: [PATCH 1/3] add isImported And categories to project tab --- src/server/adminJs/tabs/projectsTab.ts | 79 +++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/src/server/adminJs/tabs/projectsTab.ts b/src/server/adminJs/tabs/projectsTab.ts index d0cf6fbe2..fdbbcab40 100644 --- a/src/server/adminJs/tabs/projectsTab.ts +++ b/src/server/adminJs/tabs/projectsTab.ts @@ -55,6 +55,7 @@ import { User } from '../../../entities/user'; import { refreshProjectEstimatedMatchingView } from '../../../services/projectViewsService'; import { extractAdminJsReferrerUrlParams } from '../adminJs'; import { relateManyProjectsToQfRound } from '../../../repositories/qfRoundRepository2'; +import { Category } from '../../../entities/category'; // add queries depending on which filters were selected export const buildProjectsQuery = ( @@ -486,6 +487,15 @@ export const fillSocialProfileAndQfRounds: After< const projectUpdates = await findProjectUpdatesByProjectId(projectId); const project = await findProjectById(projectId); const adminJsBaseUrl = process.env.SERVER_URL; + let categories; + if (project) { + const categoryIds = project!.categories.map(cat => cat.id); + categories = await Category + .createQueryBuilder('category') + .where('category.id IN (:...ids)', { ids: categoryIds }) + .orderBy('category.name', 'ASC') + .getMany(); + } response.record = { ...record, params: { @@ -499,6 +509,10 @@ export const fillSocialProfileAndQfRounds: After< adminJsBaseUrl, }, }; + + if (categories) { + response.record.params.categories = categories.map(cat => `${cat.id} - ${cat.name}`); + } return response; }; @@ -660,7 +674,7 @@ export const projectsTab = { id: { isVisible: { list: false, - filter: false, + filter: true, show: true, edit: false, }, @@ -831,12 +845,34 @@ export const projectsTab = { edit: false, }, }, + categoryIds: { + type: 'reference', + isArray: true, + reference: 'Category', + isVisible: { + list: false, + filter: false, + show: true, + edit: true, + }, + availableValues: async (_record) => { + const categories = await Category + .createQueryBuilder('category') + .where('category.isActive = :isActive', { isActive: true }) + .orderBy('category.name', 'ASC') + .getMany(); + return categories.map(category => ({ + value: category.id, + label: `${category.id} - ${category.name}`, + })); + }, + }, isImported: { isVisible: { list: false, filter: true, show: true, - edit: false, + edit: true, }, }, totalReactions: { @@ -924,6 +960,19 @@ export const projectsTab = { isVisible: false, isAccessible: ({ currentAdmin }) => canAccessProjectAction({ currentAdmin }, ResourceActions.NEW), + before: async (request) => { + if (request.payload.categories) { + request.payload.categories = (request.payload.categories as string[]).map(id => ({ id: parseInt(id, 10) })); + } + return request; + }, + after: async (response) => { + const { record, request } = response; + if (request.payload.categoryIds) { + await saveCategories(record.params.id, request.payload.categoryIds); + } + return response; + }, }, bulkDelete: { isVisible: false, @@ -1014,6 +1063,9 @@ export const projectsTab = { // We put these status changes in payload, so in after hook we would know to send notification for users request.payload.statusChanges = statusChanges.join(','); } + if (request.payload.categories) { + request.payload.categories = (request.payload.categories as string[]).map(id => ({ id: parseInt(id, 10) })); + } return request; }, after: async ( @@ -1155,6 +1207,7 @@ export const projectsTab = { refreshUserProjectPowerView(), refreshProjectFuturePowerView(), refreshProjectPowerView(), + saveCategories(project!.id, request?.payload?.categoryIds || []) ]); return request; }, @@ -1350,3 +1403,25 @@ export const projectsTab = { }, }, }; + +async function saveCategories(projectId: number, categoryIds: string[]) { + if (categoryIds?.length === 0) return; + + const project = await Project + .createQueryBuilder('project') + .leftJoinAndSelect('project.categories', 'category') + .where('project.id = :id', { id: projectId }) + .getOne(); + + if (!project) { + throw new Error('Project not found'); + } + + const categories = await Category + .createQueryBuilder('category') + .where('category.id IN (:...ids)', { ids: categoryIds }) + .getMany(); + + project.categories = categories; + await project.save(); +} From 3d10bbf1976107b36ba04524e8a7f3d4ad9462dd Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 18 Sep 2024 01:58:40 -0500 Subject: [PATCH 2/3] add categories to show and edit forms in adminjs for projects --- src/entities/project.ts | 1 + .../tabs/components/ProjectCategories.tsx | 28 +++++++++ src/server/adminJs/tabs/projectsTab.ts | 61 ++++++++++++------- 3 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 src/server/adminJs/tabs/components/ProjectCategories.tsx diff --git a/src/entities/project.ts b/src/entities/project.ts index c0b02bcf6..ae768c263 100644 --- a/src/entities/project.ts +++ b/src/entities/project.ts @@ -217,6 +217,7 @@ export class Project extends BaseEntity { @Field(_type => [Category], { nullable: true }) @ManyToMany(_type => Category, category => category.projects, { nullable: true, + eager: true, }) @JoinTable() categories: Category[]; diff --git a/src/server/adminJs/tabs/components/ProjectCategories.tsx b/src/server/adminJs/tabs/components/ProjectCategories.tsx new file mode 100644 index 000000000..27a6e8893 --- /dev/null +++ b/src/server/adminJs/tabs/components/ProjectCategories.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { withTheme } from 'styled-components'; +import { Section, Label } from '@adminjs/design-system'; + +const ProjectUpdates = props => { + const categories = props?.record?.params?.categories; + return ( +
+ +
+ {categories?.map(category => { + return ( +
+
+
+ +

{category.name || ''} - Id: {category.id}

+
+
+ ); + })} +
+
+
+ ); +}; + +export default withTheme(ProjectUpdates); diff --git a/src/server/adminJs/tabs/projectsTab.ts b/src/server/adminJs/tabs/projectsTab.ts index fdbbcab40..3e8ad00dd 100644 --- a/src/server/adminJs/tabs/projectsTab.ts +++ b/src/server/adminJs/tabs/projectsTab.ts @@ -447,6 +447,13 @@ export const addProjectsToQfRound = async ( }; }; + export const extractCategoryIds = (payload: any) => { + if (!payload) return; + return Object.keys(payload) + .filter(key => key.startsWith('categoryIds.')) + .map(key => payload[key]); +} + export const addSingleProjectToQfRound = async ( context: AdminJsContextInterface, request: AdminJsRequestInterface, @@ -489,10 +496,10 @@ export const fillSocialProfileAndQfRounds: After< const adminJsBaseUrl = process.env.SERVER_URL; let categories; if (project) { - const categoryIds = project!.categories.map(cat => cat.id); categories = await Category .createQueryBuilder('category') - .where('category.id IN (:...ids)', { ids: categoryIds }) + .innerJoin('category.projects', 'projects') + .where('projects.id = :id', { id: project.id }) .orderBy('category.name', 'ASC') .getMany(); } @@ -511,7 +518,8 @@ export const fillSocialProfileAndQfRounds: After< }; if (categories) { - response.record.params.categories = categories.map(cat => `${cat.id} - ${cat.name}`); + response.record.params.categoryIds = categories; + response.record.params.categories = categories; } return response; }; @@ -855,6 +863,9 @@ export const projectsTab = { show: true, edit: true, }, + components: { + show: adminJs.bundle('./components/ProjectCategories'), + }, availableValues: async (_record) => { const categories = await Category .createQueryBuilder('category') @@ -968,9 +979,12 @@ export const projectsTab = { }, after: async (response) => { const { record, request } = response; - if (request.payload.categoryIds) { - await saveCategories(record.params.id, request.payload.categoryIds); - } + const project = await Project.findOne({ + where: { id: request?.record?.id }, + }); + const categoryIds = extractCategoryIds(request.record.params); + await saveCategories(project!, categoryIds || []); + return response; }, }, @@ -1001,6 +1015,13 @@ export const projectsTab = { } const project = await findProjectById(Number(request.payload.id)); + if (project) { + await Category.query(` + DELETE FROM project_categories_category + WHERE "projectId" = $1 + `, [project.id]); + } + if ( project && Number(request?.payload?.statusId) !== project?.status?.id @@ -1063,9 +1084,7 @@ export const projectsTab = { // We put these status changes in payload, so in after hook we would know to send notification for users request.payload.statusChanges = statusChanges.join(','); } - if (request.payload.categories) { - request.payload.categories = (request.payload.categories as string[]).map(id => ({ id: parseInt(id, 10) })); - } + return request; }, after: async ( @@ -1203,11 +1222,13 @@ export const projectsTab = { }); } } + const categoryIds = extractCategoryIds(request.record.params); + await Promise.all([ refreshUserProjectPowerView(), refreshProjectFuturePowerView(), refreshProjectPowerView(), - saveCategories(project!.id, request?.payload?.categoryIds || []) + saveCategories(project!, categoryIds || []), ]); return request; }, @@ -1404,18 +1425,14 @@ export const projectsTab = { }, }; -async function saveCategories(projectId: number, categoryIds: string[]) { - if (categoryIds?.length === 0) return; - - const project = await Project - .createQueryBuilder('project') - .leftJoinAndSelect('project.categories', 'category') - .where('project.id = :id', { id: projectId }) - .getOne(); - - if (!project) { - throw new Error('Project not found'); - } +async function saveCategories(project: Project, categoryIds?: string[]) { + if (!project) return; + if (!categoryIds || categoryIds?.length === 0) return; + + await Category.query(` + DELETE FROM project_categories_category + WHERE "projectId" = $1 + `, [project.id]); const categories = await Category .createQueryBuilder('category') From 4b8d3082933aa9d2483d561a4f4018e3a684eeba Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 18 Sep 2024 19:17:28 -0500 Subject: [PATCH 3/3] fix eslint --- .../CustomQfRoundMultiUpdateComponent.tsx | 4 +- .../tabs/components/ProjectCategories.tsx | 4 +- src/server/adminJs/tabs/projectsTab.ts | 63 ++++++++++--------- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/server/adminJs/tabs/components/CustomQfRoundMultiUpdateComponent.tsx b/src/server/adminJs/tabs/components/CustomQfRoundMultiUpdateComponent.tsx index 644d6ad82..c86aa78ac 100644 --- a/src/server/adminJs/tabs/components/CustomQfRoundMultiUpdateComponent.tsx +++ b/src/server/adminJs/tabs/components/CustomQfRoundMultiUpdateComponent.tsx @@ -26,9 +26,7 @@ const RecordInput = ({ index, record, updateRecord, removeRecord }) => ( - updateRecord(index, 'matchingFund', e.target.value) - } + onChange={e => updateRecord(index, 'matchingFund', e.target.value)} required /> diff --git a/src/server/adminJs/tabs/components/ProjectCategories.tsx b/src/server/adminJs/tabs/components/ProjectCategories.tsx index 27a6e8893..5706131cb 100644 --- a/src/server/adminJs/tabs/components/ProjectCategories.tsx +++ b/src/server/adminJs/tabs/components/ProjectCategories.tsx @@ -14,7 +14,9 @@ const ProjectUpdates = props => {
-

{category.name || ''} - Id: {category.id}

+

+ {category.name || ''} - Id: {category.id} +

); diff --git a/src/server/adminJs/tabs/projectsTab.ts b/src/server/adminJs/tabs/projectsTab.ts index 3e8ad00dd..a61aedfa6 100644 --- a/src/server/adminJs/tabs/projectsTab.ts +++ b/src/server/adminJs/tabs/projectsTab.ts @@ -447,12 +447,12 @@ export const addProjectsToQfRound = async ( }; }; - export const extractCategoryIds = (payload: any) => { - if (!payload) return; - return Object.keys(payload) - .filter(key => key.startsWith('categoryIds.')) - .map(key => payload[key]); -} +export const extractCategoryIds = (payload: any) => { + if (!payload) return; + return Object.keys(payload) + .filter(key => key.startsWith('categoryIds.')) + .map(key => payload[key]); +}; export const addSingleProjectToQfRound = async ( context: AdminJsContextInterface, @@ -496,12 +496,11 @@ export const fillSocialProfileAndQfRounds: After< const adminJsBaseUrl = process.env.SERVER_URL; let categories; if (project) { - categories = await Category - .createQueryBuilder('category') - .innerJoin('category.projects', 'projects') - .where('projects.id = :id', { id: project.id }) - .orderBy('category.name', 'ASC') - .getMany(); + categories = await Category.createQueryBuilder('category') + .innerJoin('category.projects', 'projects') + .where('projects.id = :id', { id: project.id }) + .orderBy('category.name', 'ASC') + .getMany(); } response.record = { ...record, @@ -866,12 +865,11 @@ export const projectsTab = { components: { show: adminJs.bundle('./components/ProjectCategories'), }, - availableValues: async (_record) => { - const categories = await Category - .createQueryBuilder('category') + availableValues: async _record => { + const categories = await Category.createQueryBuilder('category') .where('category.isActive = :isActive', { isActive: true }) .orderBy('category.name', 'ASC') - .getMany(); + .getMany(); return categories.map(category => ({ value: category.id, label: `${category.id} - ${category.name}`, @@ -971,18 +969,20 @@ export const projectsTab = { isVisible: false, isAccessible: ({ currentAdmin }) => canAccessProjectAction({ currentAdmin }, ResourceActions.NEW), - before: async (request) => { + before: async request => { if (request.payload.categories) { - request.payload.categories = (request.payload.categories as string[]).map(id => ({ id: parseInt(id, 10) })); + request.payload.categories = ( + request.payload.categories as string[] + ).map(id => ({ id: parseInt(id, 10) })); } return request; }, - after: async (response) => { - const { record, request } = response; + after: async response => { + const { request } = response; const project = await Project.findOne({ where: { id: request?.record?.id }, }); - const categoryIds = extractCategoryIds(request.record.params); + const categoryIds = extractCategoryIds(request.record.params); await saveCategories(project!, categoryIds || []); return response; @@ -1016,10 +1016,13 @@ export const projectsTab = { const project = await findProjectById(Number(request.payload.id)); if (project) { - await Category.query(` + await Category.query( + ` DELETE FROM project_categories_category WHERE "projectId" = $1 - `, [project.id]); + `, + [project.id], + ); } if ( @@ -1084,7 +1087,7 @@ export const projectsTab = { // We put these status changes in payload, so in after hook we would know to send notification for users request.payload.statusChanges = statusChanges.join(','); } - + return request; }, after: async ( @@ -1222,7 +1225,7 @@ export const projectsTab = { }); } } - const categoryIds = extractCategoryIds(request.record.params); + const categoryIds = extractCategoryIds(request.record.params); await Promise.all([ refreshUserProjectPowerView(), @@ -1429,13 +1432,15 @@ async function saveCategories(project: Project, categoryIds?: string[]) { if (!project) return; if (!categoryIds || categoryIds?.length === 0) return; - await Category.query(` + await Category.query( + ` DELETE FROM project_categories_category WHERE "projectId" = $1 - `, [project.id]); + `, + [project.id], + ); - const categories = await Category - .createQueryBuilder('category') + const categories = await Category.createQueryBuilder('category') .where('category.id IN (:...ids)', { ids: categoryIds }) .getMany();