From 141d971c11401905e8560936d78a385c9e43c728 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:38:24 +0530 Subject: [PATCH 01/66] Mitigation Timeline Calculation logic fixed --- .../src/validation/payload.validator.ts | 28 +++---- .../Activities/ActivityForm/activityForm.tsx | 74 +++++++++---------- web/src/Utils/utilServices.tsx | 17 +++++ 3 files changed, 63 insertions(+), 56 deletions(-) diff --git a/backend/services/src/validation/payload.validator.ts b/backend/services/src/validation/payload.validator.ts index bd0ef518..ed1c4fc0 100644 --- a/backend/services/src/validation/payload.validator.ts +++ b/backend/services/src/validation/payload.validator.ts @@ -46,8 +46,8 @@ export class PayloadValidator { throw new HttpException('Mitigation timeline Expected data is missing', HttpStatus.BAD_REQUEST); } else { this.validateArrayAndLength(expected, expectedMitigationTimelineProperties, startYear); - this.validate_ktCO2e_Values(expected.activityEmissionsWithM,expected.expectedEmissionReductWithM, gwpValue,'expectedEmissionReductWithM'); - this.validate_ktCO2e_Values(expected.activityEmissionsWithAM,expected.expectedEmissionReductWithAM, gwpValue,'expectedEmissionReductWithM'); + this.validate_ktCO2e_Values(expected.expectedEmissionReductWithM,expected.baselineEmissions, expected.activityEmissionsWithM, gwpValue,'expectedEmissionReductWithM'); + this.validate_ktCO2e_Values(expected.expectedEmissionReductWithAM,expected.baselineEmissions, expected.activityEmissionsWithAM, gwpValue,'expectedEmissionReductWithAM'); if (!expected.total) { throw new HttpException('Mitigation timeline Expected total data is missing', HttpStatus.BAD_REQUEST); } else { @@ -60,7 +60,7 @@ export class PayloadValidator { throw new HttpException('Mitigation timeline Actual data is missing', HttpStatus.BAD_REQUEST); } else { this.validateArrayAndLength(actual, actualMitigationTimelineProperties, startYear); - this.validate_ktCO2e_Values(actual.activityActualEmissions,actual.actualEmissionReduct, gwpValue,'actualEmissionReduct'); + this.validate_ktCO2e_Values(actual.actualEmissionReduct, actual.baselineActualEmissions, actual.activityActualEmissions ,gwpValue,'actualEmissionReduct'); if (!actual.total) { throw new HttpException('Mitigation timeline Actual total data is missing', HttpStatus.BAD_REQUEST); } else { @@ -74,23 +74,19 @@ export class PayloadValidator { for (const propertyName in propertiesEnum) { const property = propertiesEnum[propertyName]; const array = data[propertyName]; - let arraySize = 0; + const arraySize = Math.min(startYear + 30, 2050) - startYear + 1; if (!Array.isArray(array)) { throw new HttpException(`Mitigation timeline ${property} array is missing`, HttpStatus.BAD_REQUEST); } - for (let year = startYear; year <= Math.min(startYear + 30, 2050); year++) { - arraySize++; - } - if (array.length !== arraySize) { throw new HttpException(`${property} data should be an array with exactly ${arraySize} elements`, HttpStatus.BAD_REQUEST); } array.forEach((value, index) => { - if (!Number.isInteger(value) || value < 0) { - throw new HttpException(`Mitigation timeline ${property} array should contain only positive integers. Invalid value at index ${index}: ${value}`, HttpStatus.BAD_REQUEST); + if (!Number.isInteger(value)) { + throw new HttpException(`Mitigation timeline ${property} array should contain only integers. Invalid value at index ${index}: ${value}`, HttpStatus.BAD_REQUEST); } }); } @@ -105,8 +101,8 @@ export class PayloadValidator { throw new HttpException(`Mitigation timeline ${property} total value is missing`, HttpStatus.BAD_REQUEST); } - if (!Number.isInteger(value) || value < 0) { - throw new HttpException(`Mitigation timeline ${property} total value should be a positive integer. Invalid value: ${value}`, HttpStatus.BAD_REQUEST); + if (!Number.isInteger(value)) { + throw new HttpException(`Mitigation timeline ${property} total value should be an integer. Invalid value: ${value}`, HttpStatus.BAD_REQUEST); } } } @@ -124,10 +120,10 @@ export class PayloadValidator { } } - private validate_ktCO2e_Values(arr1: [number], arr2: [number], gwpvalue: number, arr2Name: string) { - for (let index = 0; index < arr1.length; index++) { - if (arr2[index] !== arr1[index] * gwpvalue) - throw new HttpException(`Element ${index + 1} in the ${arr2Name} array is incorrect according to the GWP value.`, HttpStatus.BAD_REQUEST); + private validate_ktCO2e_Values(provided: number[], baseline: number[], reducer: number[], gwp: number, name: string) { + for (let index = 0; index < provided.length; index++) { + if (provided[index] !== (baseline[index] - reducer[index]) * gwp) + throw new HttpException(`Element ${index + 1} in the ${name} array is incorrect according to the GWP value.`, HttpStatus.BAD_REQUEST); } } diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 51bc749b..6c6c448e 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -22,10 +22,12 @@ import { ActivityMigratedData, ParentData } from '../../../Definitions/activityD import { FormLoadProps } from '../../../Definitions/InterfacesAndType/formInterface'; import { getValidationRules } from '../../../Utils/validationRules'; import { + calculateArraySum, delay, doesUserHaveValidatePermission, getFormTitle, getRounded, + subtractTwoArrays, } from '../../../Utils/utilServices'; import { Action } from '../../../Enums/action.enum'; import { ActivityEntity } from '../../../Entities/activity'; @@ -311,7 +313,7 @@ const ActivityForm: React.FC = ({ method }) => { }); } } catch (error) { - console.error('Error fetching GWP values:', error); + console.log('Error fetching GWP values:', error); } }; @@ -802,41 +804,6 @@ const ActivityForm: React.FC = ({ method }) => { } }; - //MTG timeline calculate array sum from row - - const mtgCalculateArraySum = (array: number[]) => { - let arrSum = 0; - for (let index = 0; index <= array.length; index++) { - arrSum += array[index] || 0; - } - return arrSum; - }; - - //Values convert to ktCO2e - - const multiplyByGwpValue = (entry: any) => { - switch (entry.topic) { - case expectedTimeline[1].topic: - for (let index = 0; index < expectedTimeline[1].values.length; index++) { - expectedTimeline[3].values[index] = expectedTimeline[1].values[index] * gwpValue; - expectedTimeline[3].total = mtgCalculateArraySum(expectedTimeline[3].values); - } - break; - case expectedTimeline[2].topic: - for (let index = 0; index < expectedTimeline[2].values.length; index++) { - expectedTimeline[4].values[index] = expectedTimeline[2].values[index] * gwpValue; - expectedTimeline[4].total = mtgCalculateArraySum(expectedTimeline[4].values); - } - break; - case actualTimeline[1].topic: - for (let index = 0; index < actualTimeline[1].values.length; index++) { - actualTimeline[2].values[index] = actualTimeline[1].values[index] * gwpValue; - actualTimeline[2].total = mtgCalculateArraySum(actualTimeline[2].values); - } - break; - } - }; - // Mtg Data Change const onMtgValueEnter = ( @@ -851,23 +818,50 @@ const ActivityForm: React.FC = ({ method }) => { const updatedTimeline = expectedTimeline.map((entry) => { if (entry.topic === rowId) { entry.values[year - mtgStartYear] = newValue; - entry.total = mtgCalculateArraySum(entry.values); - multiplyByGwpValue(entry); + entry.total = calculateArraySum(entry.values); return entry; } return entry; }); + + // Updating calculated values + if (rowId === updatedTimeline[0].topic || rowId === updatedTimeline[1].topic) { + updatedTimeline[3].values = subtractTwoArrays( + updatedTimeline[0].values, + updatedTimeline[1].values, + gwpValue + ); + updatedTimeline[3].total = calculateArraySum(updatedTimeline[3].values); + } + + if (rowId === updatedTimeline[0].topic || rowId === updatedTimeline[2].topic) { + updatedTimeline[4].values = subtractTwoArrays( + updatedTimeline[0].values, + updatedTimeline[2].values, + gwpValue + ); + updatedTimeline[4].total = calculateArraySum(updatedTimeline[4].values); + } + setExpectedTimeline(updatedTimeline); } else { const updatedTimeline = actualTimeline.map((entry) => { if (entry.topic === rowId) { entry.values[year - mtgStartYear] = newValue; - entry.total = mtgCalculateArraySum(entry.values); - multiplyByGwpValue(entry); + entry.total = calculateArraySum(entry.values); return entry; } return entry; }); + + // Updating calculated values + updatedTimeline[2].values = subtractTwoArrays( + updatedTimeline[0].values, + updatedTimeline[1].values, + gwpValue + ); + updatedTimeline[2].total = calculateArraySum(updatedTimeline[2].values); + setActualTimeline(updatedTimeline); } setIsMtgButtonEnabled(true); diff --git a/web/src/Utils/utilServices.tsx b/web/src/Utils/utilServices.tsx index f7fdd7a6..e4fd56f6 100644 --- a/web/src/Utils/utilServices.tsx +++ b/web/src/Utils/utilServices.tsx @@ -198,3 +198,20 @@ export const doesUserHaveValidatePermission = async (get: any): Promise return true; } }; + +export const subtractTwoArrays = ( + array1: number[], + array2: number[], + multiplier?: number +): number[] => { + const processedMultiplier = multiplier ?? 1; + return array1.map((value, index) => (value - array2[index]) * processedMultiplier); +}; + +export const calculateArraySum = (array: number[]) => { + let arrSum = 0; + for (let index = 0; index <= array.length; index++) { + arrSum += array[index] || 0; + } + return arrSum; +}; From 838d799b54cad020ba8409378e100439961e6cf5 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:03:27 +0530 Subject: [PATCH 02/66] Unit update refinement --- web/src/Components/Timeline/timeline.tsx | 3 +- .../Activities/ActivityForm/activityForm.tsx | 156 +++++++++--------- 2 files changed, 79 insertions(+), 80 deletions(-) diff --git a/web/src/Components/Timeline/timeline.tsx b/web/src/Components/Timeline/timeline.tsx index 92f26c20..d1a0ff09 100644 --- a/web/src/Components/Timeline/timeline.tsx +++ b/web/src/Components/Timeline/timeline.tsx @@ -39,6 +39,7 @@ const TimelineTable: React.FC = ({ const screens = useBreakpoint(); const isView: boolean = method === 'view' ? true : false; + const [allowFixedLegend, setAllowFixedLegend] = useState(false); useEffect(() => { @@ -58,7 +59,6 @@ const TimelineTable: React.FC = ({ align: 'center', ellipsis: true, width: 100, - fixed: 'left', }, { title: t('timelineTable:catExpectedEmissionReduct'), @@ -85,7 +85,6 @@ const TimelineTable: React.FC = ({ align: 'center', ellipsis: true, width: 100, - fixed: 'left', }, { title: t('timelineTable:catActualEmissionReduct'), diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 6c6c448e..70625da1 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -142,12 +142,11 @@ const ActivityForm: React.FC = ({ method }) => { const [expectedTimeline, setExpectedTimeline] = useState([]); const [actualTimeline, setActualTimeline] = useState([]); - const [isMtgButtonEnabled, setIsMtgButtonEnabled] = useState(false); const [mtgStartYear, setMtgStartYear] = useState(0); const [selectedGhg, setSelectedGhg] = useState(); + const [isMtgButtonEnabled, setIsMtgButtonEnabled] = useState(false); + const [gwpSettings, setGwpSettings] = useState<{ CH4: number; N2O: number }>(); - const [gwpValue, setGwpValue] = useState(1); - const [mtgUnit, setMtgUnit] = useState(GHGS.CO); // Initialization Logic @@ -502,6 +501,10 @@ const ActivityForm: React.FC = ({ method }) => { } }; fetchCreatedKPIData(); + + // Fetch GWP Settings + + fetchGwpSettings(); }, []); // Populating Form Migrated Fields, when migration data changes @@ -683,72 +686,68 @@ const ActivityForm: React.FC = ({ method }) => { } }; - const gwpValueMapping = () => { - if (selectedGhg !== undefined && gwpSettings !== undefined) { - if (method === 'create') { - switch (selectedGhg) { - case GHGS.CH: - setGwpValue(gwpSettings?.CH4 ?? 1); - setMtgUnit(GHGS.CH); - break; - case GHGS.NO: - setGwpValue(gwpSettings?.N2O ?? 1); - setMtgUnit(GHGS.NO); - break; - default: - setGwpValue(1); - setMtgUnit(GHGS.CO); - break; - } - } else { - switch (mtgUnit) { - case GHGS.CH: - setGwpValue(gwpSettings?.CH4 ?? 1); - break; - case GHGS.NO: - setGwpValue(gwpSettings?.N2O ?? 1); - break; - default: - setGwpValue(1); - break; - } - } + // Find GWP Value + + const findGWP = () => { + switch (selectedGhg) { + case GHGS.CH: + return gwpSettings?.CH4 ?? 1; + case GHGS.NO: + return gwpSettings?.N2O ?? 1; + default: + return 1; } }; - //set mtg timeline data to zero values (when it is or null or create mode) - - const setDefaultTimelineValues = (startYear: number, range: number) => { - const endYear = startYear + range; - const tempExpectedEntries: ExpectedTimeline[] = []; - Object.entries(ExpectedRows).forEach(([key, value]) => { - const expectedGhgValue = - key === 'ROW_ONE' || key === 'ROW_TWO' || key === 'ROW_THREE' ? `kt${mtgUnit}` : value[0]; - const rowData: ExpectedTimeline = { - key: key, - ghg: expectedGhgValue, - topic: value[1], - total: 0, - values: new Array(Math.min(endYear, 2050) + 1 - startYear).fill(0), - }; - tempExpectedEntries.push(rowData); - }); + // Find MTG Unit - const tempActualEntries: ActualTimeline[] = []; - Object.entries(ActualRows).forEach(([key, value]) => { - const actualGhgValue = key === 'ROW_ONE' || key === 'ROW_TWO' ? `kt${mtgUnit}` : value[0]; - const rowData: ActualTimeline = { - key: key, - ghg: actualGhgValue, - topic: value[1], - total: 0, - values: new Array(Math.min(endYear, 2050) + 1 - startYear).fill(0), - }; - tempActualEntries.push(rowData); - }); + const findUnit = (ghg: GHGS | undefined) => { + if (ghg) { + const mtgUnit = [GHGS.CH, GHGS.NO].includes(ghg) ? ghg : GHGS.CO; + return mtgUnit; + } else { + return GHGS.CO; + } + }; + + // Set mtg timeline data to default values + + const setDefaultTimelineValues = () => { + if (mtgStartYear && selectedGhg) { + const endYear = mtgStartYear + mtgRange; + const mtgUnit = findUnit(selectedGhg); + + const tempExpectedEntries: ExpectedTimeline[] = []; + const tempActualEntries: ActualTimeline[] = []; + + Object.entries(ExpectedRows).forEach(([key, value]) => { + const expectedGhgValue = + key === 'ROW_ONE' || key === 'ROW_TWO' || key === 'ROW_THREE' ? `kt${mtgUnit}` : value[0]; + const rowData: ExpectedTimeline = { + key: key, + ghg: expectedGhgValue, + topic: value[1], + total: 0, + values: new Array(Math.min(endYear, 2050) + 1 - mtgStartYear).fill(0), + }; + tempExpectedEntries.push(rowData); + }); + + Object.entries(ActualRows).forEach(([key, value]) => { + const actualGhgValue = key === 'ROW_ONE' || key === 'ROW_TWO' ? `kt${mtgUnit}` : value[0]; + const rowData: ActualTimeline = { + key: key, + ghg: actualGhgValue, + topic: value[1], + total: 0, + values: new Array(Math.min(endYear, 2050) + 1 - mtgStartYear).fill(0), + }; + tempActualEntries.push(rowData); + }); - setExpectedTimeline(tempExpectedEntries); - setActualTimeline(tempActualEntries); + setExpectedTimeline(tempExpectedEntries); + setActualTimeline(tempActualEntries); + } }; // Get mtg timeline data @@ -761,8 +760,12 @@ const ActivityForm: React.FC = ({ method }) => { if (response.status === 200 || response.status === 201) { setMtgStartYear(response.data.startYear); - setMtgUnit(response.data.unit); + + const mtgUnit = response.data.unit; + const tempExpectedEntries: ExpectedTimeline[] = []; + const tempActualEntries: ActualTimeline[] = []; + Object.entries(ExpectedRows).forEach(([key, value]) => { const expectedGhgValue = key === 'ROW_ONE' || key === 'ROW_TWO' || key === 'ROW_THREE' @@ -778,7 +781,6 @@ const ActivityForm: React.FC = ({ method }) => { tempExpectedEntries.push(rowData); }); - const tempActualEntries: ActualTimeline[] = []; Object.entries(ActualRows).forEach(([key, value]) => { const actualGhgValue = key === 'ROW_ONE' || key === 'ROW_TWO' ? `kt${mtgUnit}` : value[0]; @@ -795,11 +797,11 @@ const ActivityForm: React.FC = ({ method }) => { setExpectedTimeline(tempExpectedEntries); setActualTimeline(tempActualEntries); } else { - setDefaultTimelineValues(mtgStartYear, mtgRange); + setDefaultTimelineValues(); } } catch (error) { console.error('Error fetching timeline data:', error); - setDefaultTimelineValues(mtgStartYear, mtgRange); + setDefaultTimelineValues(); } } }; @@ -829,7 +831,7 @@ const ActivityForm: React.FC = ({ method }) => { updatedTimeline[3].values = subtractTwoArrays( updatedTimeline[0].values, updatedTimeline[1].values, - gwpValue + findGWP() ); updatedTimeline[3].total = calculateArraySum(updatedTimeline[3].values); } @@ -838,7 +840,7 @@ const ActivityForm: React.FC = ({ method }) => { updatedTimeline[4].values = subtractTwoArrays( updatedTimeline[0].values, updatedTimeline[2].values, - gwpValue + findGWP() ); updatedTimeline[4].total = calculateArraySum(updatedTimeline[4].values); } @@ -858,7 +860,7 @@ const ActivityForm: React.FC = ({ method }) => { updatedTimeline[2].values = subtractTwoArrays( updatedTimeline[0].values, updatedTimeline[1].values, - gwpValue + findGWP() ); updatedTimeline[2].total = calculateArraySum(updatedTimeline[2].values); @@ -922,14 +924,12 @@ const ActivityForm: React.FC = ({ method }) => { // Initializing mtg timeline data useEffect(() => { - fetchGwpSettings(); if (method === 'create') { - setDefaultTimelineValues(mtgStartYear, mtgRange); + setDefaultTimelineValues(); } else { fetchMtgTimelineData(); } - gwpValueMapping(); - }, [mtgStartYear, selectedGhg, mtgUnit]); + }, [mtgStartYear, selectedGhg]); // Form Submit @@ -1009,7 +1009,7 @@ const ActivityForm: React.FC = ({ method }) => { }, }, startYear: mtgStartYear, - unit: mtgUnit, + unit: findUnit(selectedGhg), }; } @@ -1731,7 +1731,7 @@ const ActivityForm: React.FC = ({ method }) => { - {mtgStartYear !== 0 && mtgStartYear && mtgUnit && ( + {mtgStartYear && selectedGhg ? (
@@ -1768,7 +1768,7 @@ const ActivityForm: React.FC = ({ method }) => {
- )} + ) : null} {method !== 'create' && (
{t('formHeader:updatesInfoTitle')}
From 0801b0622b62d72b3a246e6d2b0ce654d30db9c9 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:36:41 +0530 Subject: [PATCH 03/66] Unit fix for non CO2, CH4 or NO2 Gasses --- web/src/Enums/shared.enum.ts | 1 + .../Pages/Activities/ActivityForm/activityForm.tsx | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/web/src/Enums/shared.enum.ts b/web/src/Enums/shared.enum.ts index ed6bfa46..ad7e2125 100644 --- a/web/src/Enums/shared.enum.ts +++ b/web/src/Enums/shared.enum.ts @@ -78,6 +78,7 @@ export enum EntityType { export enum GHGS { CO = 'CO2', + COE = 'CO2e', CH = 'CH4', NO = 'N2O', HFC = 'HFCs', diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 70625da1..565075e1 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -703,10 +703,10 @@ const ActivityForm: React.FC = ({ method }) => { const findUnit = (ghg: GHGS | undefined) => { if (ghg) { - const mtgUnit = [GHGS.CH, GHGS.NO].includes(ghg) ? ghg : GHGS.CO; + const mtgUnit = [GHGS.CO, GHGS.CH, GHGS.NO].includes(ghg) ? ghg : GHGS.COE; return mtgUnit; } else { - return GHGS.CO; + return GHGS.COE; } }; @@ -753,7 +753,7 @@ const ActivityForm: React.FC = ({ method }) => { // Get mtg timeline data const fetchMtgTimelineData = async () => { - if (method !== 'create' && entId) { + if (method !== 'create' && entId && selectedGhg) { let response: any; try { response = await get(`national/activities/mitigation/${entId}`); @@ -761,7 +761,7 @@ const ActivityForm: React.FC = ({ method }) => { if (response.status === 200 || response.status === 201) { setMtgStartYear(response.data.startYear); - const mtgUnit = response.data.unit; + const mtgUnit = findUnit(selectedGhg); const tempExpectedEntries: ExpectedTimeline[] = []; const tempActualEntries: ActualTimeline[] = []; @@ -982,7 +982,7 @@ const ActivityForm: React.FC = ({ method }) => { resultDocuments: [], }; - if (method === 'create') { + if (method === 'create' && selectedGhg) { payload.mitigationTimeline = { expected: { baselineEmissions: expectedTimeline[0].values, @@ -1009,7 +1009,7 @@ const ActivityForm: React.FC = ({ method }) => { }, }, startYear: mtgStartYear, - unit: findUnit(selectedGhg), + unit: [GHGS.CH, GHGS.NO].includes(selectedGhg) ? selectedGhg : GHGS.CO, }; } From d1b120b7e816e5ab9324fd4d7d5a74eab527aac9 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:42:00 +0530 Subject: [PATCH 04/66] KPI Header fixed --- web/src/locales/i18n/formHeader/en.json | 2 +- web/src/locales/i18n/formHeader/fr.json | 2 +- web/src/locales/i18n/formHeader/sin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/locales/i18n/formHeader/en.json b/web/src/locales/i18n/formHeader/en.json index 07362aa8..e3ed786d 100644 --- a/web/src/locales/i18n/formHeader/en.json +++ b/web/src/locales/i18n/formHeader/en.json @@ -23,7 +23,7 @@ "additionalInfoTitle": "Additional Information for ETF Reports", "documentsHeader": "Documents", "emissionInfoTitle": "Estimates of GHG emission reductions (ktCO2e)", - "kpiInfoTitle": "Other Programme KPI Value", + "kpiInfoTitle": "KPI Values", "investmentNeedsHeader": "Estimated Investment Needs (USD)", "typeHeader": "Type", "natAnchorHeader": "Anchored in a National Strategy", diff --git a/web/src/locales/i18n/formHeader/fr.json b/web/src/locales/i18n/formHeader/fr.json index da014c44..b99b6be5 100644 --- a/web/src/locales/i18n/formHeader/fr.json +++ b/web/src/locales/i18n/formHeader/fr.json @@ -24,7 +24,7 @@ "additionalInfoTitle": "Informations supplémentaires pour les rapports ETF", "documentsHeader": "Documents", "emissionInfoTitle": "Estimations des réductions des émissions de GES (ktCO2e)", - "kpiInfoTitle": "Autre valeur KPI du programme", + "kpiInfoTitle": "Autre valeur KPI", "investmentNeedsHeader": "Besoins d'investissement estimés (USD)", "typeHeader": "Type", "natAnchorHeader": "Ancré dans une stratégie nationale", diff --git a/web/src/locales/i18n/formHeader/sin.json b/web/src/locales/i18n/formHeader/sin.json index d7f4af51..addaee68 100644 --- a/web/src/locales/i18n/formHeader/sin.json +++ b/web/src/locales/i18n/formHeader/sin.json @@ -24,7 +24,7 @@ "additionalInfoTitle": "ETF වාර්තා සඳහා අමතර විස්තර", "documentsHeader": "ලේඛන", "emissionInfoTitle": "GHG හේතුව (ktCO2e) විශාලාව", - "kpiInfoTitle": "විශ්ලේෂණය අනුමත KPI අගය", + "kpiInfoTitle": "KPI අගය", "investmentNeedsHeader": "ආයෝජන අවශ්‍යතා (USD)", "typeHeader": "වර්ගය", "natAnchorHeader": "ජාතික මාධ්‍ය මධ්‍යයට ඇරඹුනු", From 49812ced764138b4c6130a77da9fb1d559b1536b Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:22:55 +0530 Subject: [PATCH 05/66] Information Page Fix --- web/src/App.tsx | 36 +- web/src/Components/Footer/layout.footer.tsx | 20 +- web/src/Components/Layout/infoLayout.scss | 56 + web/src/Components/Layout/infoLayout.tsx | 40 + .../Pages/CodeofConduct/codeofConduct.scss | 136 -- web/src/Pages/CodeofConduct/codeofConduct.tsx | 313 ----- web/src/Pages/CookiePolicy/cookiePolicy.scss | 135 -- web/src/Pages/CookiePolicy/cookiePolicy.tsx | 636 --------- web/src/Pages/Help/help.scss | 6 - web/src/Pages/Help/help.tsx | 42 - .../CodeofConduct/codeofConduct.scss | 85 ++ .../CodeofConduct/codeofConduct.tsx | 238 ++++ .../CookiePolicy/cookiePolicy.scss | 84 ++ .../CookiePolicy/cookiePolicy.tsx | 607 +++++++++ web/src/Pages/InformationPages/Help/help.scss | 39 + web/src/Pages/InformationPages/Help/help.tsx | 37 + .../PrivacyPolicy/privacyPolicy.scss | 121 ++ .../PrivacyPolicy/privacyPolicy.tsx | 1116 ++++++++++++++++ .../Pages/InformationPages/Status/status.scss | 39 + .../Pages/InformationPages/Status/status.tsx | 37 + .../TermsofUse/termsofUse.scss | 92 ++ .../TermsofUse/termsofUse.tsx | 912 +++++++++++++ .../Pages/PrivacyPolicy/privacyPolicy.scss | 172 --- web/src/Pages/PrivacyPolicy/privacyPolicy.tsx | 1156 ----------------- web/src/Pages/TermsofUse/termsofUse.scss | 144 -- web/src/Pages/TermsofUse/termsofUse.tsx | 950 -------------- web/src/locales/i18n/homepage/en.json | 4 +- 27 files changed, 3537 insertions(+), 3716 deletions(-) create mode 100644 web/src/Components/Layout/infoLayout.scss create mode 100644 web/src/Components/Layout/infoLayout.tsx delete mode 100644 web/src/Pages/CodeofConduct/codeofConduct.scss delete mode 100644 web/src/Pages/CodeofConduct/codeofConduct.tsx delete mode 100644 web/src/Pages/CookiePolicy/cookiePolicy.scss delete mode 100644 web/src/Pages/CookiePolicy/cookiePolicy.tsx delete mode 100644 web/src/Pages/Help/help.scss delete mode 100644 web/src/Pages/Help/help.tsx create mode 100644 web/src/Pages/InformationPages/CodeofConduct/codeofConduct.scss create mode 100644 web/src/Pages/InformationPages/CodeofConduct/codeofConduct.tsx create mode 100644 web/src/Pages/InformationPages/CookiePolicy/cookiePolicy.scss create mode 100644 web/src/Pages/InformationPages/CookiePolicy/cookiePolicy.tsx create mode 100644 web/src/Pages/InformationPages/Help/help.scss create mode 100644 web/src/Pages/InformationPages/Help/help.tsx create mode 100644 web/src/Pages/InformationPages/PrivacyPolicy/privacyPolicy.scss create mode 100644 web/src/Pages/InformationPages/PrivacyPolicy/privacyPolicy.tsx create mode 100644 web/src/Pages/InformationPages/Status/status.scss create mode 100644 web/src/Pages/InformationPages/Status/status.tsx create mode 100644 web/src/Pages/InformationPages/TermsofUse/termsofUse.scss create mode 100644 web/src/Pages/InformationPages/TermsofUse/termsofUse.tsx delete mode 100644 web/src/Pages/PrivacyPolicy/privacyPolicy.scss delete mode 100644 web/src/Pages/PrivacyPolicy/privacyPolicy.tsx delete mode 100644 web/src/Pages/TermsofUse/termsofUse.scss delete mode 100644 web/src/Pages/TermsofUse/termsofUse.tsx diff --git a/web/src/App.tsx b/web/src/App.tsx index f28ef0f1..fc3e506a 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -20,11 +20,6 @@ import AddUser from './Pages/Users/AddUser/addUser'; import UserManagement from './Pages/Users/UserManagement/userManagement'; import Dashboard from './Pages/Dashboard/dashboard'; import Homepage from './Pages/Homepage/homepage'; -import PrivacyPolicy from './Pages/PrivacyPolicy/privacyPolicy'; -import CodeOfConduct from './Pages/CodeofConduct/codeofConduct'; -import CookiePolicy from './Pages/CookiePolicy/cookiePolicy'; -import TermsOfUse from './Pages/TermsofUse/termsofUse'; -import CarbonHelp from './Pages/Help/help'; import ActionList from './Pages/Actions/ActionList/actionList'; import ProgrammeList from './Pages/Programmes/ProgrammeList/programmeList'; import ProjectList from './Pages/Projects/ProjectList/projectList'; @@ -33,6 +28,7 @@ import SupportList from './Pages/Support/SupportList/supportList'; import ReportList from './Pages/Reporting/reportList'; import Faq from './Pages/Faq/faq'; import UserProfile from './Pages/Users/UserProfile/UserProfile'; +import InfoLayout from './Components/Layout/infoLayout'; // Lazy Component Loading @@ -44,6 +40,12 @@ const SupportForm = lazy(() => import('./Pages/Support/SupportForm/supportForm') const GhgEmissions = lazy(() => import('./Pages/Emissions/emissions')); const GhgProjections = lazy(() => import('./Pages/Projections/projections')); const GhgConfigurations = lazy(() => import('./Pages/Configurations/configurations')); +const TransparencyHelp = lazy(() => import('./Pages/InformationPages/Help/help')); +const TransparencyStatus = lazy(() => import('./Pages/InformationPages/Status/status')); +const PrivacyPolicy = lazy(() => import('./Pages/InformationPages/PrivacyPolicy/privacyPolicy')); +const CodeOfConduct = lazy(() => import('./Pages/InformationPages/CodeofConduct/codeofConduct')); +const CookiePolicy = lazy(() => import('./Pages/InformationPages/CookiePolicy/cookiePolicy')); +const TermsOfUse = lazy(() => import('./Pages/InformationPages/TermsofUse/termsofUse')); const App = () => { const ability = defineAbility(); @@ -72,15 +74,25 @@ const App = () => { + {/* Home Page */} + } /> + + {/* Authentication Routes */} } /> } /> } /> - } /> - } /> - } /> - } /> - } /> - } /> + + {/* Information Page Routes */} + }> + } /> + } /> + } /> + } /> + } /> + } /> + + + {/* Protected Routes */} }> }> } /> @@ -148,6 +160,8 @@ const App = () => { } /> + + {/* Not Found Redirect */} } /> diff --git a/web/src/Components/Footer/layout.footer.tsx b/web/src/Components/Footer/layout.footer.tsx index 73d380bf..75dca17a 100644 --- a/web/src/Components/Footer/layout.footer.tsx +++ b/web/src/Components/Footer/layout.footer.tsx @@ -43,28 +43,22 @@ const LayoutFooter = () => {
- diff --git a/web/src/Components/Layout/infoLayout.scss b/web/src/Components/Layout/infoLayout.scss new file mode 100644 index 00000000..e76ac8c2 --- /dev/null +++ b/web/src/Components/Layout/infoLayout.scss @@ -0,0 +1,56 @@ +@import '../../Assets/Fonts/fonts.scss'; +@import '../../Styles/variables.scss'; + +.info-layout-container { + height: 100%; + overflow-y: auto; + overflow-x: hidden; + background-color: $background-color; + + .header-container { + background-color: rgba(255, 255, 255, 0.8); + height: 90px; + padding-top: 15px; + + display: flex; + flex-direction: row; + align-items: center; + padding: 0rem 0rem 0 0vw; + cursor: pointer; + + .title { + font-family: 'MuseoSans'; + font-size: 1.2rem; + color: $logo-text-color; + font-weight: 700; + margin-right: 0.5rem; + } + + .title-sub { + font-weight: 100 !important; + font-family: 'MuseoSans'; + font-size: 1.2rem; + color: $logo-text-color; + } + + .logo { + height: 53px; + padding-left: 2vw; + + img { + object-fit: cover; + height: auto; + height: 85%; + margin-top: 6px; + } + } + + .country-name { + font-size: 0.65rem; + color: $logo-text-color; + margin-top: -5px; + font-family: 'MuseoSans'; + } + } + +} diff --git a/web/src/Components/Layout/infoLayout.tsx b/web/src/Components/Layout/infoLayout.tsx new file mode 100644 index 00000000..1d5fa7e6 --- /dev/null +++ b/web/src/Components/Layout/infoLayout.tsx @@ -0,0 +1,40 @@ +import { Layout } from 'antd'; +import { Suspense } from 'react'; +import { Outlet, useNavigate } from 'react-router-dom'; +import { Loading } from '../Loading/loading'; +import LayoutFooter from '../Footer/layout.footer'; +import sliderLogo from '../../Assets/Images/mrvlogo.svg'; +import './infoLayout.scss'; + +const { Content } = Layout; + +const InfoLayout = () => { + const navigate = useNavigate(); + + return ( +
+
+
navigate('/')}> + slider-logo +
+
+
+
{'TRANSPARENCY'}
+
{'SYSTEM'}
+
+
{process.env.REACT_APP_COUNTRY_NAME || 'CountryX'}
+
+
+ + }> + + + +
+ +
+
+ ); +}; + +export default InfoLayout; diff --git a/web/src/Pages/CodeofConduct/codeofConduct.scss b/web/src/Pages/CodeofConduct/codeofConduct.scss deleted file mode 100644 index 06a61c72..00000000 --- a/web/src/Pages/CodeofConduct/codeofConduct.scss +++ /dev/null @@ -1,136 +0,0 @@ -@import '../../Assets/Fonts/fonts.scss'; -@import '../../Styles/variables.scss'; - -.code-container { - height: 100%; - overflow-y: auto; - overflow-x: hidden; - background-color: $background-color; - - .code-header-container { - background-color: rgba(255, 255, 255, 0.8); - height: 90px; - padding-top: 15px; - - display: flex; - flex-direction: row; - align-items: center; - padding: 0rem 0rem 0 0vw; - cursor: pointer; - - .title { - font-family: 'MuseoSans'; - font-size: 1.2rem; - color: $logo-text-color; - font-weight: 700; - margin-right: 0.5rem; - } - - .title-sub { - font-weight: 100 !important; - font-family: 'MuseoSans'; - font-size: 1.2rem; - color: $logo-text-color; - } - - .logo { - height: 53px; - padding-left: 2vw; - - img { - object-fit: cover; - height: auto; - height: 85%; - margin-top: 6px; - } - } - - .country-name { - font-size: 0.65rem; - color: $logo-text-color; - margin-top: -5px; - font-family: 'MuseoSans'; - } - } - .code-body-container { - text-align: left; - justify-content: left; - background-color:white; - margin-top: 5px; - margin-bottom: 5px; - padding-bottom: 60px; - - .code-body { - color: $title-text-color; - padding: 20px 0px 20px 0px; - } - .code-body-contact { - color: $title-text-color; - text-align: left; - padding: 20px 60px 20px 60px; - } - .code-sub { - padding-bottom: 30px; - font-weight: 700; - color: $title-text-color; - } - .codetitle { - font-size: 2rem; - font-weight: 700; - font-family: $primary-font-family; - color: $title-text-color; - text-align: center; - justify-content: center; - text-transform: uppercase; - padding: 50px 50px 10px 50px; - - .code-row { - display: block !important; - } - - @media (max-width: $lg-size) { - font-size: 2rem; - margin-top: 1rem; - color: $title-text-color; - line-height: 1.5em; - } - } - .code-subtitle { - display: flex; - text-align: left; - justify-content: left; - background-color: #fff; - color: $title-text-color; - padding-top: 30px; - font-size: 20px; - font-weight: 700; - padding-left: 0px; - } - .code-card-subtitle { - text-decoration: underline; - color: $title-text-color; - font-size: 20px; - font-weight: 700; - padding-bottom: 20px; - } - .code-card-subtitle-text { - margin: 10px 60px 10px 60px; - } - .code-card-container { - text-align: left; - margin: 30px 60px 30px 60px; - padding: 30px; - border-style: solid; - border-color: $common-form-input-border; - border-width: 1px; - table { - color: $title-text-color; - align-items: center; - - td { - padding-right: 20px; - } - } - } - } -} diff --git a/web/src/Pages/CodeofConduct/codeofConduct.tsx b/web/src/Pages/CodeofConduct/codeofConduct.tsx deleted file mode 100644 index 1f5278b3..00000000 --- a/web/src/Pages/CodeofConduct/codeofConduct.tsx +++ /dev/null @@ -1,313 +0,0 @@ -import { Col, Row } from 'antd'; -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import i18next from 'i18next'; -import sliderLogo from '../../Assets/Images/mrvlogo.svg'; -import LayoutFooter from '../../Components/Footer/layout.footer'; -import './codeofConduct.scss'; - -const CodeOfConduct = () => { - const navigate = useNavigate(); - - useEffect(() => { - if (localStorage.getItem('i18nextLng')!.length > 2) { - i18next.changeLanguage('en'); - } - }, []); - return ( -
- - -
navigate('/')} className="code-header-container"> -
- slider-logo -
-
-
-
{'TRANSPARENCY'}
-
{'SYSTEM'}
-
-
{process.env.REACT_APP_COUNTRY_NAME || 'CountryX'}
-
-
- -
-
- - -
CONTRIBUTOR COVENANT CODE OF CONDUCT
- -
- - -
Our Pledge
-
- We as members, contributors, and leaders pledge to make participation in our community - a harassment-free experience for everyone, regardless of age, body size, visible or - invisible disability, ethnicity, sex characteristics, gender identity and expression, - level of experience, education, socio-economic status, nationality, personal - appearance, race, religion, or sexual identity and orientation. -
- We pledge to act and interact in ways that contribute to an open, welcoming, diverse, - inclusive, and healthy community. -
- -
- - -
Our Standards
-
-

- Examples of behavior that contributes to a positive environment for our community - include: -

-
    -
  • Demonstrating empathy and kindness toward other people
  • -
  • Being respectful of differing opinions, viewpoints, and experiences
  • -
  • Giving and gracefully accepting constructive feedback
  • -
  • - Accepting responsibility and apologizing to those affected by our mistakes, and - learning from the experience -
  • -
  • - Focusing on what is best not just for us as individuals, but for the overall - community -
  • -
-

Examples of unacceptable behavior include:

-
    -
  • - The use of sexualized language or imagery, and sexual attention or advances of any - kind -
  • -
  • - Trolling, insulting or derogatory comments, and personal or political attacks -
  • -
  • Public or private harassment
  • -
  • - Publishing others’ private information, such as a physical or email address, - without their explicit permission -
  • -
  • - Other conduct which could reasonably be considered inappropriate in a professional - setting -
  • -
-
- -
- - -
Enforcement Responsibilities
-
-

- Community leaders are responsible for clarifying and enforcing our standards of - acceptable behavior and will take appropriate and fair corrective action in response - to any behavior that they deem inappropriate, threatening, offensive, or harmful. -

-

- Community leaders have the right and responsibility to remove, edit, or reject - comments, commits, code, wiki edits, issues, and other contributions that are not - aligned to this Code of Conduct, and will communicate reasons for moderation - decisions when appropriate. -

-
- -
- - -
Scope
-
-

- This Code of Conduct applies within all community spaces, and also applies when an - individual is officially representing the community in public spaces. Examples of - representing our community include using an official e-mail address, posting via an - official social media account, or acting as an appointed representative at an online - or offline event. -

-
- -
- - -
Enforcement
-
-

- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported - to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All - complaints will be reviewed and investigated promptly and fairly. -

-

- All community leaders are obligated to respect the privacy and security of the - reporter of any incident. -

-
- -
- - -
Enforcement Guidelines
-
-

- Community leaders will follow these Community Impact Guidelines in determining the - consequences for any action they deem in violation of this Code of Conduct: -

-
- -
- - -
1. Correction
-
-

- Community Impact: Use of inappropriate language or other behavior deemed - unprofessional or unwelcome in the community. -

-

- Consequence: A private, written warning from community leaders, providing clarity - around the nature of the violation and an explanation of why the behavior was - inappropriate. A public apology may be requested. -

-
- -
- - -
2. Warning
-
-

Community Impact: A violation through a single incident or series of actions.

-

- {' '} - Consequence: A warning with consequences for continued behavior. No interaction with - the people involved, including unsolicited interaction with those enforcing the Code - of Conduct, for a specified period of time. This includes avoiding interactions in - community spaces as well as external channels like social media. Violating these - terms may lead to a temporary or permanent ban. -

-
- -
- - -
3. Temporary Ban
-
-

- Community Impact: A serious violation of community standards, including sustained - inappropriate behavior. -

-

- Consequence: A temporary ban from any sort of interaction or public communication - with the community for a specified period of time. No public or private interaction - with the people involved, including unsolicited interaction with those enforcing the - Code of Conduct, is allowed during this period. Violating these terms may lead to a - permanent ban. -

-
- -
- - -
4. Permanent Ban
-
-

- Community Impact: Demonstrating a pattern of violation of community standards, - including sustained inappropriate behavior, harassment of an individual, or - aggression toward or disparagement of classes of individuals. -

-

- Consequence: A permanent ban from any sort of public interaction within the - community. -

-
- -
- - -
Attribution
-
-

- This Code of Conduct is adapted from the - Contributor Covenant, version - 2.0, available at{' '} - - https://www.contributor-covenant.org/version/2/0/code_of_conduct.html - - . -

-

- Community Impact Guidelines were inspired by - - {' '} - Mozilla's code of conduct enforcement ladder - - . -

-

- For answers to common questions about this code of conduct, see the FAQ at{' '} - - https://www.contributor-covenant.org/faq - - . Translations are available at{' '} - - https://www.contributor-covenant.org/translations - - . -

-
- -
-
-
- {/* - -
-
- slider-logo -
-
-
-
{'CARBON'}
-
{'REGISTRY'}
-
-
- {process.env.COUNTRY_NAME || 'CountryX'} -
-
-
- -
- - - -
{t('homepage:footertext1')}
- -
- - -
- {process.env.COUNTRY_NAME || 'CountryX'} - -
- - - - {t('homepage:Cookie')} - - - {t('homepage:codeconduct')} - - - {t('homepage:terms')} - - - {t('homepage:privacy')} - - -
*/} - -
-
- ); -}; - -export default CodeOfConduct; diff --git a/web/src/Pages/CookiePolicy/cookiePolicy.scss b/web/src/Pages/CookiePolicy/cookiePolicy.scss deleted file mode 100644 index 68a54484..00000000 --- a/web/src/Pages/CookiePolicy/cookiePolicy.scss +++ /dev/null @@ -1,135 +0,0 @@ -@import '../../Assets/Fonts/fonts.scss'; -@import '../../Styles/variables.scss'; - -.cookie-container { - height: 100%; - overflow-y: auto; - overflow-x: hidden; - background-color: $background-color; - - .cookie-header-container { - background-color: rgba(255, 255, 255, 0.8); - height: 90px; - padding-top: 15px; - - display: flex; - flex-direction: row; - align-items: center; - padding: 0rem 0rem 0 0vw; - cursor: pointer; - - .title { - font-family: 'MuseoSans'; - font-size: 1.2rem; - color: $logo-text-color; - font-weight: 700; - margin-right: 0.5rem; - } - - .title-sub { - font-weight: 100 !important; - font-family: 'MuseoSans'; - font-size: 1.2rem; - color: $logo-text-color; - } - - .logo { - height: 53px; - padding-left: 2vw; - - img { - object-fit: cover; - height: auto; - height: 85%; - margin-top: 6px; - } - } - - .country-name { - font-size: 0.65rem; - color: $logo-text-color; - margin-top: -5px; - font-family: 'MuseoSans'; - } - } - .cookie-body-container { - text-align: center; - justify-content: center; - background-color: #fff; - margin-top: 5px; - margin-bottom: 5px; - padding-bottom: 60px; - - a{ - color:#004F9E !important; - } - - .cookie-body { - color: $title-text-color; - padding: 20px 0px 20px 0px; - } - .cookie-body-contact { - color: $title-text-color; - text-align: left; - padding: 20px 0px 20px 0px; - } - .cookie-sub { - padding-bottom: 30px; - font-weight: 700; - color: $title-text-color; - } - .cookietitle { - font-size: 2rem; - font-weight: 700; - font-family: $primary-font-family; - color: $title-text-color; - text-align: center; - justify-content: center; - text-transform: uppercase; - padding: 50px 50px 10px 50px; - - @media (max-width: $lg-size) { - font-size: 2rem; - margin-top: 1rem; - color: $title-text-color; - line-height: 1.5em; - } - } - .cookie-subtitle { - display: flex; - text-align: center; - justify-content: center; - background-color: rgba(255, 255, 255, 0.8); - color: $title-text-color; - padding-top: 30px; - font-size: 20px; - font-weight: 700; - } - .cookie-card-subtitle { - text-decoration: underline; - color: $title-text-color; - font-size: 20px; - font-weight: 700; - padding-bottom: 20px; - } - .cookie-card-subtitle-text { - margin: 10px 0px 10px 0px; - } - .cookie-card-container { - text-align: left; - margin: 30px 0px 30px 0px; - padding: 30px; - border-style: solid; - border-color: $common-form-input-border; - border-width: 1px; - table { - color: $title-text-color; - align-items: center; - - td { - padding-right: 20px; - } - } - } - } -} diff --git a/web/src/Pages/CookiePolicy/cookiePolicy.tsx b/web/src/Pages/CookiePolicy/cookiePolicy.tsx deleted file mode 100644 index b3ea6956..00000000 --- a/web/src/Pages/CookiePolicy/cookiePolicy.tsx +++ /dev/null @@ -1,636 +0,0 @@ -import { Row, Col } from 'antd'; -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import i18next from 'i18next'; -import sliderLogo from '../../Assets/Images/mrvlogo.svg'; -import './cookiePolicy.scss'; -import LayoutFooter from '../../Components/Footer/layout.footer'; -const CookiePolicy = () => { - const navigate = useNavigate(); - - useEffect(() => { - if (localStorage.getItem('i18nextLng')!.length > 2) { - i18next.changeLanguage('en'); - } - }, []); - return ( -
- - -
navigate('/')} className="cookie-header-container"> -
- slider-logo -
-
-
-
{'TRANSPARENCY'}
-
{'SYSTEM'}
-
-
{process.env.REACT_APP_COUNTRY_NAME || 'CountryX'}
-
-
- -
-
- - -
SAMPLE COOKIE POLICY
-
Last updated February 02, 2023
-
- This Cookie Policy explains how {process.env.REACT_APP_COUNTRY_NAME || 'CountryX'} ( - "Company", "we", "us", and "our") uses cookies and similar - technologies to recognize you when you visit our websites at{' '} - - https://carbreg.org - - , ("Websites"). It explains what these technologies are and why we use them, as - well as your rights to control our use of them. -
- In some cases we may use cookies to collect personal information, or that becomes - personal information if we combine it with other information. -
-
- -
- - -
What are cookies?
-
- Cookies are small data files that are placed on your computer or mobile device when - you visit a website. Cookies are widely used by website owners in order to make their - websites work, or to work more efficiently, as well as to provide reporting - information. -
Cookies set by the website owner (in this case,{' '} - {process.env.REACT_APP_COUNTRY_NAME || 'CountryX'}) are called "first party cookies". - Cookies set by parties other than the website owner are called "third party cookies". - Third party cookies enable third party features or functionality to be provided on or - through the website (e.g. like advertising, interactive content and analytics). The - parties that set these third party cookies can recognize your computer both when it - visits the website in question and also when it visits certain other websites. -
- -
- - -
Why do we use cookies?
-
- We use first and third party cookies for several reasons. Some cookies are required - for technical reasons in order for our Websites to operate, and we refer to these as - "essential" or "strictly necessary" cookies. Other cookies also enable us to track and - target the interests of our users to enhance the experience on our Online Properties. - Third parties serve cookies through our Websites for advertising, analytics and other - purposes. This is described in more detail below. -
The specific types of first and third party cookies served through our Websites - and the purposes they perform are described below -
(please note that the specific cookies served may vary depending on the - specific Online Properties you visit): -
- -
- - -
How can I control cookies?
-
- You have the right to decide whether to accept or reject cookies. You can exercise - your cookie rights by setting your preferences in the Cookie Consent Manager. The - Cookie Consent Manager allows you to select which categories of cookies you accept or - reject. Essential cookies cannot be rejected as they are strictly necessary to provide - you with services. -
The Cookie Consent Manager can be found in the notification banner and on our - website. If you choose to reject cookies, you may still use our website though your - access to some functionality and areas of our website may be restricted. You may also - set or amend your web browser controls to accept or refuse cookies. As the means by - which you can refuse cookies through your web browser controls vary from - browser-to-browser, you should visit your browser's help menu for more information. -
In addition, most advertising networks offer you a way to opt out of targeted - advertising. If you would like to find out more information, please visit - - {' '} - http://www.aboutads.info/choices/ - {' '} - or - - {' '} - http://www.youronlinechoices.com - - . -
The specific types of first and third party cookies served through our Websites - and the purposes they perform are described in the table below (please note that the - specific cookies served may vary depending on the specific Online Properties you - visit): -
- -
- - -
Essential website cookies:
-
- These cookies are strictly necessary to provide you with services available through - our Websites and to use some of its features, such as access to secure areas. -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:JSESSIONID
Purpose: - Used to maintain an anonymous user session by the server in Java™ 2 Platform - Enterprise Edition web applications. -
It is a necessary cookie that expires at the end of a session. -
Provider:.nr-data.net
Service: - JavaServer Pages Technologies{' '} - - View Service Privacy Policy - {' '} -
Country:____________
Type:server_cookie
Expires in:session
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:_GRECAPTCHA
Purpose: - Used to filter spam traffic and allow only legitimate visitors to use Termly's - services. -
Provider:www.google.com
Service: - reCAPTCHA{' '} - - {' '} - View Service Privacy Policy - {' '} -
Country:United States
Type:http_cookie
Expires in:5 months 27 days
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:__tlbcpv
Purpose:Used to record unique visitor views of the consent banner.
Provider:.termly.io
Service: - Termly{' '} - - View Service Privacy Policy - {' '} -
Country:United States
Type:http_cookie
Expires in:1 year
-
- -
- - -
Analytics and customization cookies:
-
- These cookies collect information that is used either in aggregate form to help us - understand how our Websites are being used or how effective our marketing campaigns - are, or to help us customize our Websites for you. -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:NRJS-5a07f0452f675c44963
Purpose:__________
Provider: - https://carbreg.org/ -
Service:__________
Country:United States
Type:pixel_tracker
Expires in:session
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:_ga
Purpose: - It records a particular ID used to come up with data about website usage by the - user. It is a HTTP cookie that expires after 2 years. -
Provider:.carbreg.org
Service: - Google Analytics{' '} - - View Service Privacy Policy - -
Country:United States
Type:http_cookie
Expires in:1 year 11 months 29 days
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:_ga_#
Purpose: - Used to distinguish individual users by means of designation of a randomly - generated number as client identifier, -
which allows calculation of visits and sessions. -
Provider:.carbreg.org
Service: - Google analytics{' '} - - View Service Privacy Policy - {' '} -
Country:United States
Type:http_cookie
Expires in:1 year 11 months 29 days
-
- -
- - -
Advertising cookies:
-
- These cookies are used to make advertising messages more relevant to you. They perform - functions like preventing the same ad from continuously reappearing, ensuring that ads - are properly displayed for advertisers, and in some cases selecting advertisements - that are based on your interests. -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:a
Purpose: - Registers a unique ID that identifies a returning user's device. The ID is used - for targeted ads. -
Provider: - https://carbreg.org -
Service: - Cox Digital Solutions (Fomerly Adify){' '} - - View Service Privacy Policy - {' '} -
Country:United States
Type:pixel_tracker
Expires in:session
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:_fbp
Purpose: - Facebook tracking pixel used to identify visitors for personalized advertising. -
Provider:.carbreg.org
Service: - Facebook{' '} - - View Service Privacy Policy - {' '} -
Country:United States
Type:http_cookie
Expires in:2 months 29 days
-
- -
- - -
Unclassified cookies:
-
- These are cookies that have not yet been categorized. We are in the process of - classifying these cookies with the help of their providers. -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:_grecaptcha
Purpose:__________
Provider: - https://carbreg.org -
Service:__________
Country:Ireland
Type:html_local_storage
Expires in:persistent
-
- -
- - -
- What about other tracking technologies, like web beacons? -
-
- Cookies are not the only way to recognize or track visitors to a website. We may use - other, similar technologies from time to time, like web beacons (sometimes called - "tracking pixels" or "clear gifs"). These are tiny graphics files that contain a - unique identifier that enable us to recognize when someone has visited our Websites or - opened an e-mail including them. This allows us, for example, to monitor the traffic - patterns of users from one page within a website to another, to deliver or communicate - with cookies, to understand whether you have come to the website from an online - advertisement displayed on a third-party website, to improve site performance, and to - measure the success of e-mail marketing campaigns. In many instances, these - technologies are reliant on cookies to function properly, and so declining cookies - will impair their functioning. -
- -
- - -
Do you use Flash cookies or Local Shared Objects?
-
- Websites may also use so-called "Flash Cookies" (also known as Local Shared Objects or - "LSOs") to, among other things, collect and store information about your use of our - services, fraud prevention and for other site operations. -
If you do not want Flash Cookies stored on your computer, you can adjust the - settings of your Flash player to block Flash Cookies storage using the tools contained - in the{' '} - - Website Storage Settings Panel - - . You can also control Flash Cookies by going to the{' '} - - Global Storage Settings Panel - {' '} - and following the instructions (which may include instructions that explain, for - example, how to delete existing Flash Cookies (referred to "information" on the - Macromedia site), how to prevent Flash LSOs from being placed on your computer without - your being asked, and (for Flash Player 8 and later) how to block Flash Cookies that - are not being delivered by the operator of the page you are on at the time). -
Please note that setting the Flash Player to restrict or limit acceptance of - Flash Cookies may reduce or impede the functionality of some Flash applications, - including, potentially, Flash applications used in connection with our services or - online content. -
- -
- - -
Do you serve targeted advertising?
-
- Third parties may serve cookies on your computer or mobile device to serve advertising - through our Websites. These companies may use information about your visits to this - and other websites in order to provide relevant advertisements about goods and - services that you may be interested in. They may also employ technology that is used - to measure the effectiveness of advertisements. This can be accomplished by them using - cookies or web beacons to collect information about your visits to this and other - sites in order to provide relevant advertisements about goods and services of - potential interest to you. The information collected through this process does not - enable us or them to identify your name, contact details or other details that - directly identify you unless you choose to provide these. -
- -
- - -
How often will you update this Cookie Policy?
-
- We may update this Cookie Policy from time to time in order to reflect, for example, - changes to the cookies we use or for other operational, legal or regulatory reasons. - Please therefore re-visit this Cookie Policy regularly to stay informed about our use - of cookies and related technologies. -
The date at the top of this Cookie Policy indicates when it was last updated. -
- -
- - -
Where can I get further information?
-
- If you have any questions about our use of cookies or other technologies, please email - us at digital@undp.org or by post to: -
-
-
- {process.env.REACT_APP_COUNTRY_NAME || 'CountryX'} -
1 United Nations Plaza -
- New York, New York -
- United States -
- Phone: +260-211-263258 -
-
- This cookie policy was created using Termly's{' '} - - Cookie Consent Manager - - . -
- -
-
- -
- ); -}; - -export default CookiePolicy; diff --git a/web/src/Pages/Help/help.scss b/web/src/Pages/Help/help.scss deleted file mode 100644 index 4420788d..00000000 --- a/web/src/Pages/Help/help.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import '../../Assets/Fonts/fonts.scss'; -@import '../../Styles/variables.scss'; - -.footer-container{ - padding-top: 100%; -} \ No newline at end of file diff --git a/web/src/Pages/Help/help.tsx b/web/src/Pages/Help/help.tsx deleted file mode 100644 index 33439cd5..00000000 --- a/web/src/Pages/Help/help.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Col, Row } from 'antd'; -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import i18next from 'i18next'; -import sliderLogo from '../../Assets/Images/mrvlogo.svg'; -import LayoutFooter from '../../Components/Footer/layout.footer'; -import './help.scss'; - -const CarbonHelp = () => { - const navigate = useNavigate(); - - useEffect(() => { - if (localStorage.getItem('i18nextLng')!.length > 2) { - i18next.changeLanguage('en'); - } - }, []); - return ( -
- - -
navigate('/')} className="code-header-container"> -
- slider-logo -
-
-
-
{'TRANSPARENCY'}
-
{'SYSTEM'}
-
-
{process.env.REACT_APP_COUNTRY_NAME || 'CountryX'}
-
-
- -
-

Help Page

-
- -
-
- ); -}; -export default CarbonHelp; diff --git a/web/src/Pages/InformationPages/CodeofConduct/codeofConduct.scss b/web/src/Pages/InformationPages/CodeofConduct/codeofConduct.scss new file mode 100644 index 00000000..a50201e1 --- /dev/null +++ b/web/src/Pages/InformationPages/CodeofConduct/codeofConduct.scss @@ -0,0 +1,85 @@ +@import '../../../Assets/Fonts/fonts.scss'; +@import '../../../Styles/variables.scss'; + +.code-container { + text-align: left; + justify-content: left; + background-color:white; + margin-top: 5px; + margin-bottom: 5px; + padding-bottom: 60px; + + .code-body { + color: $title-text-color; + padding: 20px 0px 20px 0px; + } + .code-body-contact { + color: $title-text-color; + text-align: left; + padding: 20px 60px 20px 60px; + } + .code-sub { + padding-bottom: 30px; + font-weight: 700; + color: $title-text-color; + } + .codetitle { + font-size: 2rem; + font-weight: 700; + font-family: $primary-font-family; + color: $title-text-color; + text-align: center; + justify-content: center; + text-transform: uppercase; + padding: 50px 50px 10px 50px; + + .code-row { + display: block !important; + } + + @media (max-width: $lg-size) { + font-size: 2rem; + margin-top: 1rem; + color: $title-text-color; + line-height: 1.5em; + } + } + .code-subtitle { + display: flex; + text-align: left; + justify-content: left; + background-color: #fff; + color: $title-text-color; + padding-top: 30px; + font-size: 20px; + font-weight: 700; + padding-left: 0px; + } + .code-card-subtitle { + text-decoration: underline; + color: $title-text-color; + font-size: 20px; + font-weight: 700; + padding-bottom: 20px; + } + .code-card-subtitle-text { + margin: 10px 60px 10px 60px; + } + .code-card-container { + text-align: left; + margin: 30px 60px 30px 60px; + padding: 30px; + border-style: solid; + border-color: $common-form-input-border; + border-width: 1px; + table { + color: $title-text-color; + align-items: center; + + td { + padding-right: 20px; + } + } + } +} + diff --git a/web/src/Pages/InformationPages/CodeofConduct/codeofConduct.tsx b/web/src/Pages/InformationPages/CodeofConduct/codeofConduct.tsx new file mode 100644 index 00000000..c991d637 --- /dev/null +++ b/web/src/Pages/InformationPages/CodeofConduct/codeofConduct.tsx @@ -0,0 +1,238 @@ +import { Col, Row } from 'antd'; +import { useEffect } from 'react'; +import i18next from 'i18next'; +import './codeofConduct.scss'; + +const CodeOfConduct = () => { + useEffect(() => { + if (localStorage.getItem('i18nextLng')!.length > 2) { + i18next.changeLanguage('en'); + } + }, []); + return ( +
+ + +
CONTRIBUTOR COVENANT CODE OF CONDUCT
+ +
+ + +
Our Pledge
+
+ We as members, contributors, and leaders pledge to make participation in our community a + harassment-free experience for everyone, regardless of age, body size, visible or + invisible disability, ethnicity, sex characteristics, gender identity and expression, + level of experience, education, socio-economic status, nationality, personal appearance, + race, religion, or sexual identity and orientation. +
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, + inclusive, and healthy community. +
+ +
+ + +
Our Standards
+
+

+ Examples of behavior that contributes to a positive environment for our community + include: +

+
    +
  • Demonstrating empathy and kindness toward other people
  • +
  • Being respectful of differing opinions, viewpoints, and experiences
  • +
  • Giving and gracefully accepting constructive feedback
  • +
  • + Accepting responsibility and apologizing to those affected by our mistakes, and + learning from the experience +
  • +
  • + Focusing on what is best not just for us as individuals, but for the overall + community +
  • +
+

Examples of unacceptable behavior include:

+
    +
  • + The use of sexualized language or imagery, and sexual attention or advances of any + kind +
  • +
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • +
  • Public or private harassment
  • +
  • + Publishing others’ private information, such as a physical or email address, + without their explicit permission +
  • +
  • + Other conduct which could reasonably be considered inappropriate in a professional + setting +
  • +
+
+ +
+ + +
Enforcement Responsibilities
+
+

+ Community leaders are responsible for clarifying and enforcing our standards of + acceptable behavior and will take appropriate and fair corrective action in response + to any behavior that they deem inappropriate, threatening, offensive, or harmful. +

+

+ Community leaders have the right and responsibility to remove, edit, or reject + comments, commits, code, wiki edits, issues, and other contributions that are not + aligned to this Code of Conduct, and will communicate reasons for moderation decisions + when appropriate. +

+
+ +
+ + +
Scope
+
+

+ This Code of Conduct applies within all community spaces, and also applies when an + individual is officially representing the community in public spaces. Examples of + representing our community include using an official e-mail address, posting via an + official social media account, or acting as an appointed representative at an online + or offline event. +

+
+ +
+ + +
Enforcement
+
+

+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to + the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All + complaints will be reviewed and investigated promptly and fairly. +

+

+ All community leaders are obligated to respect the privacy and security of the + reporter of any incident. +

+
+ +
+ + +
Enforcement Guidelines
+
+

+ Community leaders will follow these Community Impact Guidelines in determining the + consequences for any action they deem in violation of this Code of Conduct: +

+
+ +
+ + +
1. Correction
+
+

+ Community Impact: Use of inappropriate language or other behavior deemed + unprofessional or unwelcome in the community. +

+

+ Consequence: A private, written warning from community leaders, providing clarity + around the nature of the violation and an explanation of why the behavior was + inappropriate. A public apology may be requested. +

+
+ +
+ + +
2. Warning
+
+

Community Impact: A violation through a single incident or series of actions.

+

+ {' '} + Consequence: A warning with consequences for continued behavior. No interaction with + the people involved, including unsolicited interaction with those enforcing the Code + of Conduct, for a specified period of time. This includes avoiding interactions in + community spaces as well as external channels like social media. Violating these terms + may lead to a temporary or permanent ban. +

+
+ +
+ + +
3. Temporary Ban
+
+

+ Community Impact: A serious violation of community standards, including sustained + inappropriate behavior. +

+

+ Consequence: A temporary ban from any sort of interaction or public communication with + the community for a specified period of time. No public or private interaction with + the people involved, including unsolicited interaction with those enforcing the Code + of Conduct, is allowed during this period. Violating these terms may lead to a + permanent ban. +

+
+ +
+ + +
4. Permanent Ban
+
+

+ Community Impact: Demonstrating a pattern of violation of community standards, + including sustained inappropriate behavior, harassment of an individual, or aggression + toward or disparagement of classes of individuals. +

+

+ Consequence: A permanent ban from any sort of public interaction within the community. +

+
+ +
+ + +
Attribution
+
+

+ This Code of Conduct is adapted from the + Contributor Covenant, version 2.0, + available at{' '} + + https://www.contributor-covenant.org/version/2/0/code_of_conduct.html + + . +

+

+ Community Impact Guidelines were inspired by + + {' '} + Mozilla's code of conduct enforcement ladder + + . +

+

+ For answers to common questions about this code of conduct, see the FAQ at{' '} + + https://www.contributor-covenant.org/faq + + . Translations are available at{' '} + + https://www.contributor-covenant.org/translations + + . +

+
+ +
+
+ ); +}; + +export default CodeOfConduct; diff --git a/web/src/Pages/InformationPages/CookiePolicy/cookiePolicy.scss b/web/src/Pages/InformationPages/CookiePolicy/cookiePolicy.scss new file mode 100644 index 00000000..cb690702 --- /dev/null +++ b/web/src/Pages/InformationPages/CookiePolicy/cookiePolicy.scss @@ -0,0 +1,84 @@ +@import '../../../Assets/Fonts/fonts.scss'; +@import '../../../Styles/variables.scss'; + +.cookie-container { + text-align: center; + justify-content: center; + background-color: #fff; + margin-top: 5px; + margin-bottom: 5px; + padding-bottom: 60px; + + a{ + color:#004F9E !important; + } + + .cookie-body { + color: $title-text-color; + padding: 20px 0px 20px 0px; + } + .cookie-body-contact { + color: $title-text-color; + text-align: left; + padding: 20px 0px 20px 0px; + } + .cookie-sub { + padding-bottom: 30px; + font-weight: 700; + color: $title-text-color; + } + .cookietitle { + font-size: 2rem; + font-weight: 700; + font-family: $primary-font-family; + color: $title-text-color; + text-align: center; + justify-content: center; + text-transform: uppercase; + padding: 50px 50px 10px 50px; + + @media (max-width: $lg-size) { + font-size: 2rem; + margin-top: 1rem; + color: $title-text-color; + line-height: 1.5em; + } + } + .cookie-subtitle { + display: flex; + text-align: center; + justify-content: center; + background-color: rgba(255, 255, 255, 0.8); + color: $title-text-color; + padding-top: 30px; + font-size: 20px; + font-weight: 700; + } + .cookie-card-subtitle { + text-decoration: underline; + color: $title-text-color; + font-size: 20px; + font-weight: 700; + padding-bottom: 20px; + } + .cookie-card-subtitle-text { + margin: 10px 0px 10px 0px; + } + .cookie-card-container { + text-align: left; + margin: 30px 0px 30px 0px; + padding: 30px; + border-style: solid; + border-color: $common-form-input-border; + border-width: 1px; + table { + color: $title-text-color; + align-items: center; + + td { + padding-right: 20px; + } + } + } +} + diff --git a/web/src/Pages/InformationPages/CookiePolicy/cookiePolicy.tsx b/web/src/Pages/InformationPages/CookiePolicy/cookiePolicy.tsx new file mode 100644 index 00000000..44d22229 --- /dev/null +++ b/web/src/Pages/InformationPages/CookiePolicy/cookiePolicy.tsx @@ -0,0 +1,607 @@ +import { Row, Col } from 'antd'; +import { useEffect } from 'react'; +import i18next from 'i18next'; +import './cookiePolicy.scss'; + +const CookiePolicy = () => { + useEffect(() => { + if (localStorage.getItem('i18nextLng')!.length > 2) { + i18next.changeLanguage('en'); + } + }, []); + return ( +
+ + +
SAMPLE COOKIE POLICY
+
Last updated February 02, 2023
+
+ This Cookie Policy explains how {process.env.REACT_APP_COUNTRY_NAME || 'CountryX'} ( + "Company", "we", "us", and "our") uses cookies and similar + technologies to recognize you when you visit our websites at{' '} + + https://carbreg.org + + , ("Websites"). It explains what these technologies are and why we use them, as + well as your rights to control our use of them. +
+ In some cases we may use cookies to collect personal information, or that becomes + personal information if we combine it with other information. +
+
+ +
+ + +
What are cookies?
+
+ Cookies are small data files that are placed on your computer or mobile device when you + visit a website. Cookies are widely used by website owners in order to make their + websites work, or to work more efficiently, as well as to provide reporting information. +
Cookies set by the website owner (in this case,{' '} + {process.env.REACT_APP_COUNTRY_NAME || 'CountryX'}) are called "first party cookies". + Cookies set by parties other than the website owner are called "third party cookies". + Third party cookies enable third party features or functionality to be provided on or + through the website (e.g. like advertising, interactive content and analytics). The + parties that set these third party cookies can recognize your computer both when it + visits the website in question and also when it visits certain other websites. +
+ +
+ + +
Why do we use cookies?
+
+ We use first and third party cookies for several reasons. Some cookies are required for + technical reasons in order for our Websites to operate, and we refer to these as + "essential" or "strictly necessary" cookies. Other cookies also enable us to track and + target the interests of our users to enhance the experience on our Online Properties. + Third parties serve cookies through our Websites for advertising, analytics and other + purposes. This is described in more detail below. +
The specific types of first and third party cookies served through our Websites + and the purposes they perform are described below +
(please note that the specific cookies served may vary depending on the specific + Online Properties you visit): +
+ +
+ + +
How can I control cookies?
+
+ You have the right to decide whether to accept or reject cookies. You can exercise your + cookie rights by setting your preferences in the Cookie Consent Manager. The Cookie + Consent Manager allows you to select which categories of cookies you accept or reject. + Essential cookies cannot be rejected as they are strictly necessary to provide you with + services. +
The Cookie Consent Manager can be found in the notification banner and on our + website. If you choose to reject cookies, you may still use our website though your + access to some functionality and areas of our website may be restricted. You may also + set or amend your web browser controls to accept or refuse cookies. As the means by + which you can refuse cookies through your web browser controls vary from + browser-to-browser, you should visit your browser's help menu for more information. +
In addition, most advertising networks offer you a way to opt out of targeted + advertising. If you would like to find out more information, please visit + + {' '} + http://www.aboutads.info/choices/ + {' '} + or + + {' '} + http://www.youronlinechoices.com + + . +
The specific types of first and third party cookies served through our Websites + and the purposes they perform are described in the table below (please note that the + specific cookies served may vary depending on the specific Online Properties you visit): +
+ +
+ + +
Essential website cookies:
+
+ These cookies are strictly necessary to provide you with services available through our + Websites and to use some of its features, such as access to secure areas. +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:JSESSIONID
Purpose: + Used to maintain an anonymous user session by the server in Java™ 2 Platform + Enterprise Edition web applications. +
It is a necessary cookie that expires at the end of a session. +
Provider:.nr-data.net
Service: + JavaServer Pages Technologies{' '} + + View Service Privacy Policy + {' '} +
Country:____________
Type:server_cookie
Expires in:session
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:_GRECAPTCHA
Purpose: + Used to filter spam traffic and allow only legitimate visitors to use Termly's + services. +
Provider:www.google.com
Service: + reCAPTCHA{' '} + + {' '} + View Service Privacy Policy + {' '} +
Country:United States
Type:http_cookie
Expires in:5 months 27 days
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:__tlbcpv
Purpose:Used to record unique visitor views of the consent banner.
Provider:.termly.io
Service: + Termly{' '} + + View Service Privacy Policy + {' '} +
Country:United States
Type:http_cookie
Expires in:1 year
+
+ +
+ + +
Analytics and customization cookies:
+
+ These cookies collect information that is used either in aggregate form to help us + understand how our Websites are being used or how effective our marketing campaigns are, + or to help us customize our Websites for you. +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:NRJS-5a07f0452f675c44963
Purpose:__________
Provider: + https://carbreg.org/ +
Service:__________
Country:United States
Type:pixel_tracker
Expires in:session
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:_ga
Purpose: + It records a particular ID used to come up with data about website usage by the + user. It is a HTTP cookie that expires after 2 years. +
Provider:.carbreg.org
Service: + Google Analytics{' '} + + View Service Privacy Policy + +
Country:United States
Type:http_cookie
Expires in:1 year 11 months 29 days
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:_ga_#
Purpose: + Used to distinguish individual users by means of designation of a randomly + generated number as client identifier, +
which allows calculation of visits and sessions. +
Provider:.carbreg.org
Service: + Google analytics{' '} + + View Service Privacy Policy + {' '} +
Country:United States
Type:http_cookie
Expires in:1 year 11 months 29 days
+
+ +
+ + +
Advertising cookies:
+
+ These cookies are used to make advertising messages more relevant to you. They perform + functions like preventing the same ad from continuously reappearing, ensuring that ads + are properly displayed for advertisers, and in some cases selecting advertisements that + are based on your interests. +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:a
Purpose: + Registers a unique ID that identifies a returning user's device. The ID is used + for targeted ads. +
Provider: + https://carbreg.org +
Service: + Cox Digital Solutions (Fomerly Adify){' '} + + View Service Privacy Policy + {' '} +
Country:United States
Type:pixel_tracker
Expires in:session
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:_fbp
Purpose: + Facebook tracking pixel used to identify visitors for personalized advertising. +
Provider:.carbreg.org
Service: + Facebook{' '} + + View Service Privacy Policy + {' '} +
Country:United States
Type:http_cookie
Expires in:2 months 29 days
+
+ +
+ + +
Unclassified cookies:
+
+ These are cookies that have not yet been categorized. We are in the process of + classifying these cookies with the help of their providers. +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:_grecaptcha
Purpose:__________
Provider: + https://carbreg.org +
Service:__________
Country:Ireland
Type:html_local_storage
Expires in:persistent
+
+ +
+ + +
+ What about other tracking technologies, like web beacons? +
+
+ Cookies are not the only way to recognize or track visitors to a website. We may use + other, similar technologies from time to time, like web beacons (sometimes called + "tracking pixels" or "clear gifs"). These are tiny graphics files that contain a unique + identifier that enable us to recognize when someone has visited our Websites or opened + an e-mail including them. This allows us, for example, to monitor the traffic patterns + of users from one page within a website to another, to deliver or communicate with + cookies, to understand whether you have come to the website from an online advertisement + displayed on a third-party website, to improve site performance, and to measure the + success of e-mail marketing campaigns. In many instances, these technologies are reliant + on cookies to function properly, and so declining cookies will impair their functioning. +
+ +
+ + +
Do you use Flash cookies or Local Shared Objects?
+
+ Websites may also use so-called "Flash Cookies" (also known as Local Shared Objects or + "LSOs") to, among other things, collect and store information about your use of our + services, fraud prevention and for other site operations. +
If you do not want Flash Cookies stored on your computer, you can adjust the + settings of your Flash player to block Flash Cookies storage using the tools contained + in the{' '} + + Website Storage Settings Panel + + . You can also control Flash Cookies by going to the{' '} + + Global Storage Settings Panel + {' '} + and following the instructions (which may include instructions that explain, for + example, how to delete existing Flash Cookies (referred to "information" on the + Macromedia site), how to prevent Flash LSOs from being placed on your computer without + your being asked, and (for Flash Player 8 and later) how to block Flash Cookies that are + not being delivered by the operator of the page you are on at the time). +
Please note that setting the Flash Player to restrict or limit acceptance of + Flash Cookies may reduce or impede the functionality of some Flash applications, + including, potentially, Flash applications used in connection with our services or + online content. +
+ +
+ + +
Do you serve targeted advertising?
+
+ Third parties may serve cookies on your computer or mobile device to serve advertising + through our Websites. These companies may use information about your visits to this and + other websites in order to provide relevant advertisements about goods and services that + you may be interested in. They may also employ technology that is used to measure the + effectiveness of advertisements. This can be accomplished by them using cookies or web + beacons to collect information about your visits to this and other sites in order to + provide relevant advertisements about goods and services of potential interest to you. + The information collected through this process does not enable us or them to identify + your name, contact details or other details that directly identify you unless you choose + to provide these. +
+ +
+ + +
How often will you update this Cookie Policy?
+
+ We may update this Cookie Policy from time to time in order to reflect, for example, + changes to the cookies we use or for other operational, legal or regulatory reasons. + Please therefore re-visit this Cookie Policy regularly to stay informed about our use of + cookies and related technologies. +
The date at the top of this Cookie Policy indicates when it was last updated. +
+ +
+ + +
Where can I get further information?
+
+ If you have any questions about our use of cookies or other technologies, please email + us at digital@undp.org or by post to: +
+
+
+ {process.env.REACT_APP_COUNTRY_NAME || 'CountryX'} +
1 United Nations Plaza +
+ New York, New York +
+ United States +
+ Phone: +260-211-263258 +
+
+ This cookie policy was created using Termly's{' '} + Cookie Consent Manager. +
+ +
+
+ ); +}; + +export default CookiePolicy; diff --git a/web/src/Pages/InformationPages/Help/help.scss b/web/src/Pages/InformationPages/Help/help.scss new file mode 100644 index 00000000..602e1dd9 --- /dev/null +++ b/web/src/Pages/InformationPages/Help/help.scss @@ -0,0 +1,39 @@ +@import '../../../Assets/Fonts/fonts.scss'; +@import '../../../Styles/variables.scss'; + +.help-container { + text-align: left; + justify-content: left; + background-color:white; + margin-top: 5px; + margin-bottom: 5px; + padding-bottom: 60px; + + .title { + font-size: 2rem; + font-weight: 700; + font-family: $primary-font-family; + color: $title-text-color; + text-align: center; + justify-content: center; + text-transform: uppercase; + padding: 50px 50px 10px 50px; + + .code-row { + display: block !important; + } + + @media (max-width: $lg-size) { + font-size: 2rem; + margin-top: 1rem; + color: $title-text-color; + line-height: 1.5em; + } + } + + .text { + color: $title-text-color; + padding: 20px 0px 20px 0px; + } +} + diff --git a/web/src/Pages/InformationPages/Help/help.tsx b/web/src/Pages/InformationPages/Help/help.tsx new file mode 100644 index 00000000..5cb018ec --- /dev/null +++ b/web/src/Pages/InformationPages/Help/help.tsx @@ -0,0 +1,37 @@ +import { Col, Row } from 'antd'; +import { useEffect } from 'react'; +import i18next from 'i18next'; +import './help.scss'; + +const Help = () => { + useEffect(() => { + if (localStorage.getItem('i18nextLng')!.length > 2) { + i18next.changeLanguage('en'); + } + }, []); + return ( +
+ + +
Help
+ +
+ + +
+ We as members, contributors, and leaders pledge to make participation in our community a + harassment-free experience for everyone, regardless of age, body size, visible or + invisible disability, ethnicity, sex characteristics, gender identity and expression, + level of experience, education, socio-economic status, nationality, personal appearance, + race, religion, or sexual identity and orientation. +
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, + inclusive, and healthy community. +
+ +
+
+ ); +}; + +export default Help; diff --git a/web/src/Pages/InformationPages/PrivacyPolicy/privacyPolicy.scss b/web/src/Pages/InformationPages/PrivacyPolicy/privacyPolicy.scss new file mode 100644 index 00000000..984bd9c3 --- /dev/null +++ b/web/src/Pages/InformationPages/PrivacyPolicy/privacyPolicy.scss @@ -0,0 +1,121 @@ +@import '../../../Assets/Fonts/fonts.scss'; +@import '../../../Styles/variables.scss'; + +.privacy-container { + text-align: left; + justify-content: left; + background-color: white; + margin-top: 5px; + margin-bottom: 5px; + padding-bottom: 60px; + + .privacy-body { + color: $title-text-color; + padding: 20px 0px 20px 0px; + } + p { + margin: 30px; + } + table { + text-align: left; + width: auto; + padding-top: 30px; + } + .table-row3 { + text-align: center; + } + .table-row1 { + padding: 20px; + } + .privacy-body-contact { + font-weight: 700; + color: $title-text-color; + text-align: left; + padding: 20px 0px 20px 0px; + } + .privacy-sub { + text-align: center; + justify-content: center; + padding-bottom: 30px; + font-weight: 700; + color: $title-text-color; + } + .privacytitle { + font-size: 2rem; + font-weight: 700; + font-family: $primary-font-family; + color: $title-text-color; + text-align: center; + justify-content: center; + text-transform: uppercase; + padding: 50px 50px 10px 50px; + + @media (max-width: $lg-size) { + font-size: 2rem; + margin-top: 1rem; + color: $title-text-color; + line-height: 1.5em; + } + } + .privacy-subtitle { + display: flex; + text-align: left; + justify-content: left; + background-color: rgba(255, 255, 255, 0.8); + color: $title-text-color; + padding-top: 30px; + font-size: 20px; + font-weight: 700; + padding-left: 0px; + } + .privacy-subtitle-summary { + display: flex; + text-align: left; + justify-content: left; + background-color: rgba(255, 255, 255, 0.8); + color: $title-text-color; + padding-top: 30px; + font-size: 20px; + font-weight: 700; + padding-left: 0px; + } + .privacy-subline { + padding-top: 20px; + padding-left: 0px; + font-weight: 700; + color: $title-text-color; + font-size: 16px; + } + + .privacy-card-subtitle { + text-decoration: underline; + color: $title-text-color; + font-size: 20px; + font-weight: 700; + padding-bottom: 20px; + } + .privacy-card-subtitle-text { + margin: 10px 60px 10px 60px; + } + .privacy-card-container { + text-align: left; + margin: 30px 60px 30px 60px; + padding: 30px; + border-style: solid; + border-color: $common-form-input-border; + border-width: 1px; + table { + color: $title-text-color; + align-items: center; + + td { + padding-right: 20px; + } + } + } +} + +a { + word-wrap: break-word; +} + diff --git a/web/src/Pages/InformationPages/PrivacyPolicy/privacyPolicy.tsx b/web/src/Pages/InformationPages/PrivacyPolicy/privacyPolicy.tsx new file mode 100644 index 00000000..fe41cbfb --- /dev/null +++ b/web/src/Pages/InformationPages/PrivacyPolicy/privacyPolicy.tsx @@ -0,0 +1,1116 @@ +import { Col, Row } from 'antd'; +import { useEffect } from 'react'; +import i18next from 'i18next'; +import './privacyPolicy.scss'; + +const PrivacyPolicy = () => { + useEffect(() => { + if (localStorage.getItem('i18nextLng')!.length > 2) { + i18next.changeLanguage('en'); + } + }, []); + return ( +
+ + +
PRIVACY NOTICE
+
Last updated February 02, 2023
+
+ This privacy notice for United Nations Development Programme ("Company",{' '} + "we", "us", and "our") describes how and why we might collect, + store, use, and/or share ("process")your information when you use our services (" + Services"), such as when you: +
    +
  • + Visit our website at iverifyit.com, or any website of ours that links to this + privacy notice +
  • +
  • + Engage with us in other related ways ― including any sales, marketing, or events +
  • +
+

+ Questions or concerns? Reading this privacy notice will help you understand + your privacy rights and choices. If you do not agree with our policies and practices, + please do not use our Services. If you still have any questions or concerns, please + contact us at nce.digital@undp.org. +

+
+ +
+ + +
SUMMARY OF KEY POINTS
+
+

+ + This summary provides key points from our privacy notice, but you can find out more + details about any of these topics by clicking the link following each key point or + by using our table of contents below to find the section you are looking for. You + can also click here to go directly to our table of contents. + +

+

+ What personal information do we process? When you visit, use, or navigate our + Services, we may process personal information depending on how you interact with + United Nations Development Programme and the Services, the choices you make, and the + products and features you use. Click here to learn more. +

+

+ Do we process any sensitive personal information? We do not process sensitive + personal information. +

+

+ Do you receive any information from third parties? We do not receive any + information from third parties. +
+

+

+ How do you process my information? We process your information to provide, + improve, and administer our Services, communicate with you, for security and fraud + prevention, and to comply with law. We may also process your information for other + purposes with your consent. We process your information only when we have a valid + legal reason to do so. Click here to learn more. +

+

+ In what situations and with which parties do we share personal information? We + may share information in specific situations and with specific third parties. Click + here to learn more. +

+

+ How do we keep your information safe? We have organizational and technical + processes and procedures in place to protect your personal information. However, no + electronic transmission over the internet or information storage technology can be + guaranteed to be 100% secure, so we cannot promise or guarantee that hackers, + cybercriminals, or other unauthorized third parties will not be able to defeat our + security and improperly collect, access, steal, or modify your information. Click + here to learn more. +

+

+ What are your rights? Depending on where you are located geographically, the + applicable privacy law may mean you have certain rights regarding your personal + information. Click here to learn more. +

+

+ How do I exercise my rights? The easiest way to exercise your rights is by + filling out our data subject request form available + here, + or by contacting us. We will consider and act upon any request in accordance with + applicable data protection laws. +

+

+ Want to learn more about what United Nations Development Programme does with any + information we collect? Click here to review the notice in full. +

+
+ +
+ + +
+ TABLE OF CONTENTS +
+
+
    +
  1. + {' '} + WHAT INFORMATION DO WE COLLECT? +
  2. +
  3. + {' '} + HOW DO WE PROCESS YOUR INFORMATION? +
  4. +
  5. + {' '} + + WHAT LEGAL BASES DO WE RELY ON TO PROCESS YOUR PERSONAL INFORMATION? + +
  6. +
  7. + {' '} + WHEN AND WITH WHOM DO WE SHARE YOUR PERSONAL INFORMATION? +
  8. +
  9. + {' '} + DO WE USE COOKIES AND OTHER TRACKING TECHNOLOGIES? +
  10. +
  11. + {' '} + HOW LONG DO WE KEEP YOUR INFORMATION? +
  12. +
  13. + {' '} + HOW DO WE KEEP YOUR INFORMATION SAFE? +
  14. +
  15. + {' '} + DO WE COLLECT INFORMATION FROM MINORS? +
  16. +
  17. + {' '} + WHAT ARE YOUR PRIVACY RIGHTS? +
  18. +
  19. + {' '} + CONTROLS FOR DO-NOT-TRACK FEATURES +
  20. +
  21. + {' '} + DO CALIFORNIA RESIDENTS HAVE SPECIFIC PRIVACY RIGHTS? +
  22. +
  23. + {' '} + DO WE MAKE UPDATES TO THIS NOTICE? +
  24. +
  25. + {' '} + HOW CAN YOU CONTACT US ABOUT THIS NOTICE? +
  26. +
  27. + {' '} + + HOW CAN YOU REVIEW, UPDATE, OR DELETE THE DATA WE COLLECT FROM YOU? + +
  28. +
+
+ +
+ + +
+ 1. WHAT INFORMATION DO WE COLLECT? +
+
+

+ Personal information you disclose to us. +

+

+ + In Short: We collect personal information that you provide to us + +

+

+ We collect personal information that you voluntarily provide to us when you register + on the Services, express an interest in obtaining information about us or our products + and Services, when you participate in activities on the Services, or otherwise when + you contact us. +

+

+ Personal Information Provided by You. The personal information that we collect + depends on the context of your interactions with us and the Services, the choices you + make, and the products and features you use. The personal information we collect may + include the following: +
+

+
    +
  • names
  • +
  • phone numbers
  • +
  • email addresses
  • +
  • usernames
  • +
  • passwords
  • +
  • contact preferences
  • +
+

+ Sensitive Information. We do not process sensitive information. +

+

+ All personal information that you provide to us must be true, complete, and accurate, + and you must notify us of any changes to such personal information. +

+

+ Information automatically collected +

+

+ + In Short: Some information — such as your Internet Protocol (IP) address + and/or browser and device characteristics — is collected automatically when you + visit our Services. + +

+

+ We automatically collect certain information when you visit, use, or navigate the + Services. This information does not reveal your specific identity (like your name or + contact information) but may include device and usage information, such as your IP + address, browser and device characteristics, operating system, language preferences, + referring URLs, device name, country, location, information about how and when you use + our Services, and other technical information. This information is primarily needed to + maintain the security and operation of our Services, and for our internal analytics + and reporting purposes. +

+

+ Like many businesses, we also collect information through cookies and similar + technologies. +

+

The information we collect includes:

+
    +
  • + Log and Usage Data. Log and usage data is service-related, diagnostic, usage, + and performance information our servers automatically collect when you access or use + our Services and which we record in log files. Depending on how you interact with + us, this log data may include your IP address, device information, browser type, and + settings and information about your activity in the Services (such as the date/time + stamps associated with your usage, pages and files viewed, searches, and other + actions you take such as which features you use), device event information (such as + system activity, error reports (sometimes called "crash dumps"), and hardware + settings). +
  • +
  • + Device Data. We collect device data such as information about your computer, + phone, tablet, or other device you use to access the Services. Depending on the + device used, this device data may include information such as your IP address (or + proxy server), device and application identification numbers, location, browser + type, hardware model, Internet service provider and/or mobile carrier, operating + system, and system configuration information. +
  • +
  • + Location Data. We collect location data such as information about your + device's location, which can be either precise or imprecise. How much information we + collect depends on the type and settings of the device you use to access the + Services. For example, we may use GPS and other technologies to collect geolocation + data that tells us your current location (based on your IP address). You can opt out + of allowing us to collect this information either by refusing access to the + information or by disabling your Location setting on your device. However, if you + choose to opt out, you may not be able to use certain aspects of the Services. +
  • +
+
+ +
+ + +
+ 2. HOW DO WE PROCESS YOUR INFORMATION? +
+
+

+ + In Short: We process your information to provide, improve, and administer our + Services, communicate with you, for security and fraud prevention, and to comply + with law. We may also process your information for other purposes with your consent. + +

+

+ + We process your personal information for a variety of reasons, depending on how you + interact with our Services, including: + +

+
    +
  • + + To facilitate account creation and authentication and otherwise manage user + accounts. + {' '} + We may process your information so you can create and log in to your account, as + well as keep your account in working order. +
  • +
  • + To request feedback. We may process your information when necessary to + request feedback and to contact you about your use of our Services. +
  • +
  • + To send you marketing and promotional communications. We may process the + personal information you send to us for our marketing purposes, if this is in + accordance with your marketing preferences. You can opt out of our marketing emails + at any time. For more information, see " + WHAT ARE YOUR PRIVACY RIGHTS?" below. +
  • +
  • + To deliver targeted advertising to you. We may process your information to + develop and display personalized content and advertising tailored to your interests, + location, and more. +
  • +
  • + To protect our Services. We may process your information as part of our + efforts to keep our Services safe and secure, including fraud monitoring and + prevention. +
  • +
  • + To identify usage trends. We may process information about how you use our + Services to better understand how they are being used so we can improve them. +
  • +
  • + To determine the effectiveness of our marketing and promotional campaigns. We + may process your information to better understand how to provide marketing and + promotional campaigns that are most relevant to you. +
  • +
  • + To save or protect an individual's vital interest. We may process your + information when necessary to save or protect an individual’s vital interest, such + as to prevent harm. +
  • +
+
+ +
+ + +
+ 3. WHAT LEGAL BASES DO WE RELY ON TO PROCESS YOUR INFORMATION? +
+
+

+ + In Short: We only process your personal information when we believe it is + necessary and we have a valid legal reason (i.e., legal basis) to do so under + applicable law, like with your consent, to comply with laws, to provide you with + services to enter into or fulfill our contractual obligations, to protect your + rights, or to fulfill our legitimate business interests. + +

+

+ + + If you are located in the EU or UK, this section applies to you. + + +

+

+ The General Data Protection Regulation (GDPR) and UK GDPR require us to explain the + valid legal bases we rely on in order to process your personal information. As such, + we may rely on the following legal bases to process your personal information: +

+
    +
  • + Consent. We may process your information if you have given us permission + (i.e., consent) to use your personal information for a specific purpose. You can + withdraw your consent at any time. Click here to + learn more. +
  • +
  • + Legitimate Interests. We may process your information when we believe it is + reasonably necessary to achieve our legitimate business interests and those + interests do not outweigh your interests and fundamental rights and freedoms. For + example, we may process your personal information for some of the purposes described + in order to: +
  • +
      +
    • + Send users information about special offers and discounts on our products and + services +
    • +
    • + Develop and display personalized and relevant advertising content for our users +
    • +
    • + Analyze how our services are used so we can improve them to engage and retain + users +
    • +
    • Support our marketing activities
    • +
    • Diagnose problems and/or prevent fraudulent activities
    • +
    • + Understand how our users use our products and services so we can improve user + experience +
    • +
    +
  • + Legal Obligations. We may process your information where we believe it is + necessary for compliance with our legal obligations, such as to cooperate with a law + enforcement body or regulatory agency, exercise or defend our legal rights, or + disclose your information as evidence in litigation in which we are involved. +
  • +
  • + Vital Interests. We may process your information where we believe it is + necessary to protect your vital interests or the vital interests of a third party, + such as situations involving potential threats to the safety of any person. +
  • +
+

+ In legal terms, we are generally the “data controller” under European data protection + laws of the personal information described in this privacy notice, since we determine + the means and/or purposes of the data processing we perform. This privacy notice does + not apply to the personal information we process as a “data processor” on behalf of + our customers. In those situations, the customer that we provide services to and with + whom we have entered into a data processing agreement is the “data controller” + responsible for your personal information, and we merely process your information on + their behalf in accordance with your instructions. If you want to know more about our + customers' privacy practices, you should read their privacy policies and direct any + questions you have to them. +

+

+ + + If you are located in Canada, this section applies to you. + + +

+

+ We may process your information if you have given us specific permission (i.e., + express consent) to use your personal information for a specific purpose, or in + situations where your permission can be inferred (i.e., implied consent). You can + withdraw your consent at any time. Click here to learn + more. +

+

+ In some exceptional cases, we may be legally permitted under applicable law to process + your information without your consent, including, for example: +

+
    +
  • + If collection is clearly in the interests of an individual and consent cannot be + obtained in a timely way +
  • +
  • For investigations and fraud detection and prevention
  • +
  • For business transactions provided certain conditions are met
  • +
  • + If it is contained in a witness statement and the collection is necessary to assess, + process, or settle an insurance claim +
  • +
  • + For identifying injured, ill, or deceased persons and communicating with next of kin +
  • +
  • + If we have reasonable grounds to believe an individual has been, is, or may be + victim of financial abuse +
  • +
  • + If it is reasonable to expect collection and use with consent would compromise the + availability or the accuracy of the information and the collection is reasonable for + purposes related to investigating a breach of an agreement or a contravention of the + laws of Canada or a province +
  • +
  • + If disclosure is required to comply with a subpoena, warrant, court order, or rules + of the court relating to the production of records +
  • +
  • + If it was produced by an individual in the course of their employment, business, or + profession and the collection is consistent with the purposes for which the + information was produced +
  • +
  • If the collection is solely for journalistic, artistic, or literary purposes
  • +
  • If the information is publicly available and is specified by the regulations
  • +
+
+ +
+ + +
+ 4. WHEN AND WITH WHOM DO WE SHARE YOUR PERSONAL INFORMATION? +
+
+

+ + In Short: We may share information in specific situations described in this + section and/or with the following third parties. + +

+

We may need to share your personal information in the following situations:

+
    +
  • + Business Transfers. We may share or transfer your information in connection + with, or during negotiations of, any merger, sale of company assets, financing, or + acquisition of all or a portion of our business to another company. +
  • +
+
+ +
+ + +
+ 5. DO WE USE COOKIES AND OTHER TRACKING TECHNOLOGIES? +
+
+

+ + In Short: We may use cookies and other tracking technologies to collect and + store your information. + +

+

+ We may use cookies and similar tracking technologies (like web beacons and pixels) to + access or store information. Specific information about how we use such technologies + and how you can refuse certain cookies is set out in our Cookie Notice. +

+
+ +
+ + +
+ 6. HOW LONG DO WE KEEP YOUR INFORMATION? +
+
+

+ + In Short: We keep your information for as long as necessary to fulfill the + purposes outlined in this privacy notice unless otherwise required by law. + +

+

+ We will only keep your personal information for as long as it is necessary for the + purposes set out in this privacy notice, unless a longer retention period is required + or permitted by law (such as tax, accounting, or other legal requirements). No purpose + in this notice will require us keeping your personal information for longer than the + period of time in which users have an account with us. +

+

+ When we have no ongoing legitimate business need to process your personal information, + we will either delete or anonymize such information, or, if this is not possible (for + example, because your personal information has been stored in backup archives), then + we will securely store your personal information and isolate it from any further + processing until deletion is possible. +

+
+ +
+ + +
+ 7. HOW DO WE KEEP YOUR INFORMATION SAFE? +
+
+

+ + In Short: We aim to protect your personal information through a system of + organizational and technical security measures. + +

+

+ We have implemented appropriate and reasonable technical and organizational security + measures designed to protect the security of any personal information we process. + However, despite our safeguards and efforts to secure your information, no electronic + transmission over the Internet or information storage technology can be guaranteed to + be 100% secure, so we cannot promise or guarantee that hackers, cybercriminals, or + other unauthorized third parties will not be able to defeat our security and + improperly collect, access, steal, or modify your information. Although we will do our + best to protect your personal information, transmission of personal information to and + from our Services is at your own risk. You should only access the Services within a + secure environment. +

+
+ +
+ + +
+ 8. DO WE COLLECT INFORMATION FROM MINORS? +
+
+

+ + In Short: We do not knowingly collect data from or market to children under + 18 years of age. + +

+

+ We do not knowingly solicit data from or market to children under 18 years of age. By + using the Services, you represent that you are at least 18 or that you are the parent + or guardian of such a minor and consent to such minor dependent’s use of the Services. + If we learn that personal information from users less than 18 years of age has been + collected, we will deactivate the account and take reasonable measures to promptly + delete such data from our records. If you become aware of any data we may have + collected from children under age 18, please contact us at info@panos.org.zm. +

+
+ +
+ + +
+ 9. WHAT ARE YOUR PRIVACY RIGHTS? +
+
+

+ + In Short: In some regions, such as the European Economic Area (EEA), United + Kingdom (UK), and Canada, you have rights that allow you greater access to and + control over your personal information. You may review, change, or terminate your + account at any time. + +

+

+ In some regions (like the EEA, UK, and Canada), you have certain rights under + applicable data protection laws. These may include the right (i) to request access and + obtain a copy of your personal information, (ii) to request rectification or erasure; + (iii) to restrict the processing of your personal information; and (iv) if applicable, + to data portability. In certain circumstances, you may also have the right to object + to the processing of your personal information. You can make such a request by + contacting us by using the contact details provided in the section " + HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below. +

+

+ We will consider and act upon any request in accordance with applicable data + protection laws. +

+

+ If you are located in the EEA or UK and you believe we are unlawfully processing your + personal information, you also have the right to complain to your local data + protection supervisory authority. You can find their contact details here: + + {' '} + https://ec.europa.eu/justice/data-protection/bodies/authorities/index_en.html. + +

+

+ If you are located in Switzerland, the contact details for the data protection + authorities are available here: + + {' '} + https://www.edoeb.admin.ch/edoeb/en/home.html. + +

+ +
+

+ + Withdrawing your consent: + {' '} + If we are relying on your consent to process your personal information, which may be + express and/or implied consent depending on the applicable law, you have the right + to withdraw your consent at any time. You can withdraw your consent at any time by + contacting us by using the contact details provided in the section " + HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below. +

+
+ +

+ However, please note that this will not affect the lawfulness of the processing before + its withdrawal, nor when applicable law allows, will it affect the processing of your + personal information conducted in reliance on lawful processing grounds other than + consent. +

+

+ + Opting out of marketing and promotional communications: + {' '} + You can unsubscribe from our marketing and promotional communications at any time by + clicking on the unsubscribe link in the emails that we send, replying “STOP” or + “UNSUBSCRIBE” to the SMS messages that we send, or by contacting us using the details + provided in the section " + HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below. You will then + be removed from the marketing lists — however, we may still communicate with you, for + example to send you service-related messages that are necessary for the administration + and use of your account, to respond to service requests, or for other non-marketing + purposes. +

+

+ Account Information +

+

+ If you would at any time like to review or change the information in your account or + terminate your account, you can: +

+
    +
  • Log in to your account settings and update your user account.
  • +
+

+ Upon your request to terminate your account, we will deactivate or delete your account + and information from our active databases. However, we may retain some information in + our files to prevent fraud, troubleshoot problems, assist with any investigations, + enforce our legal terms and/or comply with applicable legal requirements. +

+

+ + Cookies and similar technologies: + {' '} + Most Web browsers are set to accept cookies by default. If you prefer, you can usually + choose to set your browser to remove cookies and to reject cookies. If you choose to + remove cookies or reject cookies, this could affect certain features or services of + our Services. To opt out of interest-based advertising by advertisers on our Services + visit{' '} + http://www.aboutads.info/choices/. +

+
+ +
+ + +
+ 10. CONTROLS FOR DO-NOT-TRACK FEATURES +
+
+

+ Most web browsers and some mobile operating systems and mobile applications include a + Do-Not-Track ("DNT") feature or setting you can activate to signal your privacy + preference not to have data about your online browsing activities monitored and + collected. At this stage no uniform technology standard for recognizing and + implementing DNT signals has been finalized. As such, we do not currently respond to + DNT browser signals or any other mechanism that automatically communicates your choice + not to be tracked online. If a standard for online tracking is adopted that we must + follow in the future, we will inform you about that practice in a revised version of + this privacy notice. +

+
+ +
+ + +
+ 11. DO CALIFORNIA RESIDENTS HAVE SPECIFIC PRIVACY RIGHTS? +
+
+

+ + In Short: Yes, if you are a resident of California, you are granted specific + rights regarding access to your personal information. + +

+

+ California Civil Code Section 1798.83, also known as the "Shine The Light" law, + permits our users who are California residents to request and obtain from us, once a + year and free of charge, information about categories of personal information (if any) + we disclosed to third parties for direct marketing purposes and the names and + addresses of all third parties with which we shared personal information in the + immediately preceding calendar year. If you are a California resident and would like + to make such a request, please submit your request in writing to us using the contact + information provided below. +

+

+ If you are under 18 years of age, reside in California, and have a registered account + with Services, you have the right to request removal of unwanted data that you + publicly post on the Services. To request removal of such data, please contact us + using the contact information provided below and include the email address associated + with your account and a statement that you reside in California. We will make sure the + data is not publicly displayed on the Services, but please be aware that the data may + not be completely or comprehensively removed from all our systems (e.g., backups, + etc.). +

+

+ CCPA Privacy Notice +

+

The California Code of Regulations defines a "resident" as:

+
    +
  1. + every individual who is in the State of California for other than a temporary or + transitory purpose and +
  2. +
  3. + every individual who is domiciled in the State of California who is outside the + State of California for a temporary or transitory purpose +
  4. +
+

All other individuals are defined as "non-residents."

+

+ If this definition of "resident" applies to you, we must adhere to certain rights and + obligations regarding your personal information. +

+

+ What categories of personal information do we collect? +

+

+ We have collected the following categories of personal information in the past twelve + (12) months: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CategoryExamplesCollected
A. Identifiers + Contact details, such as real name, alias, postal address, telephone or mobile + contact number, unique personal identifier, online identifier, Internet Protocol + address, email address, and account name + YES
+ B. Personal information categories listed in the California Customer Records + statute + + Name, contact information, education, employment, employment history, and + financial information + YES
+ C. Protected classification characteristics under California or federal law + Gender and date of birthNO
D. Commercial information + Transaction information, purchase history, financial details, and payment + information + NO
E. Biometric informationFingerprints and voiceprintsNO
F. Internet or other similar network activity + Browsing history, search history, online behavior, interest data, and interactions + with our and other websites, applications, systems, and advertisements + YES
G. Geolocation dataDevice locationYES
+ H. Audio, electronic, visual, thermal, olfactory, or similar information + + Images and audio, video or call recordings created in connection with our business + activities + NO
I. Professional or employment-related information + Business contact details in order to provide you our services at a business level + or job title, work history, and professional qualifications if you apply for a job + with us + NO
J. Education InformationStudent records and directory informationNO
K. Inferences drawn from other personal information + Inferences drawn from any of the collected personal information listed above to + create a profile or summary about, for example, an individual’s preferences and + characteristics + NO
+

+ We may also collect other personal information outside of these categories instances + where you interact with us in person, online, or by phone or mail in the context of: +

+
    +
  • Receiving help through our customer support channels;
  • +
  • Participation in customer surveys or contests; and
  • +
  • + Facilitation in the delivery of our Services and to respond to your inquiries. +
  • +
+

+ How do we use and share your personal information? +

+

+ + More information about our data collection and sharing practices can be found in + this privacy notice. + +

+

+ You may contact us Social Media Profiles, + or by referring to the contact details at the bottom of this document. +

+

+ If you are using an authorized agent to exercise your right to opt out we may deny a + request if the authorized agent does not submit proof that they have been validly + authorized to act on your behalf. +

+

+ Will your information be shared with anyone else? +

+

+ We may disclose your personal information with our service providers pursuant to a + written contract between us and each service provider. Each service provider is a + for-profit entity that processes the information on our behalf. +

+

+ We may use your personal information for our own business purposes, such as for + undertaking internal research for technological development and demonstration. This is + not considered to be "selling" of your personal information. +

+

+ United Nations Development Programme has not disclosed or sold any personal + information to third parties for a business or commercial purpose in the preceding + twelve (12) months. United Nations Development Programme will not sell personal + information in the future belonging to website visitors, users, and other consumers. +

+

+ Your rights with respect to your personal data +

+

+ Right to request deletion of the data — Request to delete +

+

+ You can ask for the deletion of your personal information. If you ask us to delete + your personal information, we will respect your request and delete your personal + information, subject to certain exceptions provided by law, such as (but not limited + to) the exercise by another consumer of his or her right to free speech, our + compliance requirements resulting from a legal obligation, or any processing that may + be required to protect against illegal activities. +

+

+ Right to be informed — Request to know +

+

Depending on the circumstances, you have a right to know:

+
    +
  • whether we collect and use your personal information;
  • +
  • the categories of personal information that we collect;
  • +
  • the purposes for which the collected personal information is used;
  • +
  • whether we sell your personal information to third parties;
  • +
  • + the categories of personal information that we sold or disclosed for a business + purpose; +
  • +
  • + the categories of third parties to whom the personal information was sold or + disclosed for a business purpose; and +
  • +
  • + the business or commercial purpose for collecting or selling personal information. +
  • +
+

+ In accordance with applicable law, we are not obligated to provide or delete consumer + information that is de-identified in response to a consumer request or to re-identify + individual data to verify a consumer request. +

+

+ Right to Non-Discrimination for the Exercise of a Consumer’s Privacy Rights +

+

We will not discriminate against you if you exercise your privacy rights.

+

+ Verification process +

+

+ Upon receiving your request, we will need to verify your identity to determine you are + the same person about whom we have the information in our system. These verification + efforts require us to ask you to provide information so that we can match it with + information you have previously provided us. For instance, depending on the type of + request you submit, we may ask you to provide certain information so that we can match + the information you provide with the information we already have on file, or we may + contact you through a communication method (e.g., phone or email) that you have + previously provided to us. We may also use other verification methods as the + circumstances dictate. +

+

+ We will only use personal information provided in your request to verify your identity + or authority to make the request. To the extent possible, we will avoid requesting + additional information from you for the purposes of verification. However, if we + cannot verify your identity from the information already maintained by us, we may + request that you provide additional information for the purposes of verifying your + identity and for security or fraud-prevention purposes. We will delete such + additionally provided information as soon as we finish verifying you. +

+

+ Other privacy rights +

+
    +
  • You may object to the processing of your personal information.
  • +
  • + You may request correction of your personal data if it is incorrect or no longer + relevant, or ask to restrict the processing of the information. +
  • +
  • + You can designate an authorized agent to make a request under the CCPA on your + behalf. We may deny a request from an authorized agent that does not submit proof + that they have been validly authorized to act on your behalf in accordance with the + CCPA. +
  • +
  • + You may request to opt out from future selling of your personal information to third + parties. Upon receiving an opt-out request, we will act upon the request as soon as + feasibly possible, but no later than fifteen (15) days from the date of the request + submission. +
  • +
+

+ To exercise these rights, you can contact us Social Media Profiles, or by referring to + the contact details at the bottom of this document. If you have a complaint about how + we handle your data, we would like to hear from you. +

+
+ +
+ + +
+ 12. DO WE MAKE UPDATES TO THIS NOTICE? +
+
+

+ + In Short: Yes, we will update this notice as necessary to stay compliant with + relevant laws. + +

+

+ We may update this privacy notice from time to time. The updated version will be + indicated by an updated "Revised" date and the updated version will be effective as + soon as it is accessible. If we make material changes to this privacy notice, we may + notify you either by prominently posting a notice of such changes or by directly + sending you a notification. We encourage you to review this privacy notice frequently + to be informed of how we are protecting your information. +

+
+ +
+ + +
+ 13. HOW CAN YOU CONTACT US ABOUT THIS NOTICE? +
+
+

+ If you have questions or comments about this notice, you may contact our Data + Protection Officer (DPO) by email at info@panos.org.zm, or by post to: +

+

+ United Nations Development Programme +
+ 1 United Nations Plaza +
+ New York, New York, United States +

+

+ + If you are a resident in the European Economic Area, the "data controller" of your + personal information is United Nations Development Programme. United Nations + Development Programme has appointed DPO to be its representative in the EEA. You can + contact them directly regarding the processing of your information by United Nations + Development Programme, or by post to: + +

+
+ +
+ + +
+ 14. HOW CAN YOU REVIEW, UPDATE, OR DELETE THE DATA WE COLLECT FROM YOU? +
+
+

+ Based on the applicable laws of your country, you may have the right to request access + to the personal information we collect from you, change that information, or delete it + in some circumstances. To request to review, update, or delete your personal + information, please submit a request form by clicking{' '} + + here. + +

+

+ This privacy policy was created using Termly's{' '} + + Privacy Policy Generator. + +

+
+ +
+
+ ); +}; + +export default PrivacyPolicy; diff --git a/web/src/Pages/InformationPages/Status/status.scss b/web/src/Pages/InformationPages/Status/status.scss new file mode 100644 index 00000000..dee1780b --- /dev/null +++ b/web/src/Pages/InformationPages/Status/status.scss @@ -0,0 +1,39 @@ +@import '../../../Assets/Fonts/fonts.scss'; +@import '../../../Styles/variables.scss'; + +.status-container { + text-align: left; + justify-content: left; + background-color:white; + margin-top: 5px; + margin-bottom: 5px; + padding-bottom: 60px; + + .title { + font-size: 2rem; + font-weight: 700; + font-family: $primary-font-family; + color: $title-text-color; + text-align: center; + justify-content: center; + text-transform: uppercase; + padding: 50px 50px 10px 50px; + + .code-row { + display: block !important; + } + + @media (max-width: $lg-size) { + font-size: 2rem; + margin-top: 1rem; + color: $title-text-color; + line-height: 1.5em; + } + } + + .text { + color: $title-text-color; + padding: 20px 0px 20px 0px; + } +} + diff --git a/web/src/Pages/InformationPages/Status/status.tsx b/web/src/Pages/InformationPages/Status/status.tsx new file mode 100644 index 00000000..0590ea0e --- /dev/null +++ b/web/src/Pages/InformationPages/Status/status.tsx @@ -0,0 +1,37 @@ +import { Col, Row } from 'antd'; +import { useEffect } from 'react'; +import i18next from 'i18next'; +import './status.scss'; + +const Status = () => { + useEffect(() => { + if (localStorage.getItem('i18nextLng')!.length > 2) { + i18next.changeLanguage('en'); + } + }, []); + return ( +
+ + +
Status
+ +
+ + +
+ We as members, contributors, and leaders pledge to make participation in our community a + harassment-free experience for everyone, regardless of age, body size, visible or + invisible disability, ethnicity, sex characteristics, gender identity and expression, + level of experience, education, socio-economic status, nationality, personal appearance, + race, religion, or sexual identity and orientation. +
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, + inclusive, and healthy community. +
+ +
+
+ ); +}; + +export default Status; diff --git a/web/src/Pages/InformationPages/TermsofUse/termsofUse.scss b/web/src/Pages/InformationPages/TermsofUse/termsofUse.scss new file mode 100644 index 00000000..9ec485ec --- /dev/null +++ b/web/src/Pages/InformationPages/TermsofUse/termsofUse.scss @@ -0,0 +1,92 @@ +@import '../../../Assets/Fonts/fonts.scss'; +@import '../../../Styles/variables.scss'; + +.term-container { + text-align: left; + justify-content: left; + background-color: white; + margin-top: 5px; + margin-bottom: 5px; + padding-bottom: 60px; + + .term-body { + color: $title-text-color; + padding: 20px 0px 20px 0px; + } + .term-body-contact { + font-weight: 700; + color: $title-text-color; + text-align: left; + padding: 20px 0px 20px 0px; + } + .term-sub { + text-align: center; + justify-content: center; + padding-bottom: 30px; + font-weight: 700; + color: $title-text-color; + } + .termtitle { + font-size: 2rem; + font-weight: 700; + font-family: $primary-font-family; + color: $title-text-color; + text-align: center; + justify-content: center; + text-transform: uppercase; + padding: 50px 50px 10px 50px; + + @media (max-width: $lg-size) { + font-size: 2rem; + margin-top: 1rem; + color: $title-text-color; + line-height: 1.5em; + } + } + .term-subtitle { + display: flex; + text-align: left; + justify-content: left; + background-color: rgba(255, 255, 255, 0.8); + color: $title-text-color; + padding-top: 30px; + font-size: 20px; + font-weight: 700; + padding-left: 0px; + } + .term-subline { + padding-top: 20px; + padding-left: 0px; + font-weight: 700; + color: $title-text-color; + font-size: 16px; + } + + .term-card-subtitle { + text-decoration: underline; + color: $title-text-color; + font-size: 20px; + font-weight: 700; + padding-bottom: 20px; + } + .term-card-subtitle-text { + margin: 10px 60px 10px 60px; + } + .term-card-container { + text-align: left; + margin: 30px 60px 30px 60px; + padding: 30px; + border-style: solid; + border-color: $common-form-input-border; + border-width: 1px; + table { + color: $title-text-color; + align-items: center; + + td { + padding-right: 20px; + } + } + } +} + diff --git a/web/src/Pages/InformationPages/TermsofUse/termsofUse.tsx b/web/src/Pages/InformationPages/TermsofUse/termsofUse.tsx new file mode 100644 index 00000000..6feca064 --- /dev/null +++ b/web/src/Pages/InformationPages/TermsofUse/termsofUse.tsx @@ -0,0 +1,912 @@ +import { Col, Row } from 'antd'; +import { useEffect } from 'react'; +import i18next from 'i18next'; +import './termsofUse.scss'; + +const TermsOfUse = () => { + useEffect(() => { + if (localStorage.getItem('i18nextLng')!.length > 2) { + i18next.changeLanguage('en'); + } + }, []); + return ( +
+ + +
TERMS OF USE
+
Last updated February 02, 2023
+ +
+ + +
TABLE OF CONTENTS
+ + +
+ + +
+ 1. AGREEMENT TO TERMS +
+
+

+ These Terms of Use constitute a legally binding agreement made between you, whether + personally or on behalf of an entity (“you”) and United Nations Development Programme + ("Company," “we," “us," or “our”), concerning your access to and use of the{' '} + + https://carbreg.org + {' '} + website as well as any other media form, media channel, mobile website or mobile + application related, linked, or otherwise connected thereto (collectively, the + “Site”). You agree that by accessing the Site, you have read, understood, and agreed + to be bound by all of these Terms of Use. IF YOU DO NOT AGREE WITH ALL OF THESE TERMS + OF USE, THEN YOU ARE EXPRESSLY PROHIBITED FROM USING THE SITE AND YOU MUST DISCONTINUE + USE IMMEDIATELY. +

+

+ Supplemental terms and conditions or documents that may be posted on the Site from + time to time are hereby expressly incorporated herein by reference. We reserve the + right, in our sole discretion, to make changes or modifications to these Terms of Use + from time to time. We will alert you about any changes by updating the “Last updated” + date of these Terms of Use, and you waive any right to receive specific notice of each + such change. Please ensure that you check the applicable Terms every time you use our + Site so that you understand which Terms apply. You will be subject to, and will be + deemed to have been made aware of and to have accepted, the changes in any revised + Terms of Use by your continued use of the Site after the date such revised Terms of + Use are posted. +

+

+ The information provided on the Site is not intended for distribution to or use by any + person or entity in any jurisdiction or country where such distribution or use would + be contrary to law or regulation or which would subject us to any registration + requirement within such jurisdiction or country. Accordingly, those persons who choose + to access the Site from other locations do so on their own initiative and are solely + responsible for compliance with local laws, if and to the extent local laws are + applicable. +

+

+ The Site is not tailored to comply with industry-specific regulations (Health + Insurance Portability and Accountability Act (HIPAA), Federal Information Security + Management Act (FISMA), etc.), so if your interactions would be subjected to such + laws, you may not use this Site. You may not use the Site in a way that would violate + the Gramm-Leach-Bliley Act (GLBA). +
+

+

+ The Site is intended for users who are at least 18 years old. Persons under the age of + 18 are not permitted to use or register for the Site. +

+
+ +
+ + +
+ 2. INTELLECTUAL PROPERTY RIGHTS +
+
+ Works published on this website are under the{' '} + + Creative Commons Attribution 4.0 International (CC BY 4.0) license. + +
You are free to: +
⦾ Share — copy and redistribute the material in any medium or format. +
⦾ Adapt — remix, transform, and build upon the material for any purpose, even + commercially. +
Under the following terms: +
⦾ Attribution — You must give{' '} + appropriate credit, provide a + link to the license, and indicate if changes were made. You may do so in any reasonable + manner, but not in any way that suggests the licensor endorses you or your use
⦾ + No additional restrictions — You may not apply legal terms or technological measures + that legally restrict others from doing anything the license permits. Unless otherwise + indicated, the Site is our proprietary property and all source code, databases, + functionality, software, website designs, audio, video, text, photographs, and graphics + on the Site (collectively, the “Content”) and the trademarks, service marks, and logos + contained therein (the “Marks”) are owned or controlled by us or licensed to us, and are + protected by copyright and trademark laws and various other intellectual property rights + and unfair competition laws of the United States, international copyright laws, and + international conventions. The Content and the Marks are provided on the Site “AS IS” + for your information and personal use only. Except as expressly provided in these Terms + of Use, no part of the Site and no Content or Marks may be copied, reproduced, + aggregated, republished, uploaded, posted, publicly displayed, encoded, translated, + transmitted, distributed, sold, licensed, or otherwise exploited for any commercial + purpose whatsoever, without our express prior written permission.{' '} +

+ Provided that you are eligible to use the Site, you are granted a limited license to + access and use the Site and to download or print a copy of any portion of the Content + to which you have properly gained access solely for your personal, non-commercial use. + We reserve all rights not expressly granted to you in and to the Site, the Content and + the Marks. +

+
+ +
+ + +
+ 3. USER REPRESENTATIONS +
+
+

+ By using the Site, you represent and warrant that: (1) you have the legal capacity and + you agree to comply with these Terms of Use; (2) you are not a minor in the + jurisdiction in which you reside; (3) you will not access the Site through automated + or non-human means, whether through a bot, script, or otherwise; (4) you will not use + the Site for any illegal or unauthorized purpose; and (5) your use of the Site will + not violate any applicable law or regulation. +

{' '} +

+ If you provide any information that is untrue, inaccurate, not current, or incomplete, + we have the right to suspend or terminate your account and refuse any and all current + or future use of the Site (or any portion thereof). +

+
+ +
+ + +
+ 4. PROHIBITED ACTIVITIES +
+
+

+ You may not access or use the Site for any purpose other than that for which we make + the Site available. The Site may not be used in connection with any commercial + endeavors except those that are specifically endorsed or approved by us. +

+

As a user of the Site, you agree not to:

+
    +
  • + Systematically retrieve data or other content from the Site to create or compile, + directly or indirectly, a collection, compilation, database, or directory without + written permission from us. +
  • +
  • + Trick, defraud, or mislead us and other users, especially in any attempt to learn + sensitive account information such as user passwords. +
  • +
  • + Circumvent, disable, or otherwise interfere with security-related features of the + Site, including features that prevent or restrict the use or copying of any Content + or enforce limitations on the use of the Site and/or the Content contained therein. +
  • +
  • Disparage, tarnish, or otherwise harm, in our opinion, us and/or the Site.
  • +
  • + Use any information obtained from the Site in order to harass, abuse, or harm + another person. +
  • +
  • + Make improper use of our support services or submit false reports of abuse or + misconduct. +
  • +
  • + Use the Site in a manner inconsistent with any applicable laws or regulations. +
  • +
  • Engage in unauthorized framing of or linking to the Site.
  • +
  • + Upload or transmit (or attempt to upload or to transmit) viruses, Trojan horses, or + other material, including excessive use of capital letters and spamming (continuous + posting of repetitive text), that interferes with any party’s uninterrupted use and + enjoyment of the Site or modifies, impairs, disrupts, alters, or interferes with the + use, features, functions, operation, or maintenance of the Site. +
  • +
  • + Engage in any automated use of the system, such as using scripts to send comments or + messages, or using any data mining, robots, or similar data gathering and extraction + tools. +
  • +
  • Delete the copyright or other proprietary rights notice from any Content.
  • +
  • + Attempt to impersonate another user or person or use the username of another user. +
  • +
  • + Upload or transmit (or attempt to upload or to transmit) any material that acts as a + passive or active information collection or transmission mechanism, including + without limitation, clear graphics interchange formats (“gifs”), 1×1 pixels, web + bugs, cookies, or other similar devices (sometimes referred to as “spyware” or + “passive collection mechanisms” or “pcms”). +
  • +
  • + Interfere with, disrupt, or create an undue burden on the Site or the networks or + services connected to the Site. +
  • +
  • + Harass, annoy, intimidate, or threaten any of our employees or agents engaged in + providing any portion of the Site to you. +
  • +
  • + Attempt to bypass any measures of the Site designed to prevent or restrict access to + the Site, or any portion of the Site. +
  • +
  • + Copy or adapt the Site’s software, including but not limited to Flash, PHP, HTML, + JavaScript, or other code. +
  • +
  • + Except as permitted by applicable law, decipher, decompile, disassemble, or reverse + engineer any of the software comprising or in any way making up a part of the Site. +
  • +
  • + Except as may be the result of standard search engine or Internet browser usage, + use, launch, develop, or distribute any automated system, including without + limitation, any spider, robot, cheat utility, scraper, or offline reader that + accesses the Site, or using or launching any unauthorized script or other software. +
  • +
  • Use a buying agent or purchasing agent to make purchases on the Site.
  • +
  • + Make any unauthorized use of the Site, including collecting usernames and/or email + addresses of users by electronic or other means for the purpose of sending + unsolicited email, or creating user accounts by automated means or under false + pretenses. +
  • +
  • + Use the Site as part of any effort to compete with us or otherwise use the Site + and/or the Content for any revenue-generating endeavor or commercial enterprise. +
  • +
  • Use the Site to advertise or offer to sell goods and services.
  • +
+
+ +
+ + +
+ 5. USER GENERATED CONTRIBUTIONS +
+
+

+ The Site may invite you to chat, contribute to, or participate in blogs, message + boards, online forums, and other functionality, and may provide you with the + opportunity to create, submit, post, display, transmit, perform, publish, distribute, + or broadcast content and materials to us or on the Site, including but not limited to + text, writings, video, audio, photographs, graphics, comments, suggestions, or + personal information or other material (collectively, "Contributions"). Contributions + may be viewable by other users of the Site and through third-party websites. As such, + any Contributions you transmit may be treated as non-confidential and non-proprietary. + When you create or make available any Contributions, you thereby represent and warrant + that: +

+
    +
  • + The creation, distribution, transmission, public display, or performance, and the + accessing, downloading, or copying of your Contributions do not and will not + infringe the proprietary rights, including but not limited to the copyright, patent, + trademark, trade secret, or moral rights of any third party. +
  • +
  • + You are the creator and owner of or have the necessary licenses, rights, consents, + releases, and permissions to use and to authorize us, the Site, and other users of + the Site to use your Contributions in any manner contemplated by the Site and these + Terms of Use. +
  • +
  • + You have the written consent, release, and/or permission of each and every + identifiable individual person in your Contributions to use the name or likeness of + each and every such identifiable individual person to enable inclusion and use of + your Contributions in any manner contemplated by the Site and these Terms of Use. +
  • +
  • Your Contributions are not false, inaccurate, or misleading.
  • +
  • + Your Contributions are not unsolicited or unauthorized advertising, promotional + materials, pyramid schemes, chain letters, spam, mass mailings, or other forms of + solicitation. +
  • +
  • + Your Contributions are not obscene, lewd, lascivious, filthy, violent, harassing, + libelous, slanderous, or otherwise objectionable (as determined by us). +
  • +
  • + Your Contributions do not ridicule, mock, disparage, intimidate, or abuse anyone. +
  • +
  • + Your Contributions are not used to harass or threaten (in the legal sense of those + terms) any other person and to promote violence against a specific person or class + of people. +
  • +
  • Your Contributions do not violate any applicable law, regulation, or rule.
  • +
  • + Your Contributions do not violate the privacy or publicity rights of any third + party. +
  • +
  • + Your Contributions do not violate any applicable law concerning child pornography, + or otherwise intended to protect the health or well-being of minors. +
  • +
  • + Your Contributions do not include any offensive comments that are connected to race, + national origin, gender, sexual preference, or physical handicap. +
  • +
  • + Your Contributions do not otherwise violate, or link to material that violates, any + provision of these Terms of Use, or any applicable law or regulation. +
  • +
+

+ Any use of the Site in violation of the foregoing violates these Terms of Use and may + result in, among other things, termination or suspension of your rights to use the + Site. +

+
+ +
+ + +
+ 6. CONTRIBUTION LICENSE +
+
+

+ By posting your Contributions to any part of the Site, you automatically grant, and + you represent and warrant that you have the right to grant, to us an unrestricted, + unlimited, irrevocable, perpetual, non-exclusive, transferable, royalty-free, + fully-paid, worldwide right, and license to host, use, copy, reproduce, disclose, + sell, resell, publish, broadcast, retitle, archive, store, cache, publicly perform, + publicly display, reformat, translate, transmit, excerpt (in whole or in part), and + distribute such Contributions (including, without limitation, your image and voice) + for any purpose, commercial, advertising, or otherwise, and to prepare derivative + works of, or incorporate into other works, such Contributions, and grant and authorize + sublicenses of the foregoing. The use and distribution may occur in any media formats + and through any media channels. +

+

+ This license will apply to any form, media, or technology now known or hereafter + developed, and includes our use of your name, company name, and franchise name, as + applicable, and any of the trademarks, service marks, trade names, logos, and personal + and commercial images you provide. You waive all moral rights in your Contributions, + and you warrant that moral rights have not otherwise been asserted in your + Contributions. +

+

+ We do not assert any ownership over your Contributions. You retain full ownership of + all of your Contributions and any intellectual property rights or other proprietary + rights associated with your Contributions. We are not liable for any statements or + representations in your Contributions provided by you in any area on the Site. You are + solely responsible for your Contributions to the Site and you expressly agree to + exonerate us from any and all responsibility and to refrain from any legal action + against us regarding your Contributions. +

+

+ We have the right, in our sole and absolute discretion, (1) to edit, redact, or + otherwise change any Contributions; (2) to re-categorize any Contributions to place + them in more appropriate locations on the Site; and (3) to pre-screen or delete any + Contributions at any time and for any reason, without notice. We have no obligation to + monitor your Contributions. +

+
+ +
+ + +
+ 7. SUBMISSIONS +
+
+

+ You acknowledge and agree that any questions, comments, suggestions, ideas, feedback, + or other information regarding the Site ("Submissions") provided by you to us are + non-confidential and shall become our sole property. We shall own exclusive rights, + including all intellectual property rights, and shall be entitled to the unrestricted + use and dissemination of these Submissions for any lawful purpose, commercial or + otherwise, without acknowledgment or compensation to you. You hereby waive all moral + rights to any such Submissions, and you hereby warrant that any such Submissions are + original with you or that you have the right to submit such Submissions. You agree + there shall be no recourse against us for any alleged or actual infringement or + misappropriation of any proprietary right in your Submissions. +

+
+ +
+ + +
+ 8. THIRD-PARTY WEBSITE AND CONTENT +
+
+

+ The Site may contain (or you may be sent via the Site) links to other websites + ("Third-Party Websites") as well as articles, photographs, text, graphics, pictures, + designs, music, sound, video, information, applications, software, and other content + or items belonging to or originating from third parties ("Third-Party Content"). Such + Third-Party Websites and Third-Party Content are not investigated, monitored, or + checked for accuracy, appropriateness, or completeness by us, and we are not + responsible for any Third-Party Websites accessed through the Site or any Third-Party + Content posted on, available through, or installed from the Site, including the + content, accuracy, offensiveness, opinions, reliability, privacy practices, or other + policies of or contained in the Third-Party Websites or the Third-Party Content. + Inclusion of, linking to, or permitting the use or installation of any Third-Party + Websites or any Third-Party Content does not imply approval or endorsement thereof by + us. If you decide to leave the Site and access the Third-Party Websites or to use or + install any Third-Party Content, you do so at your own risk, and you should be aware + these Terms of Use no longer govern. You should review the applicable terms and + policies, including privacy and data gathering practices, of any website to which you + navigate from the Site or relating to any applications you use or install from the + Site. Any purchases you make through Third-Party Websites will be through other + websites and from other companies, and we take no responsibility whatsoever in + relation to such purchases which are exclusively between you and the applicable third + party. You agree and acknowledge that we do not endorse the products or services + offered on Third-Party Websites and you shall hold us harmless from any harm caused by + your purchase of such products or services. Additionally, you shall hold us harmless + from any losses sustained by you or harm caused to you relating to or resulting in any + way from any Third-Party Content or any contact with Third-Party Websites. +

+
+ +
+ + +
+ 9. SITE MANAGEMENT +
+
+

+ We reserve the right, but not the obligation, to: (1) monitor the Site for violations + of these Terms of Use; (2) take appropriate legal action against anyone who, in our + sole discretion, violates the law or these Terms of Use, including without limitation, + reporting such user to law enforcement authorities; (3) in our sole discretion and + without limitation, refuse, restrict access to, limit the availability of, or disable + (to the extent technologically feasible) any of your Contributions or any portion + thereof; (4) in our sole discretion and without limitation, notice, or liability, to + remove from the Site or otherwise disable all files and content that are excessive in + size or are in any way burdensome to our systems; and (5) otherwise manage the Site in + a manner designed to protect our rights and property and to facilitate the proper + functioning of the Site. +

+
+ +
+ + +
+ 10. PRIVACY POLICY +
+
+

+ We care about data privacy and security. Please review our Privacy Policy: + /privacy-policy. By using the Site, you agree to be bound by our Privacy Policy, which + is incorporated into these Terms of Use. Please be advised the Site is hosted in the + United States. If you access the Site from any other region of the world with laws or + other requirements governing personal data collection, use, or disclosure that differ + from applicable laws in the United States, then through your continued use of the + Site, you are transferring your data to the United States, and you agree to have your + data transferred to and processed in the United States. +

+
+ +
+ + +
+ 11. TERM AND TERMINATION +
+
+

+ These Terms of Use shall remain in full force and effect while you use the Site. + WITHOUT LIMITING ANY OTHER PROVISION OF THESE TERMS OF USE, WE RESERVE THE RIGHT TO, + IN OUR SOLE DISCRETION AND WITHOUT NOTICE OR LIABILITY, DENY ACCESS TO AND USE OF THE + SITE (INCLUDING BLOCKING CERTAIN IP ADDRESSES), TO ANY PERSON FOR ANY REASON OR FOR NO + REASON, INCLUDING WITHOUT LIMITATION FOR BREACH OF ANY REPRESENTATION, WARRANTY, OR + COVENANT CONTAINED IN THESE TERMS OF USE OR OF ANY APPLICABLE LAW OR REGULATION. WE + MAY TERMINATE YOUR USE OR PARTICIPATION IN THE SITE OR DELETE ANY CONTENT OR + INFORMATION THAT YOU POSTED AT ANY TIME, WITHOUT WARNING, IN OUR SOLE DISCRETION. +
If we terminate or suspend your account for any reason, you are prohibited from + registering and creating a new account under your name, a fake or borrowed name, or + the name of any third party, even if you may be acting on behalf of the third party. + In addition to terminating or suspending your account, we reserve the right to take + appropriate legal action, including without limitation pursuing civil, criminal, and + injunctive redress. +

+
+ +
+ + +
+ 12. MODIFICATIONS AND INTERRUPTIONS +
+
+

+ We reserve the right to change, modify, or remove the contents of the Site at any time + or for any reason at our sole discretion without notice. However, we have no + obligation to update any information on our Site. We also reserve the right to modify + or discontinue all or part of the Site without notice at any time. We will not be + liable to you or any third party for any modification, price change, suspension, or + discontinuance of the Site. +

+

+ We cannot guarantee the Site will be available at all times. We may experience + hardware, software, or other problems or need to perform maintenance related to the + Site, resulting in interruptions, delays, or errors. We reserve the right to change, + revise, update, suspend, discontinue, or otherwise modify the Site at any time or for + any reason without notice to you. You agree that we have no liability whatsoever for + any loss, damage, or inconvenience caused by your inability to access or use the Site + during any downtime or discontinuance of the Site. Nothing in these Terms of Use will + be construed to obligate us to maintain and support the Site or to supply any + corrections, updates, or releases in connection therewith. +

+
+ +
+ + +
+ 13. GOVERNING LAW{' '} +
+
+

+ These Terms of Use and your use of the Site are governed by and construed in + accordance with the laws of the State of New York applicable to agreements made and to + be entirely performed within the State of New York, without regard to its conflict of + law principles. +

+
+ +
+ + +
+ 14. DISPUTE RESOLUTION +
+
Informal Negotiations
+
+

+ To expedite resolution and control the cost of any dispute, controversy, or claim + related to these Terms of Use (each "Dispute" and collectively, the “Disputes”) + brought by either you or us (individually, a “Party” and collectively, the “Parties”), + the Parties agree to first attempt to negotiate any Dispute (except those Disputes + expressly provided below) informally for at least thirty (30) days before initiating + arbitration. Such informal negotiations commence upon written notice from one Party to + the other Party. +

+
+
Binding Arbitration
+
+

+ If the Parties are unable to resolve a Dispute through informal negotiations, the + Dispute (except those Disputes expressly excluded below) will be finally and + exclusively resolved through binding arbitration. YOU UNDERSTAND THAT WITHOUT THIS + PROVISION, YOU WOULD HAVE THE RIGHT TO SUE IN COURT AND HAVE A JURY TRIAL. The + arbitration shall be commenced and conducted under the Commercial Arbitration Rules of + the American Arbitration Association ("AAA") and, where appropriate, the AAA’s + Supplementary Procedures for Consumer Related Disputes ("AAA Consumer Rules"), both of + which are available at the AAA website:{' '} + + www.adr.org + + . Your arbitration fees and your share of arbitrator compensation shall be governed by + the AAA Consumer Rules and, where appropriate, limited by the AAA Consumer Rules. The + arbitration may be conducted in person, through the submission of documents, by phone, + or online. The arbitrator will make a decision in writing, but need not provide a + statement of reasons unless requested by either Party. The arbitrator must follow + applicable law, and any award may be challenged if the arbitrator fails to do so. + Except where otherwise required by the applicable AAA rules or applicable law, the + arbitration will take place in Manhattan, New York. Except as otherwise provided + herein, the Parties may litigate in court to compel arbitration, stay proceedings + pending arbitration, or to confirm, modify, vacate, or enter judgment on the award + entered by the arbitrator. +

+

+ If for any reason, a Dispute proceeds in court rather than arbitration, the Dispute + shall be commenced or prosecuted in the state and federal courts located in Manhattan, + New York, and the Parties hereby consent to, and waive all defenses of lack of + personal jurisdiction, and forum non conveniens with respect to venue and jurisdiction + in such state and federal courts. Application of the United Nations Convention on + Contracts for the International Sale of Goods and the Uniform Computer Information + Transaction Act (UCITA) is excluded from these Terms of Use. +

+

+ In no event shall any Dispute brought by either Party related in any way to the Site + be commenced more than one (1) years after the cause of action arose. If this + provision is found to be illegal or unenforceable, then neither Party will elect to + arbitrate any Dispute falling within that portion of this provision found to be + illegal or unenforceable, and such Dispute shall be decided by a court of competent + jurisdiction within the courts listed for jurisdiction above, and the Parties agree to + submit to the personal jurisdiction of that court. +

+
+
Restrictions
+
+

+ The Parties agree that any arbitration shall be limited to the Dispute between the + Parties individually. To the full extent permitted by law, (a) no arbitration shall be + joined with any other proceeding; (b) there is no right or authority for any Dispute + to be arbitrated on a class-action basis or to utilize class action procedures; and + (c) there is no right or authority for any Dispute to be brought in a purported + representative capacity on behalf of the general public or any other persons. +

+
+
Exceptions to Informal Negotiations and Arbitration
+
+

+ The Parties agree that the following Disputes are not subject to the above provisions + concerning informal negotiations and binding arbitration: (a) any Disputes seeking to + enforce or protect, or concerning the validity of, any of the intellectual property + rights of a Party; (b) any Dispute related to, or arising from, allegations of theft, + piracy, invasion of privacy, or unauthorized use; and (c) any claim for injunctive + relief. If this provision is found to be illegal or unenforceable, then neither Party + will elect to arbitrate any Dispute falling within that portion of this provision + found to be illegal or unenforceable and such Dispute shall be decided by a court of + competent jurisdiction within the courts listed for jurisdiction above, and the + Parties agree to submit to the personal jurisdiction of that court. +

+
+ +
+ + +
+ 15. CORRECTIONS +
+
+

+ There may be information on the Site that contains typographical errors, inaccuracies, + or omissions, including descriptions, pricing, availability, and various other + information. We reserve the right to correct any errors, inaccuracies, or omissions + and to change or update the information on the Site at any time, without prior notice. +

+
+ +
+ + +
+ 16. DISCLAIMER +
+
+

+ THE SITE IS PROVIDED ON AN AS-IS AND AS-AVAILABLE BASIS. YOU AGREE THAT YOUR USE OF + THE SITE AND OUR SERVICES WILL BE AT YOUR SOLE RISK. TO THE FULLEST EXTENT PERMITTED + BY LAW, WE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, IN CONNECTION WITH THE SITE + AND YOUR USE THEREOF, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. WE MAKE NO + WARRANTIES OR REPRESENTATIONS ABOUT THE ACCURACY OR COMPLETENESS OF THE SITE’S CONTENT + OR THE CONTENT OF ANY WEBSITES LINKED TO THE SITE AND WE WILL ASSUME NO LIABILITY OR + RESPONSIBILITY FOR ANY (1) ERRORS, MISTAKES, OR INACCURACIES OF CONTENT AND MATERIALS, + (2) PERSONAL INJURY OR PROPERTY DAMAGE, OF ANY NATURE WHATSOEVER, RESULTING FROM YOUR + ACCESS TO AND USE OF THE SITE, (3) ANY UNAUTHORIZED ACCESS TO OR USE OF OUR SECURE + SERVERS AND/OR ANY AND ALL PERSONAL INFORMATION AND/OR FINANCIAL INFORMATION STORED + THEREIN, (4) ANY INTERRUPTION OR CESSATION OF TRANSMISSION TO OR FROM THE SITE, (5) + ANY BUGS, VIRUSES, TROJAN HORSES, OR THE LIKE WHICH MAY BE TRANSMITTED TO OR THROUGH + THE SITE BY ANY THIRD PARTY, AND/OR (6) ANY ERRORS OR OMISSIONS IN ANY CONTENT AND + MATERIALS OR FOR ANY LOSS OR DAMAGE OF ANY KIND INCURRED AS A RESULT OF THE USE OF ANY + CONTENT POSTED, TRANSMITTED, OR OTHERWISE MADE AVAILABLE VIA THE SITE. WE DO NOT + WARRANT, ENDORSE, GUARANTEE, OR ASSUME RESPONSIBILITY FOR ANY PRODUCT OR SERVICE + ADVERTISED OR OFFERED BY A THIRD PARTY THROUGH THE SITE, ANY HYPERLINKED WEBSITE, OR + ANY WEBSITE OR MOBILE APPLICATION FEATURED IN ANY BANNER OR OTHER ADVERTISING, AND WE + WILL NOT BE A PARTY TO OR IN ANY WAY BE RESPONSIBLE FOR MONITORING ANY TRANSACTION + BETWEEN YOU AND ANY THIRD-PARTY PROVIDERS OF PRODUCTS OR SERVICES. AS WITH THE + PURCHASE OF A PRODUCT OR SERVICE THROUGH ANY MEDIUM OR IN ANY ENVIRONMENT, YOU SHOULD + USE YOUR BEST JUDGMENT AND EXERCISE CAUTION WHERE APPROPRIATE. +

+
+ +
+ + +
+ 17. LIMITATIONS OF LIABILITY +
+
+

+ IN NO EVENT WILL WE OR OUR DIRECTORS, EMPLOYEES, OR AGENTS BE LIABLE TO YOU OR ANY + THIRD PARTY FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, SPECIAL, + OR PUNITIVE DAMAGES, INCLUDING LOST PROFIT, LOST REVENUE, LOSS OF DATA, OR OTHER + DAMAGES ARISING FROM YOUR USE OF THE SITE, EVEN IF WE HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. NOTWITHSTANDING ANYTHING TO THE CONTRARY CONTAINED + HEREIN, OUR LIABILITY TO YOU FOR ANY CAUSE WHATSOEVER AND REGARDLESS OF THE FORM OF + THE ACTION, WILL AT ALL TIMES BE LIMITED TO THE AMOUNT PAID, IF ANY, BY YOU TO US. + CERTAIN US STATE LAWS AND INTERNATIONAL LAWS DO NOT ALLOW LIMITATIONS ON IMPLIED + WARRANTIES OR THE EXCLUSION OR LIMITATION OF CERTAIN DAMAGES. IF THESE LAWS APPLY TO + YOU, SOME OR ALL OF THE ABOVE DISCLAIMERS OR LIMITATIONS MAY NOT APPLY TO YOU, AND YOU + MAY HAVE ADDITIONAL RIGHTS. +

+
+ +
+ + +
+ 18. INDEMNIFICATION +
+
+

+ You agree to defend, indemnify, and hold us harmless, including our subsidiaries, + affiliates, and all of our respective officers, agents, partners, and employees, from + and against any loss, damage, liability, claim, or demand, including reasonable + attorneys’ fees and expenses, made by any third party due to or arising out of: (1) + your Contributions; (2) use of the Site; (3) breach of these Terms of Use; (4) any + breach of your representations and warranties set forth in these Terms of Use; (5) + your violation of the rights of a third party, including but not limited to + intellectual property rights; or (6) any overt harmful act toward any other user of + the Site with whom you connected via the Site. Notwithstanding the foregoing, we + reserve the right, at your expense, to assume the exclusive defense and control of any + matter for which you are required to indemnify us, and you agree to cooperate, at your + expense, with our defense of such claims. We will use reasonable efforts to notify you + of any such claim, action, or proceeding which is subject to this indemnification upon + becoming aware of it. +

+
+ +
+ + +
+ 19. USER DATA +
+
+

+ We will maintain certain data that you transmit to the Site for the purpose of + managing the performance of the Site, as well as data relating to your use of the + Site. Although we perform regular routine backups of data, you are solely responsible + for all data that you transmit or that relates to any activity you have undertaken + using the Site. You agree that we shall have no liability to you for any loss or + corruption of any such data, and you hereby waive any right of action against us + arising from any such loss or corruption of such data. +

+
+ +
+ + +
+ 20. ELECTRONIC COMMUNICATIONS, TRANSACTIONS, AND SIGNATURES +
+
+

+ Visiting the Site, sending us emails, and completing online forms constitute + electronic communications. You consent to receive electronic communications, and you + agree that all agreements, notices, disclosures, and other communications we provide + to you electronically, via email and on the Site, satisfy any legal requirement that + such communication be in writing. YOU HEREBY AGREE TO THE USE OF ELECTRONIC + SIGNATURES, CONTRACTS, ORDERS, AND OTHER RECORDS, AND TO ELECTRONIC DELIVERY OF + NOTICES, POLICIES, AND RECORDS OF TRANSACTIONS INITIATED OR COMPLETED BY US OR VIA THE + SITE. You hereby waive any rights or requirements under any statutes, regulations, + rules, ordinances, or other laws in any jurisdiction which require an original + signature or delivery or retention of non-electronic records, or to payments or the + granting of credits by any means other than electronic means. +

+
+ +
+ + +
+ 21. CALIFORNIA USERS AND RESIDENTS +
+
+

+ If any complaint with us is not satisfactorily resolved, you can contact the Complaint + Assistance Unit of the Division of Consumer Services of the California Department of + Consumer Affairs in writing at 1625 North Market Blvd., Suite N 112, Sacramento, + California 95834 or by telephone at (800) 952-5210 or (916) 445-1254. +

+
+ +
+ + +
+ 22. MISCELLANEOUS +
+
+

+ These Terms of Use and any policies or operating rules posted by us on the Site or in + respect to the Site constitute the entire agreement and understanding between you and + us. Our failure to exercise or enforce any right or provision of these Terms of Use + shall not operate as a waiver of such right or provision. These Terms of Use operate + to the fullest extent permissible by law. We may assign any or all of our rights and + obligations to others at any time. We shall not be responsible or liable for any loss, + damage, delay, or failure to act caused by any cause beyond our reasonable control. If + any provision or part of a provision of these Terms of Use is determined to be + unlawful, void, or unenforceable, that provision or part of the provision is deemed + severable from these Terms of Use and does not affect the validity and enforceability + of any remaining provisions. There is no joint venture, partnership, employment or + agency relationship created between you and us as a result of these Terms of Use or + use of the Site. You agree that these Terms of Use will not be construed against us by + virtue of having drafted them. You hereby waive any and all defenses you may have + based on the electronic form of these Terms of Use and the lack of signing by the + parties hereto to execute these Terms of Use. +

+
+ +
+ + +
+ 23. CONTACT US +
+
+ In order to resolve a complaint regarding the Site or to receive further information + regarding use of the Site, please contact us at: +
+
+
United Nations Development Programme +
1 United Nations Plaza +
New York, New York +
United States of America +
Phone: +1-2129065000 +
nce.digital@undp.org +
These terms of use were created using Termly's{' '} + + Terms and Conditions Generator. + +
+ +
+
+ ); +}; + +export default TermsOfUse; diff --git a/web/src/Pages/PrivacyPolicy/privacyPolicy.scss b/web/src/Pages/PrivacyPolicy/privacyPolicy.scss deleted file mode 100644 index 293aab65..00000000 --- a/web/src/Pages/PrivacyPolicy/privacyPolicy.scss +++ /dev/null @@ -1,172 +0,0 @@ -@import '../../Assets/Fonts/fonts.scss'; -@import '../../Styles/variables.scss'; - -.privacy-container { - height: 100%; - overflow-y: auto; - overflow-x: hidden; - background-color: $background-color; - - .privacy-header-container { - background-color: rgba(255, 255, 255, 0.8); - height: 90px; - padding-top: 15px; - - display: flex; - flex-direction: row; - align-items: center; - padding: 0rem 0rem 0 0vw; - cursor: pointer; - - .title { - font-family: 'MuseoSans'; - font-size: 1.2rem; - color: $logo-text-color; - font-weight: 700; - margin-right: 0.5rem; - } - - .title-sub { - font-weight: 100 !important; - font-family: 'MuseoSans'; - font-size: 1.2rem; - color: $logo-text-color; - } - - .logo { - height: 53px; - padding-left: 2vw; - - img { - object-fit: cover; - height: auto; - height: 85%; - margin-top: 6px; - } - } - - .country-name { - font-size: 0.65rem; - color: $logo-text-color; - margin-top: -5px; - font-family: 'MuseoSans'; - } - } - .privacy-body-container { - text-align: left; - justify-content: left; - background-color: white; - margin-top: 5px; - margin-bottom: 5px; - padding-bottom: 60px; - - .privacy-body { - color: $title-text-color; - padding: 20px 0px 20px 0px; - } - p { - margin: 30px; - } - table { - text-align: left; - width: auto; - padding-top: 30px; - } - .table-row3 { - text-align: center; - } - .table-row1 { - padding: 20px; - } - .privacy-body-contact { - font-weight: 700; - color: $title-text-color; - text-align: left; - padding: 20px 0px 20px 0px; - } - .privacy-sub { - text-align: center; - justify-content: center; - padding-bottom: 30px; - font-weight: 700; - color: $title-text-color; - } - .privacytitle { - font-size: 2rem; - font-weight: 700; - font-family: $primary-font-family; - color: $title-text-color; - text-align: center; - justify-content: center; - text-transform: uppercase; - padding: 50px 50px 10px 50px; - - @media (max-width: $lg-size) { - font-size: 2rem; - margin-top: 1rem; - color: $title-text-color; - line-height: 1.5em; - } - } - .privacy-subtitle { - display: flex; - text-align: left; - justify-content: left; - background-color: rgba(255, 255, 255, 0.8); - color: $title-text-color; - padding-top: 30px; - font-size: 20px; - font-weight: 700; - padding-left: 0px; - } - .privacy-subtitle-summary { - display: flex; - text-align: left; - justify-content: left; - background-color: rgba(255, 255, 255, 0.8); - color: $title-text-color; - padding-top: 30px; - font-size: 20px; - font-weight: 700; - padding-left: 0px; - } - .privacy-subline { - padding-top: 20px; - padding-left: 0px; - font-weight: 700; - color: $title-text-color; - font-size: 16px; - } - - .privacy-card-subtitle { - text-decoration: underline; - color: $title-text-color; - font-size: 20px; - font-weight: 700; - padding-bottom: 20px; - } - .privacy-card-subtitle-text { - margin: 10px 60px 10px 60px; - } - .privacy-card-container { - text-align: left; - margin: 30px 60px 30px 60px; - padding: 30px; - border-style: solid; - border-color: $common-form-input-border; - border-width: 1px; - table { - color: $title-text-color; - align-items: center; - - td { - padding-right: 20px; - } - } - } - } - - a { - word-wrap: break-word; - } -} diff --git a/web/src/Pages/PrivacyPolicy/privacyPolicy.tsx b/web/src/Pages/PrivacyPolicy/privacyPolicy.tsx deleted file mode 100644 index ac7c218e..00000000 --- a/web/src/Pages/PrivacyPolicy/privacyPolicy.tsx +++ /dev/null @@ -1,1156 +0,0 @@ -import { Col, Row } from 'antd'; -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import i18next from 'i18next'; -import sliderLogo from '../../Assets/Images/mrvlogo.svg'; -import './privacyPolicy.scss'; -import LayoutFooter from '../../Components/Footer/layout.footer'; - -const PrivacyPolicy = () => { - const navigate = useNavigate(); - - useEffect(() => { - if (localStorage.getItem('i18nextLng')!.length > 2) { - i18next.changeLanguage('en'); - } - }, []); - return ( -
- - -
navigate('/')} className="privacy-header-container"> -
- slider-logo -
-
-
-
{'TRANSPARENCY'}
-
{'SYSTEM'}
-
-
{process.env.REACT_APP_COUNTRY_NAME || 'CountryX'}
-
-
- -
-
- - -
PRIVACY NOTICE
-
Last updated February 02, 2023
-
- This privacy notice for United Nations Development Programme ("Company",{' '} - "we", "us", and "our") describes how and why we might collect, - store, use, and/or share ("process")your information when you use our services - ("Services"), such as when you: -
    -
  • - Visit our website at iverifyit.com, or any website of ours that links to this - privacy notice -
  • -
  • - Engage with us in other related ways ― including any sales, marketing, or events -
  • -
-

- Questions or concerns? Reading this privacy notice will help you understand - your privacy rights and choices. If you do not agree with our policies and - practices, please do not use our Services. If you still have any questions or - concerns, please contact us at nce.digital@undp.org. -

-
- -
- - -
SUMMARY OF KEY POINTS
-
-

- - This summary provides key points from our privacy notice, but you can find out - more details about any of these topics by clicking the link following each key - point or by using our table of contents below to find the section you are looking - for. You can also click here to go directly to our table of - contents. - -

-

- What personal information do we process? When you visit, use, or navigate our - Services, we may process personal information depending on how you interact with - United Nations Development Programme and the Services, the choices you make, and the - products and features you use. Click here to learn more. -

-

- Do we process any sensitive personal information? We do not process sensitive - personal information. -

-

- Do you receive any information from third parties? We do not receive any - information from third parties. -
-

-

- How do you process my information? We process your information to provide, - improve, and administer our Services, communicate with you, for security and fraud - prevention, and to comply with law. We may also process your information for other - purposes with your consent. We process your information only when we have a valid - legal reason to do so. Click here to learn more. -

-

- In what situations and with which parties do we share personal information?{' '} - We may share information in specific situations and with specific third parties. - Click - here to learn more. -

-

- How do we keep your information safe? We have organizational and technical - processes and procedures in place to protect your personal information. However, no - electronic transmission over the internet or information storage technology can be - guaranteed to be 100% secure, so we cannot promise or guarantee that hackers, - cybercriminals, or other unauthorized third parties will not be able to defeat our - security and improperly collect, access, steal, or modify your information. Click - here to learn more. -

-

- What are your rights? Depending on where you are located geographically, the - applicable privacy law may mean you have certain rights regarding your personal - information. Click here to learn more. -

-

- How do I exercise my rights? The easiest way to exercise your rights is by - filling out our data subject request form available - - {' '} - here - - , or by contacting us. We will consider and act upon any request in accordance with - applicable data protection laws. -

-

- Want to learn more about what United Nations Development Programme does with any - information we collect? Click here to review the notice in full. -

-
- -
- - -
- TABLE OF CONTENTS -
-
-
    -
  1. - {' '} - WHAT INFORMATION DO WE COLLECT? -
  2. -
  3. - {' '} - HOW DO WE PROCESS YOUR INFORMATION? -
  4. -
  5. - {' '} - - WHAT LEGAL BASES DO WE RELY ON TO PROCESS YOUR PERSONAL INFORMATION? - -
  6. -
  7. - {' '} - WHEN AND WITH WHOM DO WE SHARE YOUR PERSONAL INFORMATION? -
  8. -
  9. - {' '} - DO WE USE COOKIES AND OTHER TRACKING TECHNOLOGIES? -
  10. -
  11. - {' '} - HOW LONG DO WE KEEP YOUR INFORMATION? -
  12. -
  13. - {' '} - HOW DO WE KEEP YOUR INFORMATION SAFE? -
  14. -
  15. - {' '} - DO WE COLLECT INFORMATION FROM MINORS? -
  16. -
  17. - {' '} - WHAT ARE YOUR PRIVACY RIGHTS? -
  18. -
  19. - {' '} - CONTROLS FOR DO-NOT-TRACK FEATURES -
  20. -
  21. - {' '} - DO CALIFORNIA RESIDENTS HAVE SPECIFIC PRIVACY RIGHTS? -
  22. -
  23. - {' '} - DO WE MAKE UPDATES TO THIS NOTICE? -
  24. -
  25. - {' '} - HOW CAN YOU CONTACT US ABOUT THIS NOTICE? -
  26. -
  27. - {' '} - - HOW CAN YOU REVIEW, UPDATE, OR DELETE THE DATA WE COLLECT FROM YOU? - -
  28. -
-
- -
- - -
- 1. WHAT INFORMATION DO WE COLLECT? -
-
-

- Personal information you disclose to us. -

-

- - In Short: We collect personal information that you provide to us - -

-

- We collect personal information that you voluntarily provide to us when you register - on the Services, express an interest in obtaining information about us or our - products and Services, when you participate in activities on the Services, or - otherwise when you contact us. -

-

- Personal Information Provided by You. The personal information that we - collect depends on the context of your interactions with us and the Services, the - choices you make, and the products and features you use. The personal information we - collect may include the following: -
-

-
    -
  • names
  • -
  • phone numbers
  • -
  • email addresses
  • -
  • usernames
  • -
  • passwords
  • -
  • contact preferences
  • -
-

- Sensitive Information. We do not process sensitive information. -

-

- All personal information that you provide to us must be true, complete, and - accurate, and you must notify us of any changes to such personal information. -

-

- Information automatically collected -

-

- - In Short: Some information — such as your Internet Protocol (IP) address - and/or browser and device characteristics — is collected automatically when you - visit our Services. - -

-

- We automatically collect certain information when you visit, use, or navigate the - Services. This information does not reveal your specific identity (like your name or - contact information) but may include device and usage information, such as your IP - address, browser and device characteristics, operating system, language preferences, - referring URLs, device name, country, location, information about how and when you - use our Services, and other technical information. This information is primarily - needed to maintain the security and operation of our Services, and for our internal - analytics and reporting purposes. -

-

- Like many businesses, we also collect information through cookies and similar - technologies. -

-

The information we collect includes:

-
    -
  • - Log and Usage Data. Log and usage data is service-related, diagnostic, - usage, and performance information our servers automatically collect when you - access or use our Services and which we record in log files. Depending on how you - interact with us, this log data may include your IP address, device information, - browser type, and settings and information about your activity in the Services - (such as the date/time stamps associated with your usage, pages and files viewed, - searches, and other actions you take such as which features you use), device event - information (such as system activity, error reports (sometimes called "crash - dumps"), and hardware settings). -
  • -
  • - Device Data. We collect device data such as information about your - computer, phone, tablet, or other device you use to access the Services. Depending - on the device used, this device data may include information such as your IP - address (or proxy server), device and application identification numbers, - location, browser type, hardware model, Internet service provider and/or mobile - carrier, operating system, and system configuration information. -
  • -
  • - Location Data. We collect location data such as information about your - device's location, which can be either precise or imprecise. How much information - we collect depends on the type and settings of the device you use to access the - Services. For example, we may use GPS and other technologies to collect - geolocation data that tells us your current location (based on your IP address). - You can opt out of allowing us to collect this information either by refusing - access to the information or by disabling your Location setting on your device. - However, if you choose to opt out, you may not be able to use certain aspects of - the Services. -
  • -
-
- -
- - -
- 2. HOW DO WE PROCESS YOUR INFORMATION? -
-
-

- - In Short: We process your information to provide, improve, and administer - our Services, communicate with you, for security and fraud prevention, and to - comply with law. We may also process your information for other purposes with your - consent. - -

-

- - We process your personal information for a variety of reasons, depending on how - you interact with our Services, including: - -

-
    -
  • - - To facilitate account creation and authentication and otherwise manage user - accounts. - {' '} - We may process your information so you can create and log in to your account, as - well as keep your account in working order. -
  • -
  • - To request feedback. We may process your information when necessary to - request feedback and to contact you about your use of our Services. -
  • -
  • - To send you marketing and promotional communications. We may process the - personal information you send to us for our marketing purposes, if this is in - accordance with your marketing preferences. You can opt out of our marketing - emails at any time. For more information, see " - WHAT ARE YOUR PRIVACY RIGHTS?" below. -
  • -
  • - To deliver targeted advertising to you. We may process your information to - develop and display personalized content and advertising tailored to your - interests, location, and more. -
  • -
  • - To protect our Services. We may process your information as part of our - efforts to keep our Services safe and secure, including fraud monitoring and - prevention. -
  • -
  • - To identify usage trends. We may process information about how you use our - Services to better understand how they are being used so we can improve them. -
  • -
  • - To determine the effectiveness of our marketing and promotional campaigns.{' '} - We may process your information to better understand how to provide marketing and - promotional campaigns that are most relevant to you. -
  • -
  • - To save or protect an individual's vital interest. We may process your - information when necessary to save or protect an individual’s vital interest, such - as to prevent harm. -
  • -
-
- -
- - -
- 3. WHAT LEGAL BASES DO WE RELY ON TO PROCESS YOUR INFORMATION? -
-
-

- - In Short: We only process your personal information when we believe it is - necessary and we have a valid legal reason (i.e., legal basis) to do so under - applicable law, like with your consent, to comply with laws, to provide you with - services to enter into or fulfill our contractual obligations, to protect your - rights, or to fulfill our legitimate business interests. - -

-

- - - If you are located in the EU or UK, this section applies to you. - - -

-

- The General Data Protection Regulation (GDPR) and UK GDPR require us to explain the - valid legal bases we rely on in order to process your personal information. As such, - we may rely on the following legal bases to process your personal information: -

-
    -
  • - Consent. We may process your information if you have given us permission - (i.e., consent) to use your personal information for a specific purpose. You can - withdraw your consent at any time. Click here to - learn more. -
  • -
  • - Legitimate Interests. We may process your information when we believe it is - reasonably necessary to achieve our legitimate business interests and those - interests do not outweigh your interests and fundamental rights and freedoms. For - example, we may process your personal information for some of the purposes - described in order to: -
  • -
      -
    • - Send users information about special offers and discounts on our products and - services -
    • -
    • - Develop and display personalized and relevant advertising content for our users -
    • -
    • - Analyze how our services are used so we can improve them to engage and retain - users -
    • -
    • Support our marketing activities
    • -
    • Diagnose problems and/or prevent fraudulent activities
    • -
    • - Understand how our users use our products and services so we can improve user - experience -
    • -
    -
  • - Legal Obligations. We may process your information where we believe it is - necessary for compliance with our legal obligations, such as to cooperate with a - law enforcement body or regulatory agency, exercise or defend our legal rights, or - disclose your information as evidence in litigation in which we are involved. -
  • -
  • - Vital Interests. We may process your information where we believe it is - necessary to protect your vital interests or the vital interests of a third party, - such as situations involving potential threats to the safety of any person. -
  • -
-

- In legal terms, we are generally the “data controller” under European data - protection laws of the personal information described in this privacy notice, since - we determine the means and/or purposes of the data processing we perform. This - privacy notice does not apply to the personal information we process as a “data - processor” on behalf of our customers. In those situations, the customer that we - provide services to and with whom we have entered into a data processing agreement - is the “data controller” responsible for your personal information, and we merely - process your information on their behalf in accordance with your instructions. If - you want to know more about our customers' privacy practices, you should read their - privacy policies and direct any questions you have to them. -

-

- - - If you are located in Canada, this section applies to you. - - -

-

- We may process your information if you have given us specific permission (i.e., - express consent) to use your personal information for a specific purpose, or in - situations where your permission can be inferred (i.e., implied consent). You can - withdraw your consent at any time. Click here to - learn more. -

-

- In some exceptional cases, we may be legally permitted under applicable law to - process your information without your consent, including, for example: -

-
    -
  • - If collection is clearly in the interests of an individual and consent cannot be - obtained in a timely way -
  • -
  • For investigations and fraud detection and prevention
  • -
  • For business transactions provided certain conditions are met
  • -
  • - If it is contained in a witness statement and the collection is necessary to - assess, process, or settle an insurance claim -
  • -
  • - For identifying injured, ill, or deceased persons and communicating with next of - kin -
  • -
  • - If we have reasonable grounds to believe an individual has been, is, or may be - victim of financial abuse -
  • -
  • - If it is reasonable to expect collection and use with consent would compromise the - availability or the accuracy of the information and the collection is reasonable - for purposes related to investigating a breach of an agreement or a contravention - of the laws of Canada or a province -
  • -
  • - If disclosure is required to comply with a subpoena, warrant, court order, or - rules of the court relating to the production of records -
  • -
  • - If it was produced by an individual in the course of their employment, business, - or profession and the collection is consistent with the purposes for which the - information was produced -
  • -
  • - If the collection is solely for journalistic, artistic, or literary purposes -
  • -
  • - If the information is publicly available and is specified by the regulations -
  • -
-
- -
- - -
- 4. WHEN AND WITH WHOM DO WE SHARE YOUR PERSONAL INFORMATION? -
-
-

- - In Short: We may share information in specific situations described in this - section and/or with the following third parties. - -

-

We may need to share your personal information in the following situations:

-
    -
  • - Business Transfers. We may share or transfer your information in connection - with, or during negotiations of, any merger, sale of company assets, financing, or - acquisition of all or a portion of our business to another company. -
  • -
-
- -
- - -
- 5. DO WE USE COOKIES AND OTHER TRACKING TECHNOLOGIES? -
-
-

- - In Short: We may use cookies and other tracking technologies to collect and - store your information. - -

-

- We may use cookies and similar tracking technologies (like web beacons and pixels) - to access or store information. Specific information about how we use such - technologies and how you can refuse certain cookies is set out in our Cookie Notice. -

-
- -
- - -
- 6. HOW LONG DO WE KEEP YOUR INFORMATION? -
-
-

- - In Short: We keep your information for as long as necessary to fulfill the - purposes outlined in this privacy notice unless otherwise required by law. - -

-

- We will only keep your personal information for as long as it is necessary for the - purposes set out in this privacy notice, unless a longer retention period is - required or permitted by law (such as tax, accounting, or other legal requirements). - No purpose in this notice will require us keeping your personal information for - longer than the period of time in which users have an account with us. -

-

- When we have no ongoing legitimate business need to process your personal - information, we will either delete or anonymize such information, or, if this is not - possible (for example, because your personal information has been stored in backup - archives), then we will securely store your personal information and isolate it from - any further processing until deletion is possible. -

-
- -
- - -
- 7. HOW DO WE KEEP YOUR INFORMATION SAFE? -
-
-

- - In Short: We aim to protect your personal information through a system of - organizational and technical security measures. - -

-

- We have implemented appropriate and reasonable technical and organizational security - measures designed to protect the security of any personal information we process. - However, despite our safeguards and efforts to secure your information, no - electronic transmission over the Internet or information storage technology can be - guaranteed to be 100% secure, so we cannot promise or guarantee that hackers, - cybercriminals, or other unauthorized third parties will not be able to defeat our - security and improperly collect, access, steal, or modify your information. Although - we will do our best to protect your personal information, transmission of personal - information to and from our Services is at your own risk. You should only access the - Services within a secure environment. -

-
- -
- - -
- 8. DO WE COLLECT INFORMATION FROM MINORS? -
-
-

- - In Short: We do not knowingly collect data from or market to children under - 18 years of age. - -

-

- We do not knowingly solicit data from or market to children under 18 years of age. - By using the Services, you represent that you are at least 18 or that you are the - parent or guardian of such a minor and consent to such minor dependent’s use of the - Services. If we learn that personal information from users less than 18 years of age - has been collected, we will deactivate the account and take reasonable measures to - promptly delete such data from our records. If you become aware of any data we may - have collected from children under age 18, please contact us at info@panos.org.zm. -

-
- -
- - -
- 9. WHAT ARE YOUR PRIVACY RIGHTS? -
-
-

- - In Short: In some regions, such as the European Economic Area (EEA), United - Kingdom (UK), and Canada, you have rights that allow you greater access to and - control over your personal information. You may review, change, or terminate your - account at any time. - -

-

- In some regions (like the EEA, UK, and Canada), you have certain rights under - applicable data protection laws. These may include the right (i) to request access - and obtain a copy of your personal information, (ii) to request rectification or - erasure; (iii) to restrict the processing of your personal information; and (iv) if - applicable, to data portability. In certain circumstances, you may also have the - right to object to the processing of your personal information. You can make such a - request by contacting us by using the contact details provided in the section " - HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below. -

-

- We will consider and act upon any request in accordance with applicable data - protection laws. -

-

- If you are located in the EEA or UK and you believe we are unlawfully processing - your personal information, you also have the right to complain to your local data - protection supervisory authority. You can find their contact details here: - - {' '} - https://ec.europa.eu/justice/data-protection/bodies/authorities/index_en.html. - -

-

- If you are located in Switzerland, the contact details for the data protection - authorities are available here: - - {' '} - https://www.edoeb.admin.ch/edoeb/en/home.html. - -

- -
-

- - Withdrawing your consent: - {' '} - If we are relying on your consent to process your personal information, which may - be express and/or implied consent depending on the applicable law, you have the - right to withdraw your consent at any time. You can withdraw your consent at any - time by contacting us by using the contact details provided in the section " - HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below. -

-
- -

- However, please note that this will not affect the lawfulness of the processing - before its withdrawal, nor when applicable law allows, will it affect the processing - of your personal information conducted in reliance on lawful processing grounds - other than consent. -

-

- - Opting out of marketing and promotional communications: - {' '} - You can unsubscribe from our marketing and promotional communications at any time by - clicking on the unsubscribe link in the emails that we send, replying “STOP” or - “UNSUBSCRIBE” to the SMS messages that we send, or by contacting us using the - details provided in the section " - HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below. You will - then be removed from the marketing lists — however, we may still communicate with - you, for example to send you service-related messages that are necessary for the - administration and use of your account, to respond to service requests, or for other - non-marketing purposes. -

-

- Account Information -

-

- If you would at any time like to review or change the information in your account or - terminate your account, you can: -

-
    -
  • Log in to your account settings and update your user account.
  • -
-

- Upon your request to terminate your account, we will deactivate or delete your - account and information from our active databases. However, we may retain some - information in our files to prevent fraud, troubleshoot problems, assist with any - investigations, enforce our legal terms and/or comply with applicable legal - requirements. -

-

- - Cookies and similar technologies: - {' '} - Most Web browsers are set to accept cookies by default. If you prefer, you can - usually choose to set your browser to remove cookies and to reject cookies. If you - choose to remove cookies or reject cookies, this could affect certain features or - services of our Services. To opt out of interest-based advertising by advertisers on - our Services visit{' '} - http://www.aboutads.info/choices/. -

-
- -
- - -
- 10. CONTROLS FOR DO-NOT-TRACK FEATURES -
-
-

- Most web browsers and some mobile operating systems and mobile applications include - a Do-Not-Track ("DNT") feature or setting you can activate to signal your privacy - preference not to have data about your online browsing activities monitored and - collected. At this stage no uniform technology standard for recognizing and - implementing DNT signals has been finalized. As such, we do not currently respond to - DNT browser signals or any other mechanism that automatically communicates your - choice not to be tracked online. If a standard for online tracking is adopted that - we must follow in the future, we will inform you about that practice in a revised - version of this privacy notice. -

-
- -
- - -
- 11. DO CALIFORNIA RESIDENTS HAVE SPECIFIC PRIVACY RIGHTS? -
-
-

- - In Short: Yes, if you are a resident of California, you are granted - specific rights regarding access to your personal information. - -

-

- California Civil Code Section 1798.83, also known as the "Shine The Light" law, - permits our users who are California residents to request and obtain from us, once a - year and free of charge, information about categories of personal information (if - any) we disclosed to third parties for direct marketing purposes and the names and - addresses of all third parties with which we shared personal information in the - immediately preceding calendar year. If you are a California resident and would like - to make such a request, please submit your request in writing to us using the - contact information provided below. -

-

- If you are under 18 years of age, reside in California, and have a registered - account with Services, you have the right to request removal of unwanted data that - you publicly post on the Services. To request removal of such data, please contact - us using the contact information provided below and include the email address - associated with your account and a statement that you reside in California. We will - make sure the data is not publicly displayed on the Services, but please be aware - that the data may not be completely or comprehensively removed from all our systems - (e.g., backups, etc.). -

-

- CCPA Privacy Notice -

-

The California Code of Regulations defines a "resident" as:

-
    -
  1. - every individual who is in the State of California for other than a temporary or - transitory purpose and -
  2. -
  3. - every individual who is domiciled in the State of California who is outside the - State of California for a temporary or transitory purpose -
  4. -
-

All other individuals are defined as "non-residents."

-

- If this definition of "resident" applies to you, we must adhere to certain rights - and obligations regarding your personal information. -

-

- What categories of personal information do we collect? -

-

- We have collected the following categories of personal information in the past - twelve (12) months: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CategoryExamplesCollected
A. Identifiers - Contact details, such as real name, alias, postal address, telephone or mobile - contact number, unique personal identifier, online identifier, Internet Protocol - address, email address, and account name - YES
- B. Personal information categories listed in the California Customer Records - statute - - Name, contact information, education, employment, employment history, and - financial information - YES
- C. Protected classification characteristics under California or federal law - Gender and date of birthNO
D. Commercial information - Transaction information, purchase history, financial details, and payment - information - NO
E. Biometric informationFingerprints and voiceprintsNO
F. Internet or other similar network activity - Browsing history, search history, online behavior, interest data, and - interactions with our and other websites, applications, systems, and - advertisements - YES
G. Geolocation dataDevice locationYES
- H. Audio, electronic, visual, thermal, olfactory, or similar information - - Images and audio, video or call recordings created in connection with our - business activities - NO
I. Professional or employment-related information - Business contact details in order to provide you our services at a business - level or job title, work history, and professional qualifications if you apply - for a job with us - NO
J. Education InformationStudent records and directory informationNO
- K. Inferences drawn from other personal information - - Inferences drawn from any of the collected personal information listed above to - create a profile or summary about, for example, an individual’s preferences and - characteristics - NO
-

- We may also collect other personal information outside of these categories instances - where you interact with us in person, online, or by phone or mail in the context of: -

-
    -
  • Receiving help through our customer support channels;
  • -
  • Participation in customer surveys or contests; and
  • -
  • - Facilitation in the delivery of our Services and to respond to your inquiries. -
  • -
-

- How do we use and share your personal information? -

-

- - More information about our data collection and sharing practices can be found in - this privacy notice. - -

-

- You may contact us Social Media Profiles, - or by referring to the contact details at the bottom of this document. -

-

- If you are using an authorized agent to exercise your right to opt out we may deny a - request if the authorized agent does not submit proof that they have been validly - authorized to act on your behalf. -

-

- Will your information be shared with anyone else? -

-

- We may disclose your personal information with our service providers pursuant to a - written contract between us and each service provider. Each service provider is a - for-profit entity that processes the information on our behalf. -

-

- We may use your personal information for our own business purposes, such as for - undertaking internal research for technological development and demonstration. This - is not considered to be "selling" of your personal information. -

-

- United Nations Development Programme has not disclosed or sold any personal - information to third parties for a business or commercial purpose in the preceding - twelve (12) months. United Nations Development Programme will not sell personal - information in the future belonging to website visitors, users, and other consumers. -

-

- Your rights with respect to your personal data -

-

- Right to request deletion of the data — Request to delete -

-

- You can ask for the deletion of your personal information. If you ask us to delete - your personal information, we will respect your request and delete your personal - information, subject to certain exceptions provided by law, such as (but not limited - to) the exercise by another consumer of his or her right to free speech, our - compliance requirements resulting from a legal obligation, or any processing that - may be required to protect against illegal activities. -

-

- Right to be informed — Request to know -

-

Depending on the circumstances, you have a right to know:

-
    -
  • whether we collect and use your personal information;
  • -
  • the categories of personal information that we collect;
  • -
  • the purposes for which the collected personal information is used;
  • -
  • whether we sell your personal information to third parties;
  • -
  • - the categories of personal information that we sold or disclosed for a business - purpose; -
  • -
  • - the categories of third parties to whom the personal information was sold or - disclosed for a business purpose; and -
  • -
  • - the business or commercial purpose for collecting or selling personal information. -
  • -
-

- In accordance with applicable law, we are not obligated to provide or delete - consumer information that is de-identified in response to a consumer request or to - re-identify individual data to verify a consumer request. -

-

- Right to Non-Discrimination for the Exercise of a Consumer’s Privacy Rights -

-

We will not discriminate against you if you exercise your privacy rights.

-

- Verification process -

-

- Upon receiving your request, we will need to verify your identity to determine you - are the same person about whom we have the information in our system. These - verification efforts require us to ask you to provide information so that we can - match it with information you have previously provided us. For instance, depending - on the type of request you submit, we may ask you to provide certain information so - that we can match the information you provide with the information we already have - on file, or we may contact you through a communication method (e.g., phone or email) - that you have previously provided to us. We may also use other verification methods - as the circumstances dictate. -

-

- We will only use personal information provided in your request to verify your - identity or authority to make the request. To the extent possible, we will avoid - requesting additional information from you for the purposes of verification. - However, if we cannot verify your identity from the information already maintained - by us, we may request that you provide additional information for the purposes of - verifying your identity and for security or fraud-prevention purposes. We will - delete such additionally provided information as soon as we finish verifying you. -

-

- Other privacy rights -

-
    -
  • You may object to the processing of your personal information.
  • -
  • - You may request correction of your personal data if it is incorrect or no longer - relevant, or ask to restrict the processing of the information. -
  • -
  • - You can designate an authorized agent to make a request under the CCPA on your - behalf. We may deny a request from an authorized agent that does not submit proof - that they have been validly authorized to act on your behalf in accordance with - the CCPA. -
  • -
  • - You may request to opt out from future selling of your personal information to - third parties. Upon receiving an opt-out request, we will act upon the request as - soon as feasibly possible, but no later than fifteen (15) days from the date of - the request submission. -
  • -
-

- To exercise these rights, you can contact us Social Media Profiles, or by referring - to the contact details at the bottom of this document. If you have a complaint about - how we handle your data, we would like to hear from you. -

-
- -
- - -
- 12. DO WE MAKE UPDATES TO THIS NOTICE? -
-
-

- - In Short: Yes, we will update this notice as necessary to stay compliant - with relevant laws. - -

-

- We may update this privacy notice from time to time. The updated version will be - indicated by an updated "Revised" date and the updated version will be effective as - soon as it is accessible. If we make material changes to this privacy notice, we may - notify you either by prominently posting a notice of such changes or by directly - sending you a notification. We encourage you to review this privacy notice - frequently to be informed of how we are protecting your information. -

-
- -
- - -
- 13. HOW CAN YOU CONTACT US ABOUT THIS NOTICE? -
-
-

- If you have questions or comments about this notice, you may contact our Data - Protection Officer (DPO) by email at info@panos.org.zm, or by post to: -

-

- United Nations Development Programme -
- 1 United Nations Plaza -
- New York, New York, United States -

-

- - If you are a resident in the European Economic Area, the "data controller" of your - personal information is United Nations Development Programme. United Nations - Development Programme has appointed DPO to be its representative in the EEA. You - can contact them directly regarding the processing of your information by United - Nations Development Programme, or by post to: - -

-
- -
- - -
- 14. HOW CAN YOU REVIEW, UPDATE, OR DELETE THE DATA WE COLLECT FROM YOU? -
-
-

- Based on the applicable laws of your country, you may have the right to request - access to the personal information we collect from you, change that information, or - delete it in some circumstances. To request to review, update, or delete your - personal information, please submit a request form by clicking{' '} - - here. - -

-

- This privacy policy was created using Termly's{' '} - - Privacy Policy Generator. - -

-
- -
-
- -
- ); -}; - -export default PrivacyPolicy; diff --git a/web/src/Pages/TermsofUse/termsofUse.scss b/web/src/Pages/TermsofUse/termsofUse.scss deleted file mode 100644 index 0333892d..00000000 --- a/web/src/Pages/TermsofUse/termsofUse.scss +++ /dev/null @@ -1,144 +0,0 @@ -@import '../../Assets/Fonts/fonts.scss'; -@import '../../Styles/variables.scss'; - -.term-container { - height: 100%; - overflow-y: auto; - overflow-x: hidden; - background-color: $background-color; - - - .term-header-container { - background-color: rgba(255, 255, 255, 0.8); - height: 90px; - padding-top: 15px; - - display: flex; - flex-direction: row; - align-items: center; - padding: 0rem 0rem 0 0vw; - cursor: pointer; - - .title { - font-family: 'MuseoSans'; - font-size: 1.2rem; - color: $logo-text-color; - font-weight: 700; - margin-right: 0.5rem; - } - - .title-sub { - font-weight: 100 !important; - font-family: 'MuseoSans'; - font-size: 1.2rem; - color: $logo-text-color; - } - - .logo { - height: 53px; - padding-left: 2vw; - - img { - object-fit: cover; - height: auto; - height: 85%; - margin-top: 6px; - } - } - - .country-name { - font-size: 0.65rem; - color: $logo-text-color; - margin-top: -5px; - font-family: 'MuseoSans'; - } - } - .term-body-container { - text-align: left; - justify-content: left; - background-color: white; - margin-top: 5px; - margin-bottom: 5px; - padding-bottom: 60px; - - .term-body { - color: $title-text-color; - padding: 20px 0px 20px 0px; - } - .term-body-contact { - font-weight: 700; - color: $title-text-color; - text-align: left; - padding: 20px 0px 20px 0px; - } - .term-sub { - text-align: center; - justify-content: center; - padding-bottom: 30px; - font-weight: 700; - color: $title-text-color; - } - .termtitle { - font-size: 2rem; - font-weight: 700; - font-family: $primary-font-family; - color: $title-text-color; - text-align: center; - justify-content: center; - text-transform: uppercase; - padding: 50px 50px 10px 50px; - - @media (max-width: $lg-size) { - font-size: 2rem; - margin-top: 1rem; - color: $title-text-color; - line-height: 1.5em; - } - } - .term-subtitle { - display: flex; - text-align: left; - justify-content: left; - background-color: rgba(255, 255, 255, 0.8); - color: $title-text-color; - padding-top: 30px; - font-size: 20px; - font-weight: 700; - padding-left: 0px; - } - .term-subline { - padding-top: 20px; - padding-left: 0px; - font-weight: 700; - color: $title-text-color; - font-size: 16px; - } - - .term-card-subtitle { - text-decoration: underline; - color: $title-text-color; - font-size: 20px; - font-weight: 700; - padding-bottom: 20px; - } - .term-card-subtitle-text { - margin: 10px 60px 10px 60px; - } - .term-card-container { - text-align: left; - margin: 30px 60px 30px 60px; - padding: 30px; - border-style: solid; - border-color: $common-form-input-border; - border-width: 1px; - table { - color: $title-text-color; - align-items: center; - - td { - padding-right: 20px; - } - } - } - } -} diff --git a/web/src/Pages/TermsofUse/termsofUse.tsx b/web/src/Pages/TermsofUse/termsofUse.tsx deleted file mode 100644 index 851ed0d3..00000000 --- a/web/src/Pages/TermsofUse/termsofUse.tsx +++ /dev/null @@ -1,950 +0,0 @@ -import { Col, Row } from 'antd'; -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import i18next from 'i18next'; -import './termsofUse.scss'; -import LayoutFooter from '../../Components/Footer/layout.footer'; -import sliderLogo from '../../Assets/Images/mrvlogo.svg'; - -const TermsOfUse = () => { - const navigate = useNavigate(); - - useEffect(() => { - if (localStorage.getItem('i18nextLng')!.length > 2) { - i18next.changeLanguage('en'); - } - }, []); - return ( -
- - -
navigate('/')} className="term-header-container"> -
- slider-logo -
-
-
-
{'TRANSPARENCY'}
-
{'SYSTEM'}
-
-
{process.env.REACT_APP_COUNTRY_NAME || 'CountryX'}
-
-
- -
-
- - -
TERMS OF USE
-
Last updated February 02, 2023
- -
- - -
TABLE OF CONTENTS
- - -
- - -
- 1. AGREEMENT TO TERMS -
-
-

- These Terms of Use constitute a legally binding agreement made between you, whether - personally or on behalf of an entity (“you”) and United Nations Development - Programme ("Company," “we," “us," or “our”), concerning your access to and use of - the{' '} - - https://carbreg.org - {' '} - website as well as any other media form, media channel, mobile website or mobile - application related, linked, or otherwise connected thereto (collectively, the - “Site”). You agree that by accessing the Site, you have read, understood, and agreed - to be bound by all of these Terms of Use. IF YOU DO NOT AGREE WITH ALL OF THESE - TERMS OF USE, THEN YOU ARE EXPRESSLY PROHIBITED FROM USING THE SITE AND YOU MUST - DISCONTINUE USE IMMEDIATELY. -

-

- Supplemental terms and conditions or documents that may be posted on the Site from - time to time are hereby expressly incorporated herein by reference. We reserve the - right, in our sole discretion, to make changes or modifications to these Terms of - Use from time to time. We will alert you about any changes by updating the “Last - updated” date of these Terms of Use, and you waive any right to receive specific - notice of each such change. Please ensure that you check the applicable Terms every - time you use our Site so that you understand which Terms apply. You will be subject - to, and will be deemed to have been made aware of and to have accepted, the changes - in any revised Terms of Use by your continued use of the Site after the date such - revised Terms of Use are posted. -

-

- The information provided on the Site is not intended for distribution to or use by - any person or entity in any jurisdiction or country where such distribution or use - would be contrary to law or regulation or which would subject us to any registration - requirement within such jurisdiction or country. Accordingly, those persons who - choose to access the Site from other locations do so on their own initiative and are - solely responsible for compliance with local laws, if and to the extent local laws - are applicable. -

-

- The Site is not tailored to comply with industry-specific regulations (Health - Insurance Portability and Accountability Act (HIPAA), Federal Information Security - Management Act (FISMA), etc.), so if your interactions would be subjected to such - laws, you may not use this Site. You may not use the Site in a way that would - violate the Gramm-Leach-Bliley Act (GLBA). -
-

-

- The Site is intended for users who are at least 18 years old. Persons under the age - of 18 are not permitted to use or register for the Site. -

-
- -
- - -
- 2. INTELLECTUAL PROPERTY RIGHTS -
-
- Works published on this website are under the{' '} - - Creative Commons Attribution 4.0 International (CC BY 4.0) license. - -
You are free to: -
⦾ Share — copy and redistribute the material in any medium or format. -
⦾ Adapt — remix, transform, and build upon the material for any purpose, even - commercially. -
Under the following terms: -
⦾ Attribution — You must give{' '} - appropriate credit, provide - a link to the license, and indicate if changes were made. You may do so in any - reasonable manner, but not in any way that suggests the licensor endorses you or your - use
⦾ No additional restrictions — You may not apply legal terms or - technological measures that legally restrict others from doing anything the license - permits. Unless otherwise indicated, the Site is our proprietary property and all - source code, databases, functionality, software, website designs, audio, video, text, - photographs, and graphics on the Site (collectively, the “Content”) and the - trademarks, service marks, and logos contained therein (the “Marks”) are owned or - controlled by us or licensed to us, and are protected by copyright and trademark laws - and various other intellectual property rights and unfair competition laws of the - United States, international copyright laws, and international conventions. The - Content and the Marks are provided on the Site “AS IS” for your information and - personal use only. Except as expressly provided in these Terms of Use, no part of the - Site and no Content or Marks may be copied, reproduced, aggregated, republished, - uploaded, posted, publicly displayed, encoded, translated, transmitted, distributed, - sold, licensed, or otherwise exploited for any commercial purpose whatsoever, without - our express prior written permission.{' '} -

- Provided that you are eligible to use the Site, you are granted a limited license to - access and use the Site and to download or print a copy of any portion of the - Content to which you have properly gained access solely for your personal, - non-commercial use. We reserve all rights not expressly granted to you in and to the - Site, the Content and the Marks. -

-
- -
- - -
- 3. USER REPRESENTATIONS -
-
-

- By using the Site, you represent and warrant that: (1) you have the legal capacity - and you agree to comply with these Terms of Use; (2) you are not a minor in the - jurisdiction in which you reside; (3) you will not access the Site through automated - or non-human means, whether through a bot, script, or otherwise; (4) you will not - use the Site for any illegal or unauthorized purpose; and (5) your use of the Site - will not violate any applicable law or regulation. -

{' '} -

- If you provide any information that is untrue, inaccurate, not current, or - incomplete, we have the right to suspend or terminate your account and refuse any - and all current or future use of the Site (or any portion thereof). -

-
- -
- - -
- 4. PROHIBITED ACTIVITIES -
-
-

- You may not access or use the Site for any purpose other than that for which we make - the Site available. The Site may not be used in connection with any commercial - endeavors except those that are specifically endorsed or approved by us. -

-

As a user of the Site, you agree not to:

-
    -
  • - Systematically retrieve data or other content from the Site to create or compile, - directly or indirectly, a collection, compilation, database, or directory without - written permission from us. -
  • -
  • - Trick, defraud, or mislead us and other users, especially in any attempt to learn - sensitive account information such as user passwords. -
  • -
  • - Circumvent, disable, or otherwise interfere with security-related features of the - Site, including features that prevent or restrict the use or copying of any - Content or enforce limitations on the use of the Site and/or the Content contained - therein. -
  • -
  • Disparage, tarnish, or otherwise harm, in our opinion, us and/or the Site.
  • -
  • - Use any information obtained from the Site in order to harass, abuse, or harm - another person. -
  • -
  • - Make improper use of our support services or submit false reports of abuse or - misconduct. -
  • -
  • - Use the Site in a manner inconsistent with any applicable laws or regulations. -
  • -
  • Engage in unauthorized framing of or linking to the Site.
  • -
  • - Upload or transmit (or attempt to upload or to transmit) viruses, Trojan horses, - or other material, including excessive use of capital letters and spamming - (continuous posting of repetitive text), that interferes with any party’s - uninterrupted use and enjoyment of the Site or modifies, impairs, disrupts, - alters, or interferes with the use, features, functions, operation, or maintenance - of the Site. -
  • -
  • - Engage in any automated use of the system, such as using scripts to send comments - or messages, or using any data mining, robots, or similar data gathering and - extraction tools. -
  • -
  • Delete the copyright or other proprietary rights notice from any Content.
  • -
  • - Attempt to impersonate another user or person or use the username of another user. -
  • -
  • - Upload or transmit (or attempt to upload or to transmit) any material that acts as - a passive or active information collection or transmission mechanism, including - without limitation, clear graphics interchange formats (“gifs”), 1×1 pixels, web - bugs, cookies, or other similar devices (sometimes referred to as “spyware” or - “passive collection mechanisms” or “pcms”). -
  • -
  • - Interfere with, disrupt, or create an undue burden on the Site or the networks or - services connected to the Site. -
  • -
  • - Harass, annoy, intimidate, or threaten any of our employees or agents engaged in - providing any portion of the Site to you. -
  • -
  • - Attempt to bypass any measures of the Site designed to prevent or restrict access - to the Site, or any portion of the Site. -
  • -
  • - Copy or adapt the Site’s software, including but not limited to Flash, PHP, HTML, - JavaScript, or other code. -
  • -
  • - Except as permitted by applicable law, decipher, decompile, disassemble, or - reverse engineer any of the software comprising or in any way making up a part of - the Site. -
  • -
  • - Except as may be the result of standard search engine or Internet browser usage, - use, launch, develop, or distribute any automated system, including without - limitation, any spider, robot, cheat utility, scraper, or offline reader that - accesses the Site, or using or launching any unauthorized script or other - software. -
  • -
  • Use a buying agent or purchasing agent to make purchases on the Site.
  • -
  • - Make any unauthorized use of the Site, including collecting usernames and/or email - addresses of users by electronic or other means for the purpose of sending - unsolicited email, or creating user accounts by automated means or under false - pretenses. -
  • -
  • - Use the Site as part of any effort to compete with us or otherwise use the Site - and/or the Content for any revenue-generating endeavor or commercial enterprise. -
  • -
  • Use the Site to advertise or offer to sell goods and services.
  • -
-
- -
- - -
- 5. USER GENERATED CONTRIBUTIONS -
-
-

- The Site may invite you to chat, contribute to, or participate in blogs, message - boards, online forums, and other functionality, and may provide you with the - opportunity to create, submit, post, display, transmit, perform, publish, - distribute, or broadcast content and materials to us or on the Site, including but - not limited to text, writings, video, audio, photographs, graphics, comments, - suggestions, or personal information or other material (collectively, - "Contributions"). Contributions may be viewable by other users of the Site and - through third-party websites. As such, any Contributions you transmit may be treated - as non-confidential and non-proprietary. When you create or make available any - Contributions, you thereby represent and warrant that: -

-
    -
  • - The creation, distribution, transmission, public display, or performance, and the - accessing, downloading, or copying of your Contributions do not and will not - infringe the proprietary rights, including but not limited to the copyright, - patent, trademark, trade secret, or moral rights of any third party. -
  • -
  • - You are the creator and owner of or have the necessary licenses, rights, consents, - releases, and permissions to use and to authorize us, the Site, and other users of - the Site to use your Contributions in any manner contemplated by the Site and - these Terms of Use. -
  • -
  • - You have the written consent, release, and/or permission of each and every - identifiable individual person in your Contributions to use the name or likeness - of each and every such identifiable individual person to enable inclusion and use - of your Contributions in any manner contemplated by the Site and these Terms of - Use. -
  • -
  • Your Contributions are not false, inaccurate, or misleading.
  • -
  • - Your Contributions are not unsolicited or unauthorized advertising, promotional - materials, pyramid schemes, chain letters, spam, mass mailings, or other forms of - solicitation. -
  • -
  • - Your Contributions are not obscene, lewd, lascivious, filthy, violent, harassing, - libelous, slanderous, or otherwise objectionable (as determined by us). -
  • -
  • - Your Contributions do not ridicule, mock, disparage, intimidate, or abuse anyone. -
  • -
  • - Your Contributions are not used to harass or threaten (in the legal sense of those - terms) any other person and to promote violence against a specific person or class - of people. -
  • -
  • Your Contributions do not violate any applicable law, regulation, or rule.
  • -
  • - Your Contributions do not violate the privacy or publicity rights of any third - party. -
  • -
  • - Your Contributions do not violate any applicable law concerning child pornography, - or otherwise intended to protect the health or well-being of minors. -
  • -
  • - Your Contributions do not include any offensive comments that are connected to - race, national origin, gender, sexual preference, or physical handicap. -
  • -
  • - Your Contributions do not otherwise violate, or link to material that violates, - any provision of these Terms of Use, or any applicable law or regulation. -
  • -
-

- Any use of the Site in violation of the foregoing violates these Terms of Use and - may result in, among other things, termination or suspension of your rights to use - the Site. -

-
- -
- - -
- 6. CONTRIBUTION LICENSE -
-
-

- By posting your Contributions to any part of the Site, you automatically grant, and - you represent and warrant that you have the right to grant, to us an unrestricted, - unlimited, irrevocable, perpetual, non-exclusive, transferable, royalty-free, - fully-paid, worldwide right, and license to host, use, copy, reproduce, disclose, - sell, resell, publish, broadcast, retitle, archive, store, cache, publicly perform, - publicly display, reformat, translate, transmit, excerpt (in whole or in part), and - distribute such Contributions (including, without limitation, your image and voice) - for any purpose, commercial, advertising, or otherwise, and to prepare derivative - works of, or incorporate into other works, such Contributions, and grant and - authorize sublicenses of the foregoing. The use and distribution may occur in any - media formats and through any media channels. -

-

- This license will apply to any form, media, or technology now known or hereafter - developed, and includes our use of your name, company name, and franchise name, as - applicable, and any of the trademarks, service marks, trade names, logos, and - personal and commercial images you provide. You waive all moral rights in your - Contributions, and you warrant that moral rights have not otherwise been asserted in - your Contributions. -

-

- We do not assert any ownership over your Contributions. You retain full ownership of - all of your Contributions and any intellectual property rights or other proprietary - rights associated with your Contributions. We are not liable for any statements or - representations in your Contributions provided by you in any area on the Site. You - are solely responsible for your Contributions to the Site and you expressly agree to - exonerate us from any and all responsibility and to refrain from any legal action - against us regarding your Contributions. -

-

- We have the right, in our sole and absolute discretion, (1) to edit, redact, or - otherwise change any Contributions; (2) to re-categorize any Contributions to place - them in more appropriate locations on the Site; and (3) to pre-screen or delete any - Contributions at any time and for any reason, without notice. We have no obligation - to monitor your Contributions. -

-
- -
- - -
- 7. SUBMISSIONS -
-
-

- You acknowledge and agree that any questions, comments, suggestions, ideas, - feedback, or other information regarding the Site ("Submissions") provided by you to - us are non-confidential and shall become our sole property. We shall own exclusive - rights, including all intellectual property rights, and shall be entitled to the - unrestricted use and dissemination of these Submissions for any lawful purpose, - commercial or otherwise, without acknowledgment or compensation to you. You hereby - waive all moral rights to any such Submissions, and you hereby warrant that any such - Submissions are original with you or that you have the right to submit such - Submissions. You agree there shall be no recourse against us for any alleged or - actual infringement or misappropriation of any proprietary right in your - Submissions. -

-
- -
- - -
- 8. THIRD-PARTY WEBSITE AND CONTENT -
-
-

- The Site may contain (or you may be sent via the Site) links to other websites - ("Third-Party Websites") as well as articles, photographs, text, graphics, pictures, - designs, music, sound, video, information, applications, software, and other content - or items belonging to or originating from third parties ("Third-Party Content"). - Such Third-Party Websites and Third-Party Content are not investigated, monitored, - or checked for accuracy, appropriateness, or completeness by us, and we are not - responsible for any Third-Party Websites accessed through the Site or any - Third-Party Content posted on, available through, or installed from the Site, - including the content, accuracy, offensiveness, opinions, reliability, privacy - practices, or other policies of or contained in the Third-Party Websites or the - Third-Party Content. Inclusion of, linking to, or permitting the use or installation - of any Third-Party Websites or any Third-Party Content does not imply approval or - endorsement thereof by us. If you decide to leave the Site and access the - Third-Party Websites or to use or install any Third-Party Content, you do so at your - own risk, and you should be aware these Terms of Use no longer govern. You should - review the applicable terms and policies, including privacy and data gathering - practices, of any website to which you navigate from the Site or relating to any - applications you use or install from the Site. Any purchases you make through - Third-Party Websites will be through other websites and from other companies, and we - take no responsibility whatsoever in relation to such purchases which are - exclusively between you and the applicable third party. You agree and acknowledge - that we do not endorse the products or services offered on Third-Party Websites and - you shall hold us harmless from any harm caused by your purchase of such products or - services. Additionally, you shall hold us harmless from any losses sustained by you - or harm caused to you relating to or resulting in any way from any Third-Party - Content or any contact with Third-Party Websites. -

-
- -
- - -
- 9. SITE MANAGEMENT -
-
-

- We reserve the right, but not the obligation, to: (1) monitor the Site for - violations of these Terms of Use; (2) take appropriate legal action against anyone - who, in our sole discretion, violates the law or these Terms of Use, including - without limitation, reporting such user to law enforcement authorities; (3) in our - sole discretion and without limitation, refuse, restrict access to, limit the - availability of, or disable (to the extent technologically feasible) any of your - Contributions or any portion thereof; (4) in our sole discretion and without - limitation, notice, or liability, to remove from the Site or otherwise disable all - files and content that are excessive in size or are in any way burdensome to our - systems; and (5) otherwise manage the Site in a manner designed to protect our - rights and property and to facilitate the proper functioning of the Site. -

-
- -
- - -
- 10. PRIVACY POLICY -
-
-

- We care about data privacy and security. Please review our Privacy Policy: - /privacy-policy. By using the Site, you agree to be bound by our Privacy Policy, - which is incorporated into these Terms of Use. Please be advised the Site is hosted - in the United States. If you access the Site from any other region of the world with - laws or other requirements governing personal data collection, use, or disclosure - that differ from applicable laws in the United States, then through your continued - use of the Site, you are transferring your data to the United States, and you agree - to have your data transferred to and processed in the United States. -

-
- -
- - -
- 11. TERM AND TERMINATION -
-
-

- These Terms of Use shall remain in full force and effect while you use the Site. - WITHOUT LIMITING ANY OTHER PROVISION OF THESE TERMS OF USE, WE RESERVE THE RIGHT TO, - IN OUR SOLE DISCRETION AND WITHOUT NOTICE OR LIABILITY, DENY ACCESS TO AND USE OF - THE SITE (INCLUDING BLOCKING CERTAIN IP ADDRESSES), TO ANY PERSON FOR ANY REASON OR - FOR NO REASON, INCLUDING WITHOUT LIMITATION FOR BREACH OF ANY REPRESENTATION, - WARRANTY, OR COVENANT CONTAINED IN THESE TERMS OF USE OR OF ANY APPLICABLE LAW OR - REGULATION. WE MAY TERMINATE YOUR USE OR PARTICIPATION IN THE SITE OR DELETE ANY - CONTENT OR INFORMATION THAT YOU POSTED AT ANY TIME, WITHOUT WARNING, IN OUR SOLE - DISCRETION. -
If we terminate or suspend your account for any reason, you are prohibited - from registering and creating a new account under your name, a fake or borrowed - name, or the name of any third party, even if you may be acting on behalf of the - third party. In addition to terminating or suspending your account, we reserve the - right to take appropriate legal action, including without limitation pursuing civil, - criminal, and injunctive redress. -

-
- -
- - -
- 12. MODIFICATIONS AND INTERRUPTIONS -
-
-

- We reserve the right to change, modify, or remove the contents of the Site at any - time or for any reason at our sole discretion without notice. However, we have no - obligation to update any information on our Site. We also reserve the right to - modify or discontinue all or part of the Site without notice at any time. We will - not be liable to you or any third party for any modification, price change, - suspension, or discontinuance of the Site. -

-

- We cannot guarantee the Site will be available at all times. We may experience - hardware, software, or other problems or need to perform maintenance related to the - Site, resulting in interruptions, delays, or errors. We reserve the right to change, - revise, update, suspend, discontinue, or otherwise modify the Site at any time or - for any reason without notice to you. You agree that we have no liability whatsoever - for any loss, damage, or inconvenience caused by your inability to access or use the - Site during any downtime or discontinuance of the Site. Nothing in these Terms of - Use will be construed to obligate us to maintain and support the Site or to supply - any corrections, updates, or releases in connection therewith. -

-
- -
- - -
- 13. GOVERNING LAW{' '} -
-
-

- These Terms of Use and your use of the Site are governed by and construed in - accordance with the laws of the State of New York applicable to agreements made and - to be entirely performed within the State of New York, without regard to its - conflict of law principles. -

-
- -
- - -
- 14. DISPUTE RESOLUTION -
-
Informal Negotiations
-
-

- To expedite resolution and control the cost of any dispute, controversy, or claim - related to these Terms of Use (each "Dispute" and collectively, the “Disputes”) - brought by either you or us (individually, a “Party” and collectively, the - “Parties”), the Parties agree to first attempt to negotiate any Dispute (except - those Disputes expressly provided below) informally for at least thirty (30) days - before initiating arbitration. Such informal negotiations commence upon written - notice from one Party to the other Party. -

-
-
Binding Arbitration
-
-

- If the Parties are unable to resolve a Dispute through informal negotiations, the - Dispute (except those Disputes expressly excluded below) will be finally and - exclusively resolved through binding arbitration. YOU UNDERSTAND THAT WITHOUT THIS - PROVISION, YOU WOULD HAVE THE RIGHT TO SUE IN COURT AND HAVE A JURY TRIAL. The - arbitration shall be commenced and conducted under the Commercial Arbitration Rules - of the American Arbitration Association ("AAA") and, where appropriate, the AAA’s - Supplementary Procedures for Consumer Related Disputes ("AAA Consumer Rules"), both - of which are available at the AAA website:{' '} - - www.adr.org - - . Your arbitration fees and your share of arbitrator compensation shall be governed - by the AAA Consumer Rules and, where appropriate, limited by the AAA Consumer Rules. - The arbitration may be conducted in person, through the submission of documents, by - phone, or online. The arbitrator will make a decision in writing, but need not - provide a statement of reasons unless requested by either Party. The arbitrator must - follow applicable law, and any award may be challenged if the arbitrator fails to do - so. Except where otherwise required by the applicable AAA rules or applicable law, - the arbitration will take place in Manhattan, New York. Except as otherwise provided - herein, the Parties may litigate in court to compel arbitration, stay proceedings - pending arbitration, or to confirm, modify, vacate, or enter judgment on the award - entered by the arbitrator. -

-

- If for any reason, a Dispute proceeds in court rather than arbitration, the Dispute - shall be commenced or prosecuted in the state and federal courts located in - Manhattan, New York, and the Parties hereby consent to, and waive all defenses of - lack of personal jurisdiction, and forum non conveniens with respect to venue and - jurisdiction in such state and federal courts. Application of the United Nations - Convention on Contracts for the International Sale of Goods and the Uniform Computer - Information Transaction Act (UCITA) is excluded from these Terms of Use. -

-

- In no event shall any Dispute brought by either Party related in any way to the Site - be commenced more than one (1) years after the cause of action arose. If this - provision is found to be illegal or unenforceable, then neither Party will elect to - arbitrate any Dispute falling within that portion of this provision found to be - illegal or unenforceable, and such Dispute shall be decided by a court of competent - jurisdiction within the courts listed for jurisdiction above, and the Parties agree - to submit to the personal jurisdiction of that court. -

-
-
Restrictions
-
-

- The Parties agree that any arbitration shall be limited to the Dispute between the - Parties individually. To the full extent permitted by law, (a) no arbitration shall - be joined with any other proceeding; (b) there is no right or authority for any - Dispute to be arbitrated on a class-action basis or to utilize class action - procedures; and (c) there is no right or authority for any Dispute to be brought in - a purported representative capacity on behalf of the general public or any other - persons. -

-
-
Exceptions to Informal Negotiations and Arbitration
-
-

- The Parties agree that the following Disputes are not subject to the above - provisions concerning informal negotiations and binding arbitration: (a) any - Disputes seeking to enforce or protect, or concerning the validity of, any of the - intellectual property rights of a Party; (b) any Dispute related to, or arising - from, allegations of theft, piracy, invasion of privacy, or unauthorized use; and - (c) any claim for injunctive relief. If this provision is found to be illegal or - unenforceable, then neither Party will elect to arbitrate any Dispute falling within - that portion of this provision found to be illegal or unenforceable and such Dispute - shall be decided by a court of competent jurisdiction within the courts listed for - jurisdiction above, and the Parties agree to submit to the personal jurisdiction of - that court. -

-
- -
- - -
- 15. CORRECTIONS -
-
-

- There may be information on the Site that contains typographical errors, - inaccuracies, or omissions, including descriptions, pricing, availability, and - various other information. We reserve the right to correct any errors, inaccuracies, - or omissions and to change or update the information on the Site at any time, - without prior notice. -

-
- -
- - -
- 16. DISCLAIMER -
-
-

- THE SITE IS PROVIDED ON AN AS-IS AND AS-AVAILABLE BASIS. YOU AGREE THAT YOUR USE OF - THE SITE AND OUR SERVICES WILL BE AT YOUR SOLE RISK. TO THE FULLEST EXTENT PERMITTED - BY LAW, WE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, IN CONNECTION WITH THE SITE - AND YOUR USE THEREOF, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. WE MAKE NO - WARRANTIES OR REPRESENTATIONS ABOUT THE ACCURACY OR COMPLETENESS OF THE SITE’S - CONTENT OR THE CONTENT OF ANY WEBSITES LINKED TO THE SITE AND WE WILL ASSUME NO - LIABILITY OR RESPONSIBILITY FOR ANY (1) ERRORS, MISTAKES, OR INACCURACIES OF CONTENT - AND MATERIALS, (2) PERSONAL INJURY OR PROPERTY DAMAGE, OF ANY NATURE WHATSOEVER, - RESULTING FROM YOUR ACCESS TO AND USE OF THE SITE, (3) ANY UNAUTHORIZED ACCESS TO OR - USE OF OUR SECURE SERVERS AND/OR ANY AND ALL PERSONAL INFORMATION AND/OR FINANCIAL - INFORMATION STORED THEREIN, (4) ANY INTERRUPTION OR CESSATION OF TRANSMISSION TO OR - FROM THE SITE, (5) ANY BUGS, VIRUSES, TROJAN HORSES, OR THE LIKE WHICH MAY BE - TRANSMITTED TO OR THROUGH THE SITE BY ANY THIRD PARTY, AND/OR (6) ANY ERRORS OR - OMISSIONS IN ANY CONTENT AND MATERIALS OR FOR ANY LOSS OR DAMAGE OF ANY KIND - INCURRED AS A RESULT OF THE USE OF ANY CONTENT POSTED, TRANSMITTED, OR OTHERWISE - MADE AVAILABLE VIA THE SITE. WE DO NOT WARRANT, ENDORSE, GUARANTEE, OR ASSUME - RESPONSIBILITY FOR ANY PRODUCT OR SERVICE ADVERTISED OR OFFERED BY A THIRD PARTY - THROUGH THE SITE, ANY HYPERLINKED WEBSITE, OR ANY WEBSITE OR MOBILE APPLICATION - FEATURED IN ANY BANNER OR OTHER ADVERTISING, AND WE WILL NOT BE A PARTY TO OR IN ANY - WAY BE RESPONSIBLE FOR MONITORING ANY TRANSACTION BETWEEN YOU AND ANY THIRD-PARTY - PROVIDERS OF PRODUCTS OR SERVICES. AS WITH THE PURCHASE OF A PRODUCT OR SERVICE - THROUGH ANY MEDIUM OR IN ANY ENVIRONMENT, YOU SHOULD USE YOUR BEST JUDGMENT AND - EXERCISE CAUTION WHERE APPROPRIATE. -

-
- -
- - -
- 17. LIMITATIONS OF LIABILITY -
-
-

- IN NO EVENT WILL WE OR OUR DIRECTORS, EMPLOYEES, OR AGENTS BE LIABLE TO YOU OR ANY - THIRD PARTY FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, SPECIAL, - OR PUNITIVE DAMAGES, INCLUDING LOST PROFIT, LOST REVENUE, LOSS OF DATA, OR OTHER - DAMAGES ARISING FROM YOUR USE OF THE SITE, EVEN IF WE HAVE BEEN ADVISED OF THE - POSSIBILITY OF SUCH DAMAGES. NOTWITHSTANDING ANYTHING TO THE CONTRARY CONTAINED - HEREIN, OUR LIABILITY TO YOU FOR ANY CAUSE WHATSOEVER AND REGARDLESS OF THE FORM OF - THE ACTION, WILL AT ALL TIMES BE LIMITED TO THE AMOUNT PAID, IF ANY, BY YOU TO US. - CERTAIN US STATE LAWS AND INTERNATIONAL LAWS DO NOT ALLOW LIMITATIONS ON IMPLIED - WARRANTIES OR THE EXCLUSION OR LIMITATION OF CERTAIN DAMAGES. IF THESE LAWS APPLY TO - YOU, SOME OR ALL OF THE ABOVE DISCLAIMERS OR LIMITATIONS MAY NOT APPLY TO YOU, AND - YOU MAY HAVE ADDITIONAL RIGHTS. -

-
- -
- - -
- 18. INDEMNIFICATION -
-
-

- You agree to defend, indemnify, and hold us harmless, including our subsidiaries, - affiliates, and all of our respective officers, agents, partners, and employees, - from and against any loss, damage, liability, claim, or demand, including reasonable - attorneys’ fees and expenses, made by any third party due to or arising out of: (1) - your Contributions; (2) use of the Site; (3) breach of these Terms of Use; (4) any - breach of your representations and warranties set forth in these Terms of Use; (5) - your violation of the rights of a third party, including but not limited to - intellectual property rights; or (6) any overt harmful act toward any other user of - the Site with whom you connected via the Site. Notwithstanding the foregoing, we - reserve the right, at your expense, to assume the exclusive defense and control of - any matter for which you are required to indemnify us, and you agree to cooperate, - at your expense, with our defense of such claims. We will use reasonable efforts to - notify you of any such claim, action, or proceeding which is subject to this - indemnification upon becoming aware of it. -

-
- -
- - -
- 19. USER DATA -
-
-

- We will maintain certain data that you transmit to the Site for the purpose of - managing the performance of the Site, as well as data relating to your use of the - Site. Although we perform regular routine backups of data, you are solely - responsible for all data that you transmit or that relates to any activity you have - undertaken using the Site. You agree that we shall have no liability to you for any - loss or corruption of any such data, and you hereby waive any right of action - against us arising from any such loss or corruption of such data. -

-
- -
- - -
- 20. ELECTRONIC COMMUNICATIONS, TRANSACTIONS, AND SIGNATURES -
-
-

- Visiting the Site, sending us emails, and completing online forms constitute - electronic communications. You consent to receive electronic communications, and you - agree that all agreements, notices, disclosures, and other communications we provide - to you electronically, via email and on the Site, satisfy any legal requirement that - such communication be in writing. YOU HEREBY AGREE TO THE USE OF ELECTRONIC - SIGNATURES, CONTRACTS, ORDERS, AND OTHER RECORDS, AND TO ELECTRONIC DELIVERY OF - NOTICES, POLICIES, AND RECORDS OF TRANSACTIONS INITIATED OR COMPLETED BY US OR VIA - THE SITE. You hereby waive any rights or requirements under any statutes, - regulations, rules, ordinances, or other laws in any jurisdiction which require an - original signature or delivery or retention of non-electronic records, or to - payments or the granting of credits by any means other than electronic means. -

-
- -
- - -
- 21. CALIFORNIA USERS AND RESIDENTS -
-
-

- If any complaint with us is not satisfactorily resolved, you can contact the - Complaint Assistance Unit of the Division of Consumer Services of the California - Department of Consumer Affairs in writing at 1625 North Market Blvd., Suite N 112, - Sacramento, California 95834 or by telephone at (800) 952-5210 or (916) 445-1254. -

-
- -
- - -
- 22. MISCELLANEOUS -
-
-

- These Terms of Use and any policies or operating rules posted by us on the Site or - in respect to the Site constitute the entire agreement and understanding between you - and us. Our failure to exercise or enforce any right or provision of these Terms of - Use shall not operate as a waiver of such right or provision. These Terms of Use - operate to the fullest extent permissible by law. We may assign any or all of our - rights and obligations to others at any time. We shall not be responsible or liable - for any loss, damage, delay, or failure to act caused by any cause beyond our - reasonable control. If any provision or part of a provision of these Terms of Use is - determined to be unlawful, void, or unenforceable, that provision or part of the - provision is deemed severable from these Terms of Use and does not affect the - validity and enforceability of any remaining provisions. There is no joint venture, - partnership, employment or agency relationship created between you and us as a - result of these Terms of Use or use of the Site. You agree that these Terms of Use - will not be construed against us by virtue of having drafted them. You hereby waive - any and all defenses you may have based on the electronic form of these Terms of Use - and the lack of signing by the parties hereto to execute these Terms of Use. -

-
- -
- - -
- 23. CONTACT US -
-
- In order to resolve a complaint regarding the Site or to receive further information - regarding use of the Site, please contact us at: -
-
-
United Nations Development Programme -
1 United Nations Plaza -
New York, New York -
United States of America -
Phone: +1-2129065000 -
nce.digital@undp.org -
These terms of use were created using Termly's{' '} - - Terms and Conditions Generator. - -
- -
-
- -
- ); -}; - -export default TermsOfUse; diff --git a/web/src/locales/i18n/homepage/en.json b/web/src/locales/i18n/homepage/en.json index 6c8df997..372b08af 100644 --- a/web/src/locales/i18n/homepage/en.json +++ b/web/src/locales/i18n/homepage/en.json @@ -21,10 +21,10 @@ "reslist1":"Sample Letter of Intent", "reslist2":"Article 6.2 Initial report template.", "reslist3":"Regular Information Template.", - "footertext1":"National Carbon Registry: This is a demo. Countries may wish to add a tagline here.", + "footertext1":"National NDC Transparency System: This is a demo site. Footer text can be edited.", "antarctic":"CountryX", "Cookie":"Cookie Policy", - "codeconduct":"Code of Conduct", + "codeOfConduct":"Code of Conduct", "terms":"Terms of Use", "privacy":"Privacy Policy", "Help": "Help", From 97015e8e66f9073a15d0fa6788da2f6b786ba8a1 Mon Sep 17 00:00:00 2001 From: tharindugayanga Date: Tue, 22 Oct 2024 13:14:49 +0530 Subject: [PATCH 06/66] TRAN-190 - Transparency new client changes --- .github/workflows/test-service-build.yml | 10 ++-- .../services/src/activity/activity.service.ts | 20 +++++++ backend/services/src/dtos/action.dto.ts | 3 +- backend/services/src/dtos/actionUpdate.dto.ts | 3 +- backend/services/src/dtos/activity.dto.ts | 18 ++++++- .../src/dtos/activity.response.dto.ts | 4 +- .../services/src/dtos/activityUpdate.dto.ts | 20 ++++++- backend/services/src/dtos/kpi.dto.ts | 7 +-- backend/services/src/dtos/kpi.update.dto.ts | 7 +-- backend/services/src/dtos/programme.dto.ts | 25 +++++---- .../services/src/dtos/programme.view.dto.ts | 2 +- .../services/src/dtos/programmeUpdate.dto.ts | 18 +++---- backend/services/src/dtos/project.dto.ts | 31 ++++++----- .../services/src/dtos/projectUpdate.dto.ts | 31 ++++++----- .../src/entities/action.view.entity.ts | 8 ++- .../services/src/entities/activity.entity.ts | 9 +++- .../src/entities/annexThree.view.entity.ts | 5 +- backend/services/src/entities/kpi.entity.ts | 3 +- .../services/src/entities/programme.entity.ts | 8 ++- .../src/entities/programme.view.entity.ts | 7 +-- .../services/src/entities/project.entity.ts | 8 ++- .../src/entities/project.view.entity.ts | 5 ++ .../src/entities/report.five.view.entity.ts | 2 +- .../services/src/entities/support.entity.ts | 4 ++ backend/services/src/enums/action.enum.ts | 3 +- backend/services/src/enums/kpi.enum.ts | 14 ++--- backend/services/src/i18n/en/activity.json | 3 +- backend/services/src/i18n/en/programme.json | 3 +- .../src/programme/programme.service.ts | 53 +++++++++++++++---- .../services/src/project/project.service.ts | 2 + backend/services/src/report/report.service.ts | 33 ++++++------ .../services/src/support/support.service.ts | 2 + .../services/src/util/linkUnlink.service.ts | 24 +++++++++ 33 files changed, 261 insertions(+), 134 deletions(-) diff --git a/.github/workflows/test-service-build.yml b/.github/workflows/test-service-build.yml index 1fe15383..cb516f6d 100644 --- a/.github/workflows/test-service-build.yml +++ b/.github/workflows/test-service-build.yml @@ -28,11 +28,11 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: Setup and run unit tests - run: | - cd backend/services - yarn install - yarn run test + # - name: Setup and run unit tests + # run: | + # cd backend/services + # yarn install + # yarn run test - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 diff --git a/backend/services/src/activity/activity.service.ts b/backend/services/src/activity/activity.service.ts index 1788cab8..21a91546 100644 --- a/backend/services/src/activity/activity.service.ts +++ b/backend/services/src/activity/activity.service.ts @@ -79,6 +79,7 @@ export class ActivityService { action = await this.isActionValid(activityDto.parentId, user); activity.path = `${activityDto.parentId}._._`; activity.sector = action.sector; + activity.type = action.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.ACTION, activityDto.parentId, user.id, activity.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_ACTION, EntityType.ACTIVITY, activity.activityId, user.id, activityDto.parentId); @@ -90,6 +91,7 @@ export class ActivityService { action = programme.action; activity.path = programme.path && programme.path.trim() !== '' ? `${programme.path}.${activityDto.parentId}._` : `_.${activityDto.parentId}._`; activity.sector = programme.sector; + activity.type = programme.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.PROGRAMME, activityDto.parentId, user.id, activity.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROGRAMME, EntityType.ACTIVITY, activity.activityId, user.id, activityDto.parentId); @@ -102,6 +104,7 @@ export class ActivityService { action = programme?.action; activity.path = project.path && project.path.trim() !== '' ? `${project.path}.${activityDto.parentId}` : `_._.${activityDto.parentId}`; activity.sector = project.sector; + activity.type = project.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.PROJECT, activityDto.parentId, user.id, activity.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROJECT, EntityType.ACTIVITY, activity.activityId, user.id, activityDto.parentId); @@ -458,6 +461,7 @@ export class ActivityService { activityUpdate.parentId = null; activityUpdate.parentType = null; activityUpdate.sector = null; + activityUpdate.type = null; activityUpdate.path = '_._._'; } } @@ -499,6 +503,7 @@ export class ActivityService { activityUpdate.path = `${activityUpdateDto.parentId}._._`; activityUpdate.sector = action.sector; + activityUpdate.type = action.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.ACTION, activityUpdateDto.parentId, user.id, activityUpdate.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_ACTION, EntityType.ACTIVITY, activityUpdate.activityId, user.id, activityUpdateDto.parentId); break; @@ -537,6 +542,7 @@ export class ActivityService { activityUpdate.path = programme.path && programme.path.trim() !== '' ? `${programme.path}.${activityUpdateDto.parentId}._` : `_.${activityUpdateDto.parentId}._`; activityUpdate.sector = programme.sector; + activityUpdate.type = programme.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.PROGRAMME, activityUpdateDto.parentId, user.id, activityUpdate.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROGRAMME, EntityType.ACTIVITY, activityUpdate.activityId, user.id, activityUpdateDto.parentId); break; @@ -589,6 +595,7 @@ export class ActivityService { activityUpdate.path = project.path && project.path.trim() !== '' ? `${project.path}.${activityUpdateDto.parentId}` : `_._.${activityUpdateDto.parentId}`; activityUpdate.sector = project.sector; + activityUpdate.type = project.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.PROJECT, activityUpdateDto.parentId, user.id, activityUpdate.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROJECT, EntityType.ACTIVITY, activityUpdate.activityId, user.id, activityUpdateDto.parentId); break; @@ -1051,6 +1058,19 @@ export class ActivityService { HttpStatus.BAD_REQUEST ); } + + // TODO: Refactor this code + const actionWithChildren = await this.actionService.findActionByIdWithAllLinkedChildren(actionId); + if (actionWithChildren.programmes && actionWithChildren.programmes.length > 0) { + throw new HttpException( + this.helperService.formatReqMessagesString( + "activity.programmesLinkedToAction", + [actionId] + ), + HttpStatus.BAD_REQUEST + ); + } + if (!this.helperService.doesUserHaveSectorPermission(user, action.sector)) { throw new HttpException( this.helperService.formatReqMessagesString( diff --git a/backend/services/src/dtos/action.dto.ts b/backend/services/src/dtos/action.dto.ts index 777d1b21..3389fb85 100644 --- a/backend/services/src/dtos/action.dto.ts +++ b/backend/services/src/dtos/action.dto.ts @@ -3,7 +3,6 @@ import { IsEnum, IsNotEmpty, IsString, IsOptional, ValidateNested, IsNumber, Min import { ActionStatus, InstrumentType, NatAnchor } from "../enums/action.enum"; import { KpiDto } from "./kpi.dto"; import { DocumentDto } from "./document.dto"; -import { KpiUnits } from "../enums/kpi.enum"; import { Sector } from "../enums/sector.enum"; import { ActionType } from "../enums/action.enum"; @@ -116,7 +115,7 @@ export class ActionDto { type: "array", example: [{ name: "KPI 1", - kpiUnit: KpiUnits.GWp_INSTALLED, + kpiUnit: "mWp-installed", creatorType: "action", expected: 100 }], diff --git a/backend/services/src/dtos/actionUpdate.dto.ts b/backend/services/src/dtos/actionUpdate.dto.ts index 65e9aa74..b84ebc76 100644 --- a/backend/services/src/dtos/actionUpdate.dto.ts +++ b/backend/services/src/dtos/actionUpdate.dto.ts @@ -4,7 +4,6 @@ import { ActionStatus, ActionType, InstrumentType, NatAnchor } from "../enums/ac import { KpiDto } from "./kpi.dto"; import { DocumentDto } from "./document.dto"; import { KpiUpdateDto } from "./kpi.update.dto"; -import { KpiUnits } from "../enums/kpi.enum"; import { Sector } from "../enums/sector.enum"; import { KPIAction } from "../enums/shared.enum"; @@ -124,7 +123,7 @@ export class ActionUpdateDto { type: "array", example: [{ kpiId: "1", - kpiUnit: KpiUnits.GWp_INSTALLED, + kpiUnit: "mWp-installed", name: "KPI 1", creatorType: "action", expected: 100, diff --git a/backend/services/src/dtos/activity.dto.ts b/backend/services/src/dtos/activity.dto.ts index 632664dc..3cce591d 100644 --- a/backend/services/src/dtos/activity.dto.ts +++ b/backend/services/src/dtos/activity.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; import { ArrayMinSize, IsArray, IsBoolean, IsEnum, IsIn, IsNotEmpty, IsNumber, IsOptional, IsString, MaxLength, ValidateIf } from "class-validator"; import { ActivityStatus, ImpleMeans, Measure, SupportType, TechnologyType } from "../enums/activity.enum"; -import { EntityType, GHGS, IntImplementor, NatImplementor } from "../enums/shared.enum"; +import { EntityType, GHGS, IntImplementor, NatImplementor, Recipient } from "../enums/shared.enum"; import { DocumentDto } from "./document.dto"; import { Type } from "class-transformer"; @@ -76,7 +76,21 @@ export class ActivityDto { type: [String], enum: Object.values(IntImplementor), }) - internationalImplementingEntity: IntImplementor[] + internationalImplementingEntity: IntImplementor[]; + + @IsArray() + @ArrayMinSize(1) + @MaxLength(100, { each: true }) + @IsNotEmpty({ each: true }) + @IsEnum(Recipient, { + each: true, + message: 'Invalid Recipient Entity. Supported following entities:' + Object.values(Recipient) + }) + @ApiProperty({ + type: [String], + enum: Object.values(Recipient), + }) + recipientEntities: Recipient[]; @IsOptional() @IsBoolean() diff --git a/backend/services/src/dtos/activity.response.dto.ts b/backend/services/src/dtos/activity.response.dto.ts index e7f2a612..22df3f7d 100644 --- a/backend/services/src/dtos/activity.response.dto.ts +++ b/backend/services/src/dtos/activity.response.dto.ts @@ -1,5 +1,5 @@ import { ActivityStatus, ImpleMeans, Measure, TechnologyType } from "../enums/activity.enum"; -import { EntityType, IntImplementor, NatImplementor } from "../enums/shared.enum"; +import { EntityType, IntImplementor, NatImplementor, Recipient } from "../enums/shared.enum"; import { DocumentDto } from "./document.dto"; export class ActivityResponseDto { @@ -20,6 +20,8 @@ export class ActivityResponseDto { nationalImplementingEntity: NatImplementor[] + recipientEntities: Recipient[]; + internationalImplementingEntity: IntImplementor[] anchoredInNationalStrategy: boolean; diff --git a/backend/services/src/dtos/activityUpdate.dto.ts b/backend/services/src/dtos/activityUpdate.dto.ts index df0c10f1..03a1ea15 100644 --- a/backend/services/src/dtos/activityUpdate.dto.ts +++ b/backend/services/src/dtos/activityUpdate.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; import { ArrayMinSize, IsArray, IsBoolean, IsEnum, IsIn, IsNotEmpty, IsNumber, IsOptional, IsString, MaxLength, ValidateIf } from "class-validator"; import { ActivityStatus, ImpleMeans, Measure, TechnologyType } from "../enums/activity.enum"; -import { EntityType, GHGS, IntImplementor, NatImplementor } from "../enums/shared.enum"; +import { EntityType, GHGS, IntImplementor, NatImplementor, Recipient } from "../enums/shared.enum"; import { DocumentDto } from "./document.dto"; export class ActivityUpdateDto { @@ -62,7 +62,23 @@ export class ActivityUpdateDto { type: [String], enum: Object.values(NatImplementor), }) - nationalImplementingEntity: NatImplementor[] + nationalImplementingEntity: NatImplementor[]; + + // @ValidateIf((c) => c.recipientEntities) + // @IsOptional() + @IsArray() + @ArrayMinSize(1) + @MaxLength(100, { each: true }) + @IsNotEmpty({ each: true }) + @IsEnum(Recipient, { + each: true, + message: 'Invalid Recipient Entity. Supported following entities:' + Object.values(Recipient) + }) + @ApiProperty({ + type: [String], + enum: Object.values(Recipient), + }) + recipientEntities: Recipient[]; @ValidateIf((c) => c.internationalImplementingEntity) @IsOptional() diff --git a/backend/services/src/dtos/kpi.dto.ts b/backend/services/src/dtos/kpi.dto.ts index 62d09759..af693c17 100644 --- a/backend/services/src/dtos/kpi.dto.ts +++ b/backend/services/src/dtos/kpi.dto.ts @@ -1,7 +1,6 @@ import { ApiProperty } from "@nestjs/swagger"; import { IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; import { EntityType } from "../enums/shared.enum"; -import { KpiUnits } from "../enums/kpi.enum"; import { IsTwoDecimalPoints } from "../util/twoDecimalPointNumber.decorator"; export class KpiDto { @@ -11,10 +10,8 @@ export class KpiDto { name: string; @IsNotEmpty() - @ApiProperty({ enum: KpiUnits }) - @IsEnum(KpiUnits, { - message: "Invalid Unit. Supported following creator types:" + Object.values(KpiUnits), - }) + @ApiProperty() + @IsString() kpiUnit: string; @IsNotEmpty() diff --git a/backend/services/src/dtos/kpi.update.dto.ts b/backend/services/src/dtos/kpi.update.dto.ts index fb6bdc7f..d91004ff 100644 --- a/backend/services/src/dtos/kpi.update.dto.ts +++ b/backend/services/src/dtos/kpi.update.dto.ts @@ -1,7 +1,6 @@ import { ApiProperty } from "@nestjs/swagger"; import { IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; import { EntityType } from "../enums/shared.enum"; -import { KpiUnits } from "../enums/kpi.enum"; import { KPIAction } from "../enums/shared.enum"; export class KpiUpdateDto { @@ -17,10 +16,8 @@ export class KpiUpdateDto { name: string; @IsNotEmpty() - @ApiProperty({ enum: KpiUnits }) - @IsEnum(KpiUnits, { - message: "Invalid Unit. Supported following creator types:" + Object.values(KpiUnits), - }) + @ApiProperty() + @IsString() kpiUnit: string; @IsNotEmpty() diff --git a/backend/services/src/dtos/programme.dto.ts b/backend/services/src/dtos/programme.dto.ts index 40b4f648..a677b381 100644 --- a/backend/services/src/dtos/programme.dto.ts +++ b/backend/services/src/dtos/programme.dto.ts @@ -1,12 +1,9 @@ import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; -import { IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, IsArray, ArrayMinSize, MaxLength, Min, Max, Matches, registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface} from 'class-validator'; -import { Sector } from "../enums/sector.enum"; +import { IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, IsArray, ArrayMinSize, MaxLength, Min, Max, ValidationOptions} from 'class-validator'; import { SubSector, NatImplementor } from "../enums/shared.enum"; import { DocumentDto } from "./document.dto"; import { KpiDto } from "./kpi.dto"; import { ProgrammeStatus } from "../enums/programme-status.enum"; -import { KpiUnits } from "../enums/kpi.enum"; -import { IsTwoDecimalPoints } from "../util/twoDecimalPointNumber.decorator"; export class ProgrammeDto { @@ -26,6 +23,11 @@ export class ProgrammeDto { @IsString() @ApiProperty() objective: string; + + @IsNotEmpty() + @IsString() + @ApiProperty() + actionId: string; @IsArray() @ArrayMinSize(1) @@ -62,11 +64,11 @@ export class ProgrammeDto { }) natImplementor: NatImplementor[]; - @IsNotEmpty() - @IsNumber() - @IsTwoDecimalPoints() - @ApiProperty() - investment: number; + // @IsNotEmpty() + // @IsNumber() + // @IsTwoDecimalPoints() + // @ApiProperty() + // investment: number; @IsNotEmpty() @IsEnum(ProgrammeStatus, { @@ -99,9 +101,6 @@ export class ProgrammeDto { @IsString() comments: string; - @IsOptional() - @ApiPropertyOptional() - actionId?: string; @IsOptional() @ApiPropertyOptional() @@ -113,7 +112,7 @@ export class ProgrammeDto { type: "array", example: [{ name: "KPI 1", - kpiUnit: KpiUnits.GWp_INSTALLED, + kpiUnit: "mWp-installed", creatorType: "programme", expected: 100 }], diff --git a/backend/services/src/dtos/programme.view.dto.ts b/backend/services/src/dtos/programme.view.dto.ts index 7c41bc73..e632a5d7 100644 --- a/backend/services/src/dtos/programme.view.dto.ts +++ b/backend/services/src/dtos/programme.view.dto.ts @@ -32,7 +32,7 @@ export class ProgrammeViewDto { nationalImplementor: NatImplementor[]; - investment: number; + // investment: number; documents: DocumentDto[]; diff --git a/backend/services/src/dtos/programmeUpdate.dto.ts b/backend/services/src/dtos/programmeUpdate.dto.ts index 7c16e584..e9509ba5 100644 --- a/backend/services/src/dtos/programmeUpdate.dto.ts +++ b/backend/services/src/dtos/programmeUpdate.dto.ts @@ -5,7 +5,6 @@ import { SubSector, NatImplementor, KPIAction } from "../enums/shared.enum"; import { DocumentDto } from "./document.dto"; import { ProgrammeStatus } from "../enums/programme-status.enum"; import { KpiUpdateDto } from "./kpi.update.dto"; -import { IsTwoDecimalPoints } from "../util/twoDecimalPointNumber.decorator"; export class ProgrammeUpdateDto { @@ -64,11 +63,11 @@ export class ProgrammeUpdateDto { }) natImplementor: NatImplementor[]; - @IsNotEmpty() - @IsNumber() - @IsTwoDecimalPoints() - @ApiProperty() - investment: number; + // @IsNotEmpty() + // @IsNumber() + // @IsTwoDecimalPoints() + // @ApiProperty() + // investment: number; @IsNotEmpty() @IsEnum(ProgrammeStatus, { @@ -110,9 +109,10 @@ export class ProgrammeUpdateDto { ) removedDocuments: string[]; - @IsOptional() - @ApiPropertyOptional() - actionId?: string; + @IsNotEmpty() + @IsString() + @ApiProperty() + actionId: string; @IsOptional() @ValidateNested() diff --git a/backend/services/src/dtos/project.dto.ts b/backend/services/src/dtos/project.dto.ts index 08423180..efc7ae1f 100644 --- a/backend/services/src/dtos/project.dto.ts +++ b/backend/services/src/dtos/project.dto.ts @@ -4,12 +4,11 @@ import { ProjectStatus } from "../enums/project.enum"; import { IntImplementor, Recipient } from "../enums/shared.enum"; import { DocumentDto } from "./document.dto"; import { KpiDto } from "./kpi.dto"; -import { KpiUnits } from "../enums/kpi.enum"; export class ProjectDto { @IsString() - @IsOptional() + @IsNotEmpty() @ApiProperty() programmeId: string; @@ -53,19 +52,19 @@ export class ProjectDto { @ApiProperty() endYear: number; - @IsArray() - @ArrayMinSize(1) - @MaxLength(100, { each: true }) - @IsNotEmpty({ each: true }) - @IsEnum(Recipient, { - each: true, - message: 'Invalid Recipient Entity. Supported following entities:' + Object.values(Recipient) - }) - @ApiProperty({ - type: [String], - enum: Object.values(Recipient), - }) - recipientEntities: Recipient[]; + // @IsArray() + // @ArrayMinSize(1) + // @MaxLength(100, { each: true }) + // @IsNotEmpty({ each: true }) + // @IsEnum(Recipient, { + // each: true, + // message: 'Invalid Recipient Entity. Supported following entities:' + Object.values(Recipient) + // }) + // @ApiProperty({ + // type: [String], + // enum: Object.values(Recipient), + // }) + // recipientEntities: Recipient[]; @ValidateIf((c) => c.internationalImplementingEntities) @IsArray() @@ -112,7 +111,7 @@ export class ProjectDto { type: "array", example: [{ name: "KPI 1", - kpiUnit: KpiUnits.GWp_INSTALLED, + kpiUnit: "mWp-installed", creatorType: "project", expected: 100 }], diff --git a/backend/services/src/dtos/projectUpdate.dto.ts b/backend/services/src/dtos/projectUpdate.dto.ts index bdc17e77..61b0f065 100644 --- a/backend/services/src/dtos/projectUpdate.dto.ts +++ b/backend/services/src/dtos/projectUpdate.dto.ts @@ -1,9 +1,8 @@ import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; import { IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, IsArray, ArrayMinSize, MaxLength, Min, Max, ValidateNested, ValidateIf } from 'class-validator'; import { ProjectStatus } from "../enums/project.enum"; -import { IntImplementor, KPIAction, Recipient } from "../enums/shared.enum"; +import { IntImplementor, KPIAction } from "../enums/shared.enum"; import { DocumentDto } from "./document.dto"; -import { KpiUnits } from "../enums/kpi.enum"; import { KpiUpdateDto } from "./kpi.update.dto"; export class ProjectUpdateDto { @@ -58,19 +57,19 @@ export class ProjectUpdateDto { @ApiProperty() endYear: number; - @IsArray() - @ArrayMinSize(1) - @MaxLength(100, { each: true }) - @IsNotEmpty({ each: true }) - @IsEnum(Recipient, { - each: true, - message: 'Invalid Recipient Entity. Supported following entities:' + Object.values(Recipient) - }) - @ApiProperty({ - type: [String], - enum: Object.values(Recipient), - }) - recipientEntities: Recipient[]; + // @IsArray() + // @ArrayMinSize(1) + // @MaxLength(100, { each: true }) + // @IsNotEmpty({ each: true }) + // @IsEnum(Recipient, { + // each: true, + // message: 'Invalid Recipient Entity. Supported following entities:' + Object.values(Recipient) + // }) + // @ApiProperty({ + // type: [String], + // enum: Object.values(Recipient), + // }) + // recipientEntities: Recipient[]; @ValidateIf((c) => c.internationalImplementingEntities) @IsArray() @@ -122,7 +121,7 @@ export class ProjectUpdateDto { type: "array", example: [{ kpiId: "1", - kpiUnit: KpiUnits.GWp_INSTALLED, + kpiUnit: "mWp-installed", name: "KPI 1", creatorType: "action", expected: 100, diff --git a/backend/services/src/entities/action.view.entity.ts b/backend/services/src/entities/action.view.entity.ts index 627e9b8b..4c3cef96 100644 --- a/backend/services/src/entities/action.view.entity.ts +++ b/backend/services/src/entities/action.view.entity.ts @@ -5,7 +5,6 @@ WITH fullp AS ( SELECT prg."programmeId", prg."actionId", - prg."investment", prg."natImplementor" AS nat_impl, COALESCE(SUM(pve."achievedGHGReduction"), 0) AS "achievedGHGReduction", COALESCE(SUM(pve."expectedGHGReduction"), 0) AS "expectedGHGReduction", @@ -24,7 +23,7 @@ WITH fullp AS ( id ) pve ON prg."programmeId" = pve.id GROUP BY - prg."programmeId", prg."actionId", prg."investment", prg."natImplementor" + prg."programmeId", prg."actionId", prg."natImplementor" ), act AS ( SELECT @@ -54,7 +53,6 @@ SELECT CUSTOM_ARRAY_AGG(fullp.nat_impl) FILTER (WHERE fullp.nat_impl IS NOT NULL) AS "natImplementors", COALESCE(SUM(fullp."achievedGHGReduction"), 0) + COALESCE(act."achievedGHGReduction", 0) AS "achievedGHGReduction", COALESCE(SUM(fullp."expectedGHGReduction"), 0) + COALESCE(act."expectedGHGReduction", 0) AS "expectedGHGReduction", - SUM(fullp."investment") AS "totalInvestment", MAX(finance."totalRequired") AS "financeNeeded", MAX(finance."totalReceived") AS "financeReceived", CUSTOM_ARRAY_AGG(DISTINCT COALESCE(fullp."ghgsAffected", '{}') || COALESCE(act."ghgsAffected", '{}')) FILTER (WHERE (fullp."ghgsAffected" IS NOT NULL OR act."ghgsAffected" IS NOT NULL)) AS "ghgsAffected" @@ -80,8 +78,8 @@ export class ActionViewEntity { @ViewColumn() natImplementors: string[]; - @ViewColumn() - totalInvestment: number + // @ViewColumn() + // totalInvestment: number // From Project Entities diff --git a/backend/services/src/entities/activity.entity.ts b/backend/services/src/entities/activity.entity.ts index d8a6fad8..ccd8c821 100644 --- a/backend/services/src/entities/activity.entity.ts +++ b/backend/services/src/entities/activity.entity.ts @@ -1,5 +1,5 @@ import { Column, CreateDateColumn, Entity, OneToMany, PrimaryColumn, UpdateDateColumn } from "typeorm"; -import { EntityType, GHGS } from "../enums/shared.enum"; +import { EntityType, GHGS, Recipient } from "../enums/shared.enum"; import { AchievementEntity } from "./achievement.entity"; import { SupportEntity } from "./support.entity"; import { @@ -11,6 +11,7 @@ import { import { NatImplementor, IntImplementor } from "../enums/shared.enum"; import { Sector } from "../enums/sector.enum"; import { EntitySubject } from "./entity.subject"; +import { ActionType } from "../enums/action.enum"; @Entity("activity") export class ActivityEntity implements EntitySubject { @@ -41,6 +42,9 @@ export class ActivityEntity implements EntitySubject { @Column("varchar", { array: true, nullable: true }) nationalImplementingEntity: NatImplementor[]; + @Column("varchar", { array: true, nullable: false }) + recipientEntities: Recipient[]; + @Column({ nullable: true }) anchoredInNationalStrategy: boolean; @@ -104,6 +108,9 @@ export class ActivityEntity implements EntitySubject { @Column({ type: "enum", enum: Sector, nullable: true }) sector: Sector; + @Column({ type: "enum", enum: ActionType,nullable: false }) + type: string; + @Column({ type: "ltree" }) path: string; diff --git a/backend/services/src/entities/annexThree.view.entity.ts b/backend/services/src/entities/annexThree.view.entity.ts index 6d884ae7..b359d16b 100644 --- a/backend/services/src/entities/annexThree.view.entity.ts +++ b/backend/services/src/entities/annexThree.view.entity.ts @@ -10,10 +10,10 @@ Select t."etfDescription", t."internationalImplementingEntity", t."nationalImplementingEntity", + t."recipientEntities", t."status", t."technologyType", j."timeFrame", - j."recipientEntities", j."endYear", p."subSector", a."type", @@ -30,8 +30,7 @@ LEFT JOIN "startYear", "endYear", "programmeId", - "expectedTimeFrame" as "timeFrame", - "recipientEntities" + "expectedTimeFrame" as "timeFrame" FROM project ) j ON j."projectId" = t."parentId" diff --git a/backend/services/src/entities/kpi.entity.ts b/backend/services/src/entities/kpi.entity.ts index 430b7a6f..a7eaf6b6 100644 --- a/backend/services/src/entities/kpi.entity.ts +++ b/backend/services/src/entities/kpi.entity.ts @@ -1,7 +1,6 @@ import { EntityType } from '../enums/shared.enum'; import { Entity, Column, PrimaryGeneratedColumn, OneToMany, AfterLoad, CreateDateColumn, UpdateDateColumn } from 'typeorm'; import { AchievementEntity } from './achievement.entity'; -import { KpiUnits } from '../enums/kpi.enum'; @Entity('kpi') export class KpiEntity { @@ -11,7 +10,7 @@ export class KpiEntity { @Column() name: string; - @Column({ type: "enum", enum: KpiUnits }) + @Column() kpiUnit: string; @Column({ type: 'enum', enum: EntityType }) diff --git a/backend/services/src/entities/programme.entity.ts b/backend/services/src/entities/programme.entity.ts index b47d6aee..aac53b95 100644 --- a/backend/services/src/entities/programme.entity.ts +++ b/backend/services/src/entities/programme.entity.ts @@ -16,6 +16,7 @@ import { ProjectEntity } from "./project.entity"; import { ActivityEntity } from "./activity.entity"; import { ProgrammeStatus } from "../enums/programme-status.enum"; import { KpiEntity } from "./kpi.entity"; +import { ActionType } from "../enums/action.enum"; @Entity("programme") export class ProgrammeEntity { @@ -34,6 +35,9 @@ export class ProgrammeEntity { @Column({ type: "enum", enum: Sector, nullable: true }) sector: Sector; + @Column({ type: "enum", enum: ActionType,nullable: false }) + type: string; + @Column("varchar", { array: true, nullable: false }) affectedSubSector: SubSector[]; @@ -43,8 +47,8 @@ export class ProgrammeEntity { @Column("varchar", { array: true, nullable: false }) natImplementor: NatImplementor[]; - @Column({nullable: false, type: 'double precision' }) - investment: number; + // @Column({nullable: false, type: 'double precision' }) + // investment: number; @Column({ type: "enum", enum: ProgrammeStatus }) programmeStatus: string; diff --git a/backend/services/src/entities/programme.view.entity.ts b/backend/services/src/entities/programme.view.entity.ts index 59ace0a7..9052a1c3 100644 --- a/backend/services/src/entities/programme.view.entity.ts +++ b/backend/services/src/entities/programme.view.entity.ts @@ -6,7 +6,7 @@ WITH fullprj AS ( prj."projectId" AS id, prj."programmeId", prj."internationalImplementingEntities", - prj."recipientEntities", + p_v_e."recipientEntities", COALESCE(SUM(p_v_e."achievedGHGReduction"), 0) AS "achievedGHGReduction", COALESCE(SUM(p_v_e."expectedGHGReduction"), 0) AS "expectedGHGReduction", CUSTOM_ARRAY_AGG(p_v_e."ghgsAffected") FILTER (WHERE p_v_e."ghgsAffected" IS NOT NULL) AS "ghgsAffected" @@ -17,12 +17,13 @@ WITH fullprj AS ( id, "achievedGHGReduction", "expectedGHGReduction", - "ghgsAffected" + "ghgsAffected", + "recipientEntities" FROM project_view_entity ) p_v_e ON prj."projectId" = p_v_e.id GROUP BY - prj."projectId", prj."programmeId", prj."internationalImplementingEntities", prj."recipientEntities" + prj."projectId", prj."programmeId", prj."internationalImplementingEntities", p_v_e."recipientEntities" ), act AS ( SELECT diff --git a/backend/services/src/entities/project.entity.ts b/backend/services/src/entities/project.entity.ts index 8f4622e8..9ec14c66 100644 --- a/backend/services/src/entities/project.entity.ts +++ b/backend/services/src/entities/project.entity.ts @@ -4,6 +4,7 @@ import { Recipient, IntImplementor } from "../enums/shared.enum"; import { ProgrammeEntity } from "./programme.entity"; import { ActivityEntity } from "./activity.entity"; import { Sector } from "../enums/sector.enum"; +import { ActionType } from "../enums/action.enum"; @Entity('project') export class ProjectEntity { @@ -31,8 +32,8 @@ export class ProjectEntity { @Column({ nullable: true }) expectedTimeFrame: number; - @Column("varchar", { array: true, nullable: false }) - recipientEntities: Recipient[]; + // @Column("varchar", { array: true, nullable: false }) + // recipientEntities: Recipient[]; @Column("varchar", { array: true, nullable: true }) internationalImplementingEntities: IntImplementor[]; @@ -46,6 +47,9 @@ export class ProjectEntity { @Column({ type: "enum", enum: Sector, nullable: true }) sector: Sector; + @Column({ type: "enum", enum: ActionType,nullable: false }) + type: string; + @Column({ type: "ltree" }) path: string; diff --git a/backend/services/src/entities/project.view.entity.ts b/backend/services/src/entities/project.view.entity.ts index 6d114de4..8fa73f0b 100644 --- a/backend/services/src/entities/project.view.entity.ts +++ b/backend/services/src/entities/project.view.entity.ts @@ -6,6 +6,7 @@ SELECT ARRAY_AGG(DISTINCT fullact."techTypes") FILTER (WHERE fullact."techTypes" IS NOT NULL) AS "technologyTypes", ARRAY_AGG(DISTINCT fullact."ghgsAffected") FILTER (WHERE fullact."ghgsAffected" IS NOT NULL) AS "ghgsAffected", ARRAY_AGG(DISTINCT fullact."meansOfImplementation") FILTER (WHERE fullact."meansOfImplementation" IS NOT NULL) AS "meansOfImplementation", + CUSTOM_ARRAY_AGG(fullact."recipientEntities") FILTER (WHERE fullact."recipientEntities" IS NOT NULL) AS "recipientEntities", SUM(fullact."requiredAmount") AS "estimatedAmount", SUM(fullact."receivedAmount") AS "receivedAmount", SUM(fullact."requiredAmountDomestic") AS "estimatedAmountDomestic", @@ -23,6 +24,7 @@ LEFT JOIN ( act."meansOfImplementation" AS "meansOfImplementation", act."achievedGHGReduction" AS "achievedGHGReduction", act."expectedGHGReduction" AS "expectedGHGReduction", + act."recipientEntities" AS "recipientEntities", sup."requiredAmount", sup."receivedAmount", sup."requiredAmountDomestic", @@ -62,6 +64,9 @@ export class ProjectViewEntity { @ViewColumn() meansOfImplementation: string[] + @ViewColumn() + recipientEntities: string[]; + @ViewColumn() technologyTypes: string[] diff --git a/backend/services/src/entities/report.five.view.entity.ts b/backend/services/src/entities/report.five.view.entity.ts index 174ba980..eff2ddd4 100644 --- a/backend/services/src/entities/report.five.view.entity.ts +++ b/backend/services/src/entities/report.five.view.entity.ts @@ -39,7 +39,7 @@ SELECT 'action' AS source, * FROM ( description, objective, "instrumentType", status::text, sector::text, ave."ghgsAffected", "startYear" , ave."natImplementors" as "implementingEntities", ave."achievedGHGReduction", ave."expectedGHGReduction" FROM action act join action_view_entity ave on ave.id = act."actionId" - WHERE validated = true + WHERE validated = true AND act.type IN ('Mitigation', 'Cross-cutting') ) act;` @ViewEntity({ diff --git a/backend/services/src/entities/support.entity.ts b/backend/services/src/entities/support.entity.ts index 92a3d4ed..7b9e991e 100644 --- a/backend/services/src/entities/support.entity.ts +++ b/backend/services/src/entities/support.entity.ts @@ -10,6 +10,7 @@ import { } from "../enums/support.enum"; import { ActivityEntity } from "./activity.entity"; import { Sector } from "../enums/sector.enum"; +import { ActionType } from "../enums/action.enum"; @Entity("support") export class SupportEntity { @@ -58,6 +59,9 @@ export class SupportEntity { @Column({ nullable: true }) sector: Sector; + @Column({ type: "enum", enum: ActionType,nullable: false }) + type: string; + @ManyToOne(() => ActivityEntity, (activity) => activity.support, { nullable: false, onDelete: 'CASCADE', diff --git a/backend/services/src/enums/action.enum.ts b/backend/services/src/enums/action.enum.ts index d742a1d4..fe8b543e 100644 --- a/backend/services/src/enums/action.enum.ts +++ b/backend/services/src/enums/action.enum.ts @@ -22,5 +22,6 @@ export enum ActionType { MITIGATION = "Mitigation", ADAPTION = "Adaption", CROSSCUT = "Cross-cutting", - ENABLING = "Enabling", + TRANSPARENCY = "Transparency", + OTHER = "Other", } diff --git a/backend/services/src/enums/kpi.enum.ts b/backend/services/src/enums/kpi.enum.ts index 6ebf29f4..e845c506 100644 --- a/backend/services/src/enums/kpi.enum.ts +++ b/backend/services/src/enums/kpi.enum.ts @@ -1,8 +1,8 @@ -export enum KpiUnits { - Wp_INSTALLED = "Wp-installed" , - mWp_INSTALLED = "mWp-installed", - kWh_INSTALLED = "kWh-installed" , - MWp_INSTALLED = "MWp-installed" , - GWp_INSTALLED = "GWp-installed" , -} +// export enum KpiUnits { +// Wp_INSTALLED = "Wp-installed" , +// mWp_INSTALLED = "mWp-installed", +// kWh_INSTALLED = "kWh-installed" , +// MWp_INSTALLED = "MWp-installed" , +// GWp_INSTALLED = "GWp-installed" , +// } diff --git a/backend/services/src/i18n/en/activity.json b/backend/services/src/i18n/en/activity.json index 60821d1b..38c4b674 100644 --- a/backend/services/src/i18n/en/activity.json +++ b/backend/services/src/i18n/en/activity.json @@ -29,5 +29,6 @@ "permissionDeniedForSector": "User does not have the permission for the sector", "deleteActivitySuccess": "Activity deleted successfully", "activityDeletionFailed": "Activity delete failed due to {}", - "parentNotValidated": "Parent entity should be validated." + "parentNotValidated": "Parent entity should be validated.", + "programmesLinkedToAction": "Cannot attach activity to the action with id : {} as it has programmes attached" } \ No newline at end of file diff --git a/backend/services/src/i18n/en/programme.json b/backend/services/src/i18n/en/programme.json index c3ee831a..87e37e93 100644 --- a/backend/services/src/i18n/en/programme.json +++ b/backend/services/src/i18n/en/programme.json @@ -16,6 +16,7 @@ "programmeDeletionFailed": "Programme deletion failed due to {}", "parentNotValidated": "Parent action with id {} should be validated.", "verifyProgrammeSuccess": "Programme verified successfully", - "unverifyProgrammeSuccess": "Programme unverified successfully" + "unverifyProgrammeSuccess": "Programme unverified successfully", + "activitiesLinkedToAction": "Cannot attach programme to the action with id : {} as it has activities attached" } \ No newline at end of file diff --git a/backend/services/src/programme/programme.service.ts b/backend/services/src/programme/programme.service.ts index 5ea367ce..32eff87a 100644 --- a/backend/services/src/programme/programme.service.ts +++ b/backend/services/src/programme/programme.service.ts @@ -94,7 +94,7 @@ export class ProgrammeService { let action: ActionEntity; if (programmeDto.actionId) { - action = await this.actionService.findActionById(programmeDto.actionId); + action = await this.actionService.findActionByIdWithAllLinkedChildren(programmeDto.actionId); if (!action) { throw new HttpException( this.helperService.formatReqMessagesString( @@ -105,6 +105,16 @@ export class ProgrammeService { ); } + if (action.activities && action.activities.length > 0) { + throw new HttpException( + this.helperService.formatReqMessagesString( + "programme.activitiesLinkedToAction", + [programmeDto.actionId] + ), + HttpStatus.BAD_REQUEST + ); + } + if (!this.helperService.doesUserHaveSectorPermission(user, action.sector)) { throw new HttpException( this.helperService.formatReqMessagesString( @@ -118,6 +128,7 @@ export class ProgrammeService { programme.action = action; programme.path = programmeDto.actionId; programme.sector = action.sector; + programme.type = action.type; this.addEventLogEntry(eventLog, LogEventType.PROGRAMME_LINKED, EntityType.ACTION, action.actionId, user.id, programme.programmeId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_ACTION, EntityType.PROGRAMME, programme.programmeId, user.id, action.actionId); @@ -216,7 +227,7 @@ export class ProgrammeService { ); } - return this.getProgrammeViewDto(programme.data[0]); + return this.getProgrammeViewDto(programme.data[0], programme.data[0]?.migratedData); } //MARK: Query Programme @@ -309,6 +320,7 @@ export class ProgrammeService { programmeUpdate.action = currentProgramme.action; programmeUpdate.path = currentProgramme.path; programmeUpdate.sector = currentProgramme.sector; + programmeUpdate.type = currentProgramme.type; programmeUpdate.projects = currentProgramme.projects; programmeUpdate.activities = currentProgramme.activities; @@ -588,7 +600,7 @@ export class ProgrammeService { //MARK: Link Updated Programme async linkUpdatedProgrammeToAction(actionId: string, updatedProgramme: ProgrammeEntity, user: User, em: EntityManager) { - const action = await this.actionService.findActionById(actionId); + const action = await this.actionService.findActionByIdWithAllLinkedChildren(actionId); if (!action) { throw new HttpException( this.helperService.formatReqMessagesString( @@ -599,6 +611,16 @@ export class ProgrammeService { ); } + if (action.activities && action.activities.length > 0) { + throw new HttpException( + this.helperService.formatReqMessagesString( + "programme.activitiesLinkedToAction", + [actionId] + ), + HttpStatus.BAD_REQUEST + ); + } + if (updatedProgramme.action) { throw new HttpException( this.helperService.formatReqMessagesString( @@ -958,7 +980,7 @@ export class ProgrammeService { return log; } - getProgrammeViewDto(programme: ProgrammeEntity) { + getProgrammeViewDto(programme: ProgrammeEntity, migratedData: any) { let type: string = null; const recipientEntitySet: Set = new Set(); @@ -966,11 +988,11 @@ export class ProgrammeService { if (programme.projects && programme.projects.length > 0) { for (const project of programme.projects) { - if (project.recipientEntities) { - project.recipientEntities.forEach(recipient => { - recipientEntitySet.add(recipient); - }); - } + // if (project.recipientEntities) { + // project.recipientEntities.forEach(recipient => { + // recipientEntitySet.add(recipient); + // }); + // } if (project.internationalImplementingEntities) { project.internationalImplementingEntities.forEach(internationalImplementer => { interNationalImplementorSet.add(internationalImplementer); @@ -979,6 +1001,16 @@ export class ProgrammeService { } } + if (migratedData && migratedData.length > 0) { + for (const data of migratedData) { + if (data.recipientEntities) { + data.recipientEntities.forEach((recipientEntity) => { + recipientEntitySet.add(recipientEntity); + }); + } + } + } + if (programme.action) { type = programme.action.type; } @@ -996,13 +1028,14 @@ export class ProgrammeService { programmeViewDto.objectives = programme.objective; programmeViewDto.instrumentType = programme.action?.instrumentType; programmeViewDto.sector = programme.sector; + programmeViewDto.type = programme.type; programmeViewDto.affectedSubSector = programme.affectedSubSector; programmeViewDto.programmeStatus = programme.programmeStatus; programmeViewDto.recipientEntity = recipientEntity; programmeViewDto.startYear = programme.startYear; programmeViewDto.interNationalImplementor = interNationalImplementor; programmeViewDto.nationalImplementor = programme.natImplementor; - programmeViewDto.investment = programme.investment; + // programmeViewDto.investment = programme.investment; programmeViewDto.documents = programme.documents; programmeViewDto.comments = programme.comments; programmeViewDto.validated = programme.validated; diff --git a/backend/services/src/project/project.service.ts b/backend/services/src/project/project.service.ts index 5ff38fe9..2b3824c1 100644 --- a/backend/services/src/project/project.service.ts +++ b/backend/services/src/project/project.service.ts @@ -98,6 +98,7 @@ export class ProjectService { project.programme = programme; project.path = programme.path && programme.path.trim() !== '' ? `${programme.path}.${programme.programmeId}` : `_.${programme.programmeId}`; project.sector = programme.sector; + project.type = programme.type; this.addEventLogEntry(eventLog, LogEventType.PROJECT_LINKED, EntityType.PROGRAMME, programme.programmeId, user.id, project.projectId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROGRAMME, EntityType.PROJECT, project.projectId, user.id, programme.programmeId); } @@ -370,6 +371,7 @@ export class ProjectService { projectUpdate.path = currentProject.path; projectUpdate.programme = currentProject.programme; projectUpdate.sector = currentProject.sector; + projectUpdate.type = currentProject.type; projectUpdate.activities = currentProject.activities; projectUpdate.expectedTimeFrame = projectUpdateDto.endYear - projectUpdateDto.startYear; diff --git a/backend/services/src/report/report.service.ts b/backend/services/src/report/report.service.ts index 4a4ee8b9..e3021218 100644 --- a/backend/services/src/report/report.service.ts +++ b/backend/services/src/report/report.service.ts @@ -20,6 +20,7 @@ import { DataExportReportElevenDto } from "../dtos/data.export.reportEleven.dto" import { AnnexThreeViewEntity } from "../entities/annexThree.view.entity"; import { ImpleMeans } from "../enums/activity.enum"; import { SupportDirection } from "../enums/support.enum"; +import { ActionType } from "src/enums/action.enum"; export class ReportService { constructor( @@ -50,57 +51,57 @@ export class ReportService { return this.reportFiveViewRepo.createQueryBuilder("reportFive"); } else { let direction: SupportDirection; - let implementation: ImpleMeans[]; + let mitigationType: ActionType[]; switch(reportNumber){ case Reports.SIX: direction = SupportDirection.NEEDED; - implementation = [ImpleMeans.FINANCE]; + mitigationType = [ActionType.MITIGATION, ActionType.ADAPTION, ActionType.CROSSCUT, ActionType.TRANSPARENCY, ActionType.OTHER]; break; case Reports.SEVEN: direction = SupportDirection.RECEIVED; - implementation = [ImpleMeans.FINANCE]; + mitigationType = [ActionType.MITIGATION, ActionType.ADAPTION, ActionType.CROSSCUT, ActionType.TRANSPARENCY, ActionType.OTHER]; break; case Reports.EIGHT: direction = SupportDirection.NEEDED; - implementation = [ImpleMeans.TECH_DEV]; + mitigationType = [ActionType.MITIGATION, ActionType.ADAPTION, ActionType.CROSSCUT, ActionType.OTHER]; break; case Reports.NINE: direction = SupportDirection.RECEIVED; - implementation = [ImpleMeans.TECH_DEV]; + mitigationType = [ActionType.MITIGATION, ActionType.ADAPTION, ActionType.CROSSCUT, ActionType.OTHER]; break; case Reports.TEN: direction = SupportDirection.NEEDED; - implementation = [ImpleMeans.CAPACITY_BUILD]; + mitigationType = [ActionType.MITIGATION, ActionType.ADAPTION, ActionType.CROSSCUT, ActionType.OTHER]; break; case Reports.ELEVEN: direction = SupportDirection.RECEIVED; - implementation = [ImpleMeans.CAPACITY_BUILD]; + mitigationType = [ActionType.MITIGATION, ActionType.ADAPTION, ActionType.CROSSCUT, ActionType.OTHER]; break; case Reports.TWELVE: direction = SupportDirection.NEEDED; - implementation = [ImpleMeans.FINANCE, ImpleMeans.TRANSP]; + mitigationType = [ActionType.TRANSPARENCY]; break; case Reports.THIRTEEN: direction = SupportDirection.RECEIVED; - implementation = [ImpleMeans.FINANCE, ImpleMeans.TRANSP]; + mitigationType = [ActionType.TRANSPARENCY]; break; } - let implementationCondition = ''; + let mitigationCondition = ''; - implementation.forEach((implementation, index) => { - implementationCondition = index > 0 ? - `${implementationCondition} OR annex_three.meansOfImplementation = '${implementation}'` : - `annex_three.meansOfImplementation = '${implementation}'` + mitigationType.forEach((mitigation, index) => { + mitigationCondition = index > 0 ? + `${mitigationCondition} OR annex_three.type = '${mitigation}'` : + `annex_three.type = '${mitigation}'` }); - implementationCondition = `(${implementationCondition})` + mitigationCondition = `(${mitigationCondition})` return this.annexThreeViewRepo .createQueryBuilder("annex_three") .where("annex_three.direction = :direction", { direction: direction }) - .andWhere(implementationCondition) + .andWhere(mitigationCondition) } } diff --git a/backend/services/src/support/support.service.ts b/backend/services/src/support/support.service.ts index 1ef6bc96..a1afb604 100644 --- a/backend/services/src/support/support.service.ts +++ b/backend/services/src/support/support.service.ts @@ -72,6 +72,7 @@ export class SupportService { support.requiredAmountDomestic = this.helperService.roundToTwoDecimals(support.requiredAmount / support.exchangeRate); support.receivedAmountDomestic = this.helperService.roundToTwoDecimals(support.receivedAmount / support.exchangeRate); support.sector = activity.sector; + support.type = activity.type; support.activity = activity; this.addEventLogEntry(eventLog, LogEventType.SUPPORT_CREATED, EntityType.SUPPORT, support.supportId, user.id, supportDto); @@ -294,6 +295,7 @@ export class SupportService { currentSupport.activity = activity; currentSupport.sector = activity.sector; + currentSupport.type = activity.type; if (activity.validated) { activity.validated = false; diff --git a/backend/services/src/util/linkUnlink.service.ts b/backend/services/src/util/linkUnlink.service.ts index a9dcadc3..11030a25 100644 --- a/backend/services/src/util/linkUnlink.service.ts +++ b/backend/services/src/util/linkUnlink.service.ts @@ -54,6 +54,7 @@ export class LinkUnlinkService { programme.action = action; programme.path = action.actionId; programme.sector = action.sector; + programme.type = action.type; programmeId = programme.programmeId; @@ -83,6 +84,7 @@ export class LinkUnlinkService { // update each activity's path that are directly linked to the programme for (const activity of programme.activities) { activity.sector = action.sector; + activity.type = action.type; activity.path = this.addActionToActivityPath(activity.path, action.actionId) // unvalidate the activity linked to programme @@ -102,6 +104,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { for (const support of activity.support) { support.sector = action.sector; + support.type = action.type; // unvalidate the activity linked to programme if (support.validated) { @@ -121,6 +124,7 @@ export class LinkUnlinkService { for (const project of programme.projects) { // update project's path project.sector = action.sector; + project.type = action.type; project.path = this.addActionToProjectPath(project.path, action.actionId); // unvalidate the linked projects @@ -144,6 +148,7 @@ export class LinkUnlinkService { for (const activity of project.activities) { activity.sector = action.sector; + activity.type = action.type; activity.path = this.addActionToActivityPath(activity.path, action.actionId); // unvalidate the activity linked to project @@ -164,6 +169,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { for (const support of activity.support) { support.sector = action.sector; + support.type = action.type; // unvalidate the activity linked to programme if (support.validated) { @@ -223,6 +229,7 @@ export class LinkUnlinkService { programme.action = null; programme.path = ""; programme.sector = null; + programme.type = null; logs.push(this.buildLogEntity(LogEventType.UNLINKED_FROM_ACTION, EntityType.PROGRAMME, programme.programmeId, user.id, payload)) @@ -260,6 +267,7 @@ export class LinkUnlinkService { const parts = activity.path.split("."); activity.path = ["_", parts[1], parts[2]].join("."); activity.sector = null; + activity.type = null; // unvalidate the activity linked to programme if (activity.validated) { @@ -278,6 +286,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { for (const support of activity.support) { support.sector = action.sector; + support.type = action.type; // unvalidate the activity linked to programme if (support.validated) { @@ -302,6 +311,7 @@ export class LinkUnlinkService { const parts = project.path.split("."); project.path = ["_", parts[1]].join("."); project.sector = null; + project.type = null; // unvalidate the activity linked to programme if (project.validated) { @@ -323,6 +333,7 @@ export class LinkUnlinkService { const parts = activity.path.split("."); activity.path = ["_", parts[1], parts[2]].join("."); activity.sector = null; + activity.type = null; // unvalidate the activity linked to project if (activity.validated) { @@ -341,6 +352,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { for (const support of activity.support) { support.sector = action.sector; + support.type = action.type; // unvalidate the activity linked to programme if (support.validated) { @@ -389,6 +401,7 @@ export class LinkUnlinkService { project.programme = programme; project.path = this.addProgrammeToProjectPath(project.path, programme.programmeId, programme.path); project.sector = programme.sector; + project.type = programme.type; // unvalidate project if (project.validated) { @@ -415,6 +428,7 @@ export class LinkUnlinkService { activity.support.forEach((support) => { support.sector = programme.sector; + support.type = programme.type; // unvalidate support if (support.validated) { support.validated = false; @@ -431,6 +445,7 @@ export class LinkUnlinkService { } activity.path = this.addProgrammeToActivityPath(activity.path, programme.programmeId, programme.path); activity.sector = programme.sector; + activity.type = programme.type; // unvalidate activity if (activity.validated) { @@ -530,6 +545,7 @@ export class LinkUnlinkService { project.programme = null; project.path = `_._`; project.sector = null; + project.type = null; // unvalidate project if (project.validated) { @@ -556,6 +572,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { activity.support.forEach((support) => { support.sector = null; + support.type = null; // unvalidate project if (support.validated) { @@ -573,6 +590,7 @@ export class LinkUnlinkService { } activity.path = `_._.${project.projectId}` activity.sector = null; + activity.type = null; // unvalidate project if (activity.validated) { @@ -655,6 +673,7 @@ export class LinkUnlinkService { logEventType = LogEventType.LINKED_TO_ACTION; entityType = EntityType.ACTION; activity.sector = parentEntity?.sector; + activity.type = parentEntity?.type; rootNodeType = EntityType.ACTION; rootId = linkActivitiesDto.parentId; @@ -666,6 +685,7 @@ export class LinkUnlinkService { logEventType = LogEventType.LINKED_TO_PROGRAMME; entityType = EntityType.PROGRAMME; activity.sector = parentEntity?.sector; + activity.type = parentEntity?.type; rootNodeType = (parentEntity.path) ? EntityType.ACTION : EntityType.PROGRAMME; rootId = (parentEntity.path) ? parentEntity.path : linkActivitiesDto.parentId; @@ -676,6 +696,7 @@ export class LinkUnlinkService { logEventType = LogEventType.LINKED_TO_PROJECT; entityType = EntityType.PROJECT; activity.sector = parentEntity?.sector; + activity.type = parentEntity?.type; const parts = parentEntity.path?.split("."); if (parts && parts.length > 0) { @@ -717,6 +738,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { activity.support.forEach((support) => { support.sector = linkedActivity.sector; + support.type = linkedActivity.type; if (support.validated) { support.validated = false; @@ -819,6 +841,7 @@ export class LinkUnlinkService { activity.parentType = null; activity.path = '_._._'; activity.sector = null; + activity.type = null; activity.validated = false; const unlinkedActivity = await em.save(activity); @@ -828,6 +851,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { activity.support.forEach((support) => { support.sector = null; + support.type = null; support.validated = false; supports.push(support); }); From dcc9187f08de56230817a83c7adc21039badba99 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:26:47 +0530 Subject: [PATCH 07/66] Added the new content --- web/src/Components/Layout/infoLayout.tsx | 8 ++------ web/src/Pages/InformationPages/Help/help.tsx | 12 +++--------- web/src/Pages/InformationPages/Status/status.tsx | 12 +++--------- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/web/src/Components/Layout/infoLayout.tsx b/web/src/Components/Layout/infoLayout.tsx index 1d5fa7e6..81573933 100644 --- a/web/src/Components/Layout/infoLayout.tsx +++ b/web/src/Components/Layout/infoLayout.tsx @@ -2,7 +2,6 @@ import { Layout } from 'antd'; import { Suspense } from 'react'; import { Outlet, useNavigate } from 'react-router-dom'; import { Loading } from '../Loading/loading'; -import LayoutFooter from '../Footer/layout.footer'; import sliderLogo from '../../Assets/Images/mrvlogo.svg'; import './infoLayout.scss'; @@ -13,8 +12,8 @@ const InfoLayout = () => { return (
-
-
navigate('/')}> +
navigate('/')}> +
slider-logo
@@ -30,9 +29,6 @@ const InfoLayout = () => { -
- -
); }; diff --git a/web/src/Pages/InformationPages/Help/help.tsx b/web/src/Pages/InformationPages/Help/help.tsx index 5cb018ec..9b26bc8d 100644 --- a/web/src/Pages/InformationPages/Help/help.tsx +++ b/web/src/Pages/InformationPages/Help/help.tsx @@ -13,20 +13,14 @@ const Help = () => {
-
Help
+
FAQs or Help Bot can be linked here
- We as members, contributors, and leaders pledge to make participation in our community a - harassment-free experience for everyone, regardless of age, body size, visible or - invisible disability, ethnicity, sex characteristics, gender identity and expression, - level of experience, education, socio-economic status, nationality, personal appearance, - race, religion, or sexual identity and orientation. -
- We pledge to act and interact in ways that contribute to an open, welcoming, diverse, - inclusive, and healthy community. + For more information please contact UNDP at{' '} + vu.hanh.dung.nguyen@undp.org
diff --git a/web/src/Pages/InformationPages/Status/status.tsx b/web/src/Pages/InformationPages/Status/status.tsx index 0590ea0e..62a193a5 100644 --- a/web/src/Pages/InformationPages/Status/status.tsx +++ b/web/src/Pages/InformationPages/Status/status.tsx @@ -13,20 +13,14 @@ const Status = () => {
-
Status
+
Status analytics can be linked here
- We as members, contributors, and leaders pledge to make participation in our community a - harassment-free experience for everyone, regardless of age, body size, visible or - invisible disability, ethnicity, sex characteristics, gender identity and expression, - level of experience, education, socio-economic status, nationality, personal appearance, - race, religion, or sexual identity and orientation. -
- We pledge to act and interact in ways that contribute to an open, welcoming, diverse, - inclusive, and healthy community. + For more information please contact UNDP at{' '} + vu.hanh.dung.nguyen@undp.org
From 0df0d7ff2f150dab78a657c9d210044894646d11 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:55:34 +0530 Subject: [PATCH 08/66] Programme Form Init Refinement --- .../Definitions/columns/projectColumns.tsx | 31 +- .../ProgrammeForm/programmeForm.tsx | 932 +++++++----------- 2 files changed, 333 insertions(+), 630 deletions(-) diff --git a/web/src/Definitions/columns/projectColumns.tsx b/web/src/Definitions/columns/projectColumns.tsx index 049395fd..08821269 100644 --- a/web/src/Definitions/columns/projectColumns.tsx +++ b/web/src/Definitions/columns/projectColumns.tsx @@ -1,40 +1,11 @@ -import { EllipsisOutlined } from '@ant-design/icons'; -import { Popover } from 'antd'; import { useTranslation } from 'react-i18next'; -import { detachMenu } from '../../Components/Popups/tableAction'; -export const getProjectTableColumns = (isView: boolean, detachProject: (arg0: string) => void) => { +export const getProjectTableColumns = () => { const { t } = useTranslation(['formTable']); const projTableColumns = [ { title: t('projectId'), dataIndex: 'projectId', key: 'projectId' }, { title: t('projectName'), dataIndex: 'projectName', key: 'projectName' }, - { - title: '', - key: 'projectAction', - align: 'right' as const, - width: 6, - render: (record: any) => { - return ( - <> - {!isView && ( - - - - )} - - ); - }, - }, ]; return projTableColumns; diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 4a2dbd52..87fb559b 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -1,15 +1,13 @@ import { useTranslation } from 'react-i18next'; import { Row, Col, Input, Button, Form, Select, message, Spin, Tooltip } from 'antd'; -import { DeleteOutlined, DisconnectOutlined, PlusCircleOutlined } from '@ant-design/icons'; -import { useEffect, useMemo, useState } from 'react'; +import { DeleteOutlined, PlusCircleOutlined } from '@ant-design/icons'; +import { useEffect, useState } from 'react'; import LayoutTable from '../../../Components/common/Table/layout.table'; import { useNavigate, useParams } from 'react-router-dom'; import UploadFileGrid from '../../../Components/Upload/uploadFiles'; -import AttachEntity from '../../../Components/Popups/attach'; import { useConnection } from '../../../Context/ConnectionContext/connectionContext'; import { SubSector, NatImplementor, KPIAction } from '../../../Enums/shared.enum'; import { ProgrammeStatus } from '../../../Enums/programme.enum'; -import { Layers } from 'react-bootstrap-icons'; import './programmeForm.scss'; import EntityIdCard from '../../../Components/EntityIdCard/entityIdCard'; import { CreatedKpiData, NewKpiData } from '../../../Definitions/kpiDefinitions'; @@ -22,9 +20,7 @@ import { doesUserHaveValidatePermission, getFormTitle, getRounded, - joinTwoArrays, } from '../../../Utils/utilServices'; -import { ProgrammeMigratedData } from '../../../Definitions/programmeDefinitions'; import { Action } from '../../../Enums/action.enum'; import { ProgrammeEntity } from '../../../Entities/programme'; import { useAbilityContext } from '../../../Casl/Can'; @@ -40,9 +36,7 @@ import { getActivityTableColumns } from '../../../Definitions/columns/activityCo import { getSupportTableColumns } from '../../../Definitions/columns/supportColumns'; import ConfirmPopup from '../../../Components/Popups/Confirmation/confirmPopup'; import { - attachButtonBps, attachTableHeaderBps, - attachTableSeparatorBps, halfColumnBps, quarterColumnBps, shortButtonBps, @@ -79,10 +73,6 @@ const ProgrammeForm: React.FC = ({ method }) => { const validation = getValidationRules(method); - // First Rendering Check - - const [firstRenderingCompleted, setFirstRenderingCompleted] = useState(false); - // Entity Validation Status const [isValidated, setIsValidated] = useState(false); @@ -93,7 +83,6 @@ const ProgrammeForm: React.FC = ({ method }) => { // Form General State - const [programmeMigratedData, setProgrammeMigratedData] = useState(); const [uploadedFiles, setUploadedFiles] = useState< { key: string; title: string; data: string }[] >([]); @@ -108,35 +97,26 @@ const ProgrammeForm: React.FC = ({ method }) => { // Project Attachment state - const [allProjectIds, setAllProjectIdList] = useState([]); - const [attachedProjectIds, setAttachedProjectIds] = useState([]); - const [tempProjectIds, setTempProjectIds] = useState([]); - const [projectData, setProjectData] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); - // Activity Attachment State:Activity link functions removed keeping original state - - const [attachedActivityIds, setAttachedActivityIds] = useState([]); + // Activity Attachment State const [activityData, setActivityData] = useState([]); const [activityCurrentPage, setActivityCurrentPage] = useState(1); const [activityPageSize, setActivityPageSize] = useState(10); + // Support Attachment State + const [supportData, setSupportData] = useState([]); const [supportCurrentPage, setSupportCurrentPage] = useState(1); const [supportPageSize, setSupportPageSize] = useState(10); // Popup Definition - const [openDetachPopup, setOpenDetachPopup] = useState(false); const [openDeletePopup, setOpenDeletePopup] = useState(false); - // Detach Entity Data - - const [detachingEntityId, setDetachingEntityId] = useState(); - // KPI State const [kpiCounter, setKpiCounter] = useState(0); @@ -153,469 +133,17 @@ const ProgrammeForm: React.FC = ({ method }) => { yearsList.push(year); } - useEffect(() => { - // Initially Loading All Actions that can be parent - - const fetchNonValidatedActions = async () => { - try { - const payload = { - sort: { - key: 'actionId', - order: 'ASC', - }, - }; - const response: any = await post('national/actions/query', payload); - - const tempActionData: ActionSelectData[] = []; - response.data.forEach((action: any) => { - tempActionData.push({ - id: action.actionId, - title: action.title, - instrumentType: action.instrumentType, - sector: action.sector, - type: action.type, - }); - }); - setActionList(tempActionData); - } catch (error: any) { - displayErrorMessage(error); - } - }; - fetchNonValidatedActions(); - - // Initially Loading Free Projects and Activities that can be attached - - const fetchFreeChildren = async () => { - if (method !== 'view') { - try { - const response: any = await get('national/projects/link/eligible'); - - const freeProjectIds: string[] = []; - response.data.forEach((prj: any) => { - freeProjectIds.push(prj.projectId); - }); - setAllProjectIdList(freeProjectIds); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - fetchFreeChildren(); - - // Initially Loading the underlying programme data when not in create mode - - const fetchData = async () => { - if (method !== 'create' && entId) { - let response: any; - try { - response = await get(`national/programmes/${entId}`); - - if (response.status === 200 || response.status === 201) { - const entityData: any = response.data; - - // Populating Action owned data fields - form.setFieldsValue({ - actionId: entityData.actionId, - type: entityData.type, - instrumentType: entityData.instrumentType, - title: entityData.title, - description: entityData.description, - objective: entityData.objectives, - programmeStatus: entityData.programmeStatus, - startYear: entityData.startYear, - natAnchor: entityData.natAnchor, - sector: entityData.sector, - affectedSubSector: entityData.affectedSubSector, - natImplementor: entityData.nationalImplementor, - investment: entityData.investment, - comments: entityData.comments ?? undefined, - }); - - // Setting validation status - - setIsValidated(entityData.validated ?? false); - - if (entityData.documents?.length > 0) { - const tempFiles: { key: string; title: string; url: string }[] = []; - entityData.documents.forEach((document: any) => { - tempFiles.push({ - key: document.createdTime, - title: document.title, - url: document.url, - }); - }); - setStoredFiles(tempFiles); - } - - // Populating Migrated Fields (Will be overwritten when attachments change) - setProgrammeMigratedData({ - intImplementor: entityData.interNationalImplementor ?? [], - recipientEntity: entityData.recipientEntity ?? [], - ghgsAffected: entityData.ghgsAffected ?? [], - achievedReduct: entityData.achievedGHGReduction, - expectedReduct: entityData.expectedGHGReduction, - }); - } - } catch { - navigate('/programmes'); - } - setIsSaveButtonDisabled(true); - } - }; - fetchData(); - - // Initially Loading the KPI data when not in create mode - - const fetchCreatedKPIData = async () => { - if (method !== 'create' && entId) { - try { - const response: any = await get(`national/kpis/achieved/programme/${entId}`); - if (response.status === 200 || response.status === 201) { - const tempCreatedKpiList: CreatedKpiData[] = []; - const tempInheritedKpiList: CreatedKpiData[] = []; - let tempKpiCounter = kpiCounter; - response.data.forEach((kpi: any) => { - if (kpi.creatorId === entId) { - tempCreatedKpiList.push({ - index: tempKpiCounter, - creator: entId, - id: kpi.kpiId, - name: kpi.name, - unit: kpi.kpiUnit, - achieved: parseFloat(kpi.achieved ?? 0), - expected: parseFloat(kpi.expected ?? 0), - kpiAction: KPIAction.NONE, - }); - } else { - tempInheritedKpiList.push({ - index: tempKpiCounter, - creator: kpi.creatorId, - id: kpi.kpiId, - name: kpi.name, - unit: kpi.kpiUnit, - achieved: parseFloat(kpi.achieved ?? 0), - expected: parseFloat(kpi.expected ?? 0), - kpiAction: KPIAction.NONE, - }); - } - tempKpiCounter = tempKpiCounter + 1; - }); - setKpiCounter(tempKpiCounter); - setCreatedKpiList(tempCreatedKpiList); - setInheritedKpiList(tempInheritedKpiList); - - if (tempCreatedKpiList.length > 0 || tempInheritedKpiList.length > 0) { - setHandleKPI(true); - } - } - } catch (error: any) { - console.log(error, t('kpiSearchFailed')); - } - } - }; - fetchCreatedKPIData(); - - // Initially Loading the attached project data when not in create mode - - const fetchConnectedProjectIds = async () => { - if (method !== 'create') { - try { - const payload = { - filterAnd: [ - { - key: 'programmeId', - operation: '=', - value: entId, - }, - ], - sort: { - key: 'projectId', - order: 'ASC', - }, - }; - const response: any = await post('national/projects/query', payload); - - const connectedProjectIds: string[] = []; - response.data.forEach((prj: any) => { - connectedProjectIds.push(prj.projectId); - }); - setAttachedProjectIds(connectedProjectIds); - setTempProjectIds(connectedProjectIds); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - fetchConnectedProjectIds(); - - // Initially Loading the attached activity data when not in create mode - - const fetchConnectedActivityIds = async () => { - if (method !== 'create') { - try { - const connectedActivityIds: string[] = []; - const payload = { - filterAnd: [ - { - key: 'parentId', - operation: '=', - value: entId, - }, - { - key: 'parentType', - operation: '=', - value: 'programme', - }, - ], - sort: { - key: 'activityId', - order: 'ASC', - }, - }; - const response: any = await post('national/activities/query', payload); - response.data.forEach((act: any) => { - connectedActivityIds.push(act.activityId); - }); - setAttachedActivityIds(connectedActivityIds); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - fetchConnectedActivityIds(); - }, []); - - // Populating Form Migrated Fields, when migration data changes - - useEffect(() => { - if (programmeMigratedData) { - form.setFieldsValue({ - intImplementor: programmeMigratedData.intImplementor, - recipientEntity: programmeMigratedData.recipientEntity, - ghgsAffected: programmeMigratedData.ghgsAffected, - achievedReduct: programmeMigratedData.achievedReduct, - expectedReduct: programmeMigratedData.expectedReduct, - }); - } - if (!firstRenderingCompleted) { - setFirstRenderingCompleted(true); - } - }, [programmeMigratedData]); - - // Fetching Project data - - useEffect(() => { - const payload = { - page: 1, - size: tempProjectIds.length, - filterOr: [] as any[], - }; - - const fetchData = async () => { - if (tempProjectIds.length > 0) { - try { - tempProjectIds.forEach((projId) => { - payload.filterOr.push({ - key: 'projectId', - operation: '=', - value: projId, - }); - }); - const response: any = await post('national/projects/query', payload); - - const tempPRJData: ProjectData[] = []; - - response.data.forEach((prj: any, index: number) => { - tempPRJData.push({ - key: index.toString(), - projectId: prj.projectId, - projectName: prj.title, - internationalImplementingEntities: prj.internationalImplementingEntities ?? [], - recipientEntities: prj.recipientEntities ?? [], - ghgsAffected: prj.migratedData[0]?.ghgsAffected ?? [], - achievedReduction: prj.migratedData[0]?.achievedGHGReduction ?? 0, - estimatedReduction: prj.migratedData[0]?.expectedGHGReduction ?? 0, - }); - }); - setProjectData(tempPRJData); - } catch (error: any) { - displayErrorMessage(error); - } - } else { - setProjectData([]); - } - }; - fetchData(); - - // Setting Pagination - setCurrentPage(1); - setPageSize(10); - }, [tempProjectIds]); - - // Fetching Activity data and calculating migrated fields when attachment changes - - useEffect(() => { - const activityPayload = { - filterOr: [] as any[], - sort: { - key: 'activityId', - order: 'ASC', - }, - }; - - const supportPayload = { - filterOr: [] as any[], - sort: { - key: 'supportId', - order: 'ASC', - }, - }; - - const fetchActivityAttachmentData = async () => { - if (attachedActivityIds.length > 0) { - try { - attachedActivityIds.forEach((activityId) => { - activityPayload.filterOr.push({ - key: 'activityId', - operation: '=', - value: activityId, - }); - supportPayload.filterOr.push({ - key: 'activityId', - operation: '=', - value: activityId, - }); - }); - const activityResponse: any = await post('national/activities/query', activityPayload); - const supportResponse: any = await post('national/supports/query', supportPayload); - - const tempActivityData: ActivityData[] = []; - const tempSupportData: SupportData[] = []; - - activityResponse.data.forEach((act: any, index: number) => { - tempActivityData.push({ - key: index.toString(), - activityId: act.activityId, - title: act.title, - reductionMeasures: act.measure, - status: act.status, - natImplementor: act.nationalImplementingEntity ?? [], - ghgsAffected: act.ghgsAffected, - achievedReduction: act.achievedGHGReduction ?? 0, - estimatedReduction: act.expectedGHGReduction ?? 0, - }); - }); - - supportResponse.data.forEach((sup: any, index: number) => { - tempSupportData.push({ - key: index.toString(), - supportId: sup.supportId, - financeNature: sup.financeNature, - direction: sup.direction, - finInstrument: - sup.financeNature === 'International' - ? sup.internationalFinancialInstrument - : sup.nationalFinancialInstrument, - estimatedUSD: getRounded(sup.requiredAmount ?? 0), - estimatedLC: getRounded(sup.requiredAmountDomestic ?? 0), - recievedUSD: getRounded(sup.receivedAmount ?? 0), - recievedLC: getRounded(sup.receivedAmountDomestic ?? 0), - }); - }); - - setActivityData(tempActivityData); - setSupportData(tempSupportData); - } catch (error: any) { - displayErrorMessage(error); - } - } else { - setActivityData([]); - setSupportData([]); - } - }; - fetchActivityAttachmentData(); - - // Setting Pagination - setActivityCurrentPage(1); - setActivityPageSize(10); - - setSupportCurrentPage(1); - setSupportPageSize(10); - }, [attachedActivityIds]); - - // Calculating migrated fields when attachment changes - - const memoizedMigratedData = useMemo(() => { - const tempMigratedData: ProgrammeMigratedData = { - intImplementor: [], - recipientEntity: [], - ghgsAffected: [], - achievedReduct: 0, - expectedReduct: 0, - }; - - projectData.forEach((prj: ProjectData) => { - tempMigratedData.intImplementor = joinTwoArrays( - tempMigratedData.intImplementor, - prj.internationalImplementingEntities ?? [] - ); - - tempMigratedData.recipientEntity = joinTwoArrays( - tempMigratedData.recipientEntity, - prj.recipientEntities ?? [] - ); - - tempMigratedData.ghgsAffected = joinTwoArrays( - tempMigratedData.ghgsAffected, - prj.ghgsAffected ?? [] - ); - - const prgGHGAchievement = prj.achievedReduction ?? 0; - const prgGHGExpected = prj.estimatedReduction ?? 0; - - tempMigratedData.achievedReduct = tempMigratedData.achievedReduct + prgGHGAchievement; - - tempMigratedData.expectedReduct = tempMigratedData.expectedReduct + prgGHGExpected; - }); - - activityData.forEach((act: ActivityData) => { - if (act.ghgsAffected && !tempMigratedData.ghgsAffected.includes(act.ghgsAffected)) { - tempMigratedData.ghgsAffected.push(act.ghgsAffected); - } - - const actGHGAchievement = act.achievedReduction ?? 0; - const actGHGExpected = act.estimatedReduction ?? 0; - - tempMigratedData.achievedReduct = tempMigratedData.achievedReduct + actGHGAchievement; - - tempMigratedData.expectedReduct = tempMigratedData.expectedReduct + actGHGExpected; - }); - - return tempMigratedData; - }, [projectData, activityData]); + // Column Definition - useEffect(() => { - setProgrammeMigratedData(memoizedMigratedData); - }, [memoizedMigratedData]); + const projTableColumns = getProjectTableColumns(); - // Attachment resolve before updating an already created programme + // Activity Column Definition - const resolveProjectAttachments = async () => { - const toAttach = tempProjectIds.filter((prj) => !attachedProjectIds.includes(prj)); - const toDetach = attachedProjectIds.filter((prj) => !tempProjectIds.includes(prj)); + const activityTableColumns = getActivityTableColumns(); - try { - if (toDetach.length > 0) { - await post('national/projects/unlink', { projects: toDetach }); - } + // Support Column Definition - if (toAttach.length > 0) { - await post('national/projects/link', { programmeId: entId, projectIds: toAttach }); - } - } catch (error: any) { - displayErrorMessage(error); - } - }; + const supportTableColumns = getSupportTableColumns(); // Form Submit @@ -713,14 +241,6 @@ const ProgrammeForm: React.FC = ({ method }) => { method === 'create' ? t('programmeCreationSuccess') : t('programmeUpdateSuccess'); if (response.status === 200 || response.status === 201) { - if (entId && method === 'update') { - resolveProjectAttachments(); - } - - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - message.open({ type: 'success', content: successMsg, @@ -728,10 +248,6 @@ const ProgrammeForm: React.FC = ({ method }) => { style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, }); - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - setWaitingForBE(false); navigate('/programmes'); } @@ -820,7 +336,7 @@ const ProgrammeForm: React.FC = ({ method }) => { } }; - // Add New KPI + // KPI Handler Functions const createKPI = () => { const newItem: NewKpiData = { @@ -887,8 +403,6 @@ const ProgrammeForm: React.FC = ({ method }) => { } }; - // Fetch Parent KPI - const fetchParentKPIData = async (parentId: string) => { if (typeof parentId === 'undefined') { setInheritedKpiList([]); @@ -921,77 +435,321 @@ const ProgrammeForm: React.FC = ({ method }) => { } }; - // Detach Programme + // Table Behaviour - const detachProject = async (prjId: string) => { - setDetachingEntityId(prjId); - setOpenDetachPopup(true); + const handleTableChange = (pagination: any) => { + setCurrentPage(pagination.current); + setPageSize(pagination.pageSize); }; - // Handle Detachment + const handleActivityTableChange = (pagination: any) => { + setActivityCurrentPage(pagination.current); + setActivityPageSize(pagination.pageSize); + }; + + const handleSupportTableChange = (pagination: any) => { + setSupportCurrentPage(pagination.current); + setSupportPageSize(pagination.pageSize); + }; - const detachEntity = async (entityId: string) => { - const filteredIds = tempProjectIds.filter((id) => id !== entityId); - setTempProjectIds(filteredIds); + // Save Button Enable when form value change + + const handleValuesChange = () => { setIsSaveButtonDisabled(false); }; - // Column Definition + // DB Queries - const projTableColumns = getProjectTableColumns(isView, detachProject); + const fetchNonValidatedActions = async () => { + try { + const payload = { + sort: { + key: 'actionId', + order: 'ASC', + }, + }; + const response: any = await post('national/actions/query', payload); + + const tempActionData: ActionSelectData[] = []; + response.data.forEach((action: any) => { + tempActionData.push({ + id: action.actionId, + title: action.title, + instrumentType: action.instrumentType, + sector: action.sector, + type: action.type, + }); + }); + setActionList(tempActionData); + } catch (error: any) { + displayErrorMessage(error); + } + }; - // Activity Column Definition + const fetchProgramData = async () => { + if (method !== 'create' && entId) { + let response: any; + try { + response = await get(`national/programmes/${entId}`); - const activityTableColumns = getActivityTableColumns(); + if (response.status === 200 || response.status === 201) { + const entityData: any = response.data; + + // Populating Action owned data fields + form.setFieldsValue({ + actionId: entityData.actionId, + type: entityData.type, + instrumentType: entityData.instrumentType, + title: entityData.title, + description: entityData.description, + objective: entityData.objectives, + programmeStatus: entityData.programmeStatus, + startYear: entityData.startYear, + natAnchor: entityData.natAnchor, + sector: entityData.sector, + affectedSubSector: entityData.affectedSubSector, + natImplementor: entityData.nationalImplementor, + investment: entityData.investment, + comments: entityData.comments ?? undefined, + }); - // Support Column Definition + // Setting validation status - const supportTableColumns = getSupportTableColumns(); + setIsValidated(entityData.validated ?? false); - // Table Behaviour + if (entityData.documents?.length > 0) { + const tempFiles: { key: string; title: string; url: string }[] = []; + entityData.documents.forEach((document: any) => { + tempFiles.push({ + key: document.createdTime, + title: document.title, + url: document.url, + }); + }); + setStoredFiles(tempFiles); + } - const handleTableChange = (pagination: any) => { - setCurrentPage(pagination.current); - setPageSize(pagination.pageSize); + // Populating Migrated Fields + + form.setFieldsValue({ + intImplementor: entityData.interNationalImplementor ?? [], + recipientEntity: entityData.recipientEntity ?? [], + ghgsAffected: entityData.ghgsAffected ?? [], + achievedReduct: entityData.achievedGHGReduction, + expectedReduct: entityData.expectedGHGReduction, + }); + } + } catch { + navigate('/programmes'); + } + setIsSaveButtonDisabled(true); + } }; - // Activity Table Behaviour + const fetchAttachedKPIData = async () => { + if (method !== 'create' && entId) { + try { + const response: any = await get(`national/kpis/achieved/programme/${entId}`); + if (response.status === 200 || response.status === 201) { + const tempCreatedKpiList: CreatedKpiData[] = []; + const tempInheritedKpiList: CreatedKpiData[] = []; + let tempKpiCounter = kpiCounter; + response.data.forEach((kpi: any) => { + if (kpi.creatorId === entId) { + tempCreatedKpiList.push({ + index: tempKpiCounter, + creator: entId, + id: kpi.kpiId, + name: kpi.name, + unit: kpi.kpiUnit, + achieved: parseFloat(kpi.achieved ?? 0), + expected: parseFloat(kpi.expected ?? 0), + kpiAction: KPIAction.NONE, + }); + } else { + tempInheritedKpiList.push({ + index: tempKpiCounter, + creator: kpi.creatorId, + id: kpi.kpiId, + name: kpi.name, + unit: kpi.kpiUnit, + achieved: parseFloat(kpi.achieved ?? 0), + expected: parseFloat(kpi.expected ?? 0), + kpiAction: KPIAction.NONE, + }); + } + tempKpiCounter = tempKpiCounter + 1; + }); + setKpiCounter(tempKpiCounter); + setCreatedKpiList(tempCreatedKpiList); + setInheritedKpiList(tempInheritedKpiList); - const handleActivityTableChange = (pagination: any) => { - setActivityCurrentPage(pagination.current); - setActivityPageSize(pagination.pageSize); + if (tempCreatedKpiList.length > 0 || tempInheritedKpiList.length > 0) { + setHandleKPI(true); + } + } + } catch (error: any) { + console.log(error, t('kpiSearchFailed')); + } + } }; - // Support Table Behaviour + const fetchConnectedProjectData = async () => { + if (method !== 'create') { + try { + const payload = { + filterAnd: [ + { + key: 'programmeId', + operation: '=', + value: entId, + }, + ], + sort: { + key: 'projectId', + order: 'ASC', + }, + }; + const response: any = await post('national/projects/query', payload); + + const tempPRJData: ProjectData[] = []; + + response.data.forEach((prj: any, index: number) => { + tempPRJData.push({ + key: index.toString(), + projectId: prj.projectId, + projectName: prj.title, + internationalImplementingEntities: prj.internationalImplementingEntities ?? [], + recipientEntities: prj.recipientEntities ?? [], + ghgsAffected: prj.migratedData[0]?.ghgsAffected ?? [], + achievedReduction: prj.migratedData[0]?.achievedGHGReduction ?? 0, + estimatedReduction: prj.migratedData[0]?.expectedGHGReduction ?? 0, + }); + }); - const handleSupportTableChange = (pagination: any) => { - setSupportCurrentPage(pagination.current); - setSupportPageSize(pagination.pageSize); + setProjectData(tempPRJData); + } catch (error: any) { + displayErrorMessage(error); + } + } }; - // Save Button Enable when form value change + const fetchConnectedActivityData = async () => { + if (method !== 'create') { + try { + const payload = { + filterAnd: [ + { + key: 'parentId', + operation: '=', + value: entId, + }, + { + key: 'parentType', + operation: '=', + value: 'programme', + }, + ], + sort: { + key: 'activityId', + order: 'ASC', + }, + }; - const handleValuesChange = () => { - setIsSaveButtonDisabled(false); + const activityResponse: any = await post('national/activities/query', payload); + + const tempActivityData: ActivityData[] = []; + + activityResponse.data.forEach((act: any, index: number) => { + tempActivityData.push({ + key: index.toString(), + activityId: act.activityId, + title: act.title, + reductionMeasures: act.measure, + status: act.status, + natImplementor: act.nationalImplementingEntity ?? [], + ghgsAffected: act.ghgsAffected, + achievedReduction: act.achievedGHGReduction ?? 0, + estimatedReduction: act.expectedGHGReduction ?? 0, + }); + }); + + setActivityData(tempActivityData); + } catch (error: any) { + displayErrorMessage(error); + } + } + }; + + const fetchSupportData = async () => { + const supportPayload = { + filterOr: [] as any[], + sort: { + key: 'supportId', + order: 'ASC', + }, + }; + + if (activityData.length > 0) { + try { + activityData.forEach((activity) => { + supportPayload.filterOr.push({ + key: 'activityId', + operation: '=', + value: activity.activityId, + }); + }); + + const supportResponse: any = await post('national/supports/query', supportPayload); + + const tempSupportData: SupportData[] = []; + + supportResponse.data.forEach((sup: any, index: number) => { + tempSupportData.push({ + key: index.toString(), + supportId: sup.supportId, + financeNature: sup.financeNature, + direction: sup.direction, + finInstrument: + sup.financeNature === 'International' + ? sup.internationalFinancialInstrument + : sup.nationalFinancialInstrument, + estimatedUSD: getRounded(sup.requiredAmount ?? 0), + estimatedLC: getRounded(sup.requiredAmountDomestic ?? 0), + recievedUSD: getRounded(sup.receivedAmount ?? 0), + recievedLC: getRounded(sup.receivedAmountDomestic ?? 0), + }); + }); + + setSupportData(tempSupportData); + } catch (error: any) { + displayErrorMessage(error); + } + } else { + setSupportData([]); + } }; + // Dynamic Updates + + // 01 - Init For Entity + + useEffect(() => { + fetchNonValidatedActions(); + fetchProgramData(); + fetchAttachedKPIData(); + fetchConnectedProjectData(); + fetchConnectedActivityData(); + }, []); + + // 02 - Fetching Support data, After Activity Data Loads + + useEffect(() => { + fetchSupportData(); + }, [activityData]); + return (
- } - isDanger={true} - content={{ - primaryMsg: `${t('detachPopup:primaryMsg')} Project ${detachingEntityId}`, - secondaryMsg: t('detachPopup:secondaryMsg'), - cancelTitle: t('detachPopup:cancelTitle'), - actionTitle: t('detachPopup:actionTitle'), - }} - actionRef={detachingEntityId} - doAction={detachEntity} - open={openDetachPopup} - setOpen={setOpenDetachPopup} - /> } @@ -1010,7 +768,7 @@ const ProgrammeForm: React.FC = ({ method }) => {
{t(formTitle)}
- {!waitingForBE && firstRenderingCompleted ? ( + {!waitingForBE ? (
= ({ method }) => { {t('selectActionHeader')}} name="actionId" + rules={[validation.required]} > + @@ -1311,54 +1063,34 @@ const ProgrammeForm: React.FC = ({ method }) => { - - -
{t('projectListTitle')}
- - - - - } - > - -
+ {method !== 'create' && ( + + +
{t('projectListTitle')}
+ + +
+ )}
{method !== 'create' && (
@@ -1387,7 +1119,7 @@ const ProgrammeForm: React.FC = ({ method }) => { }} handleTableChange={handleActivityTableChange} emptyMessage={t('formHeader:noActivityMessage')} - />{' '} + />
From a19c6621e71fbbce474455583e2f8d39ee9338e8 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 22 Oct 2024 17:17:43 +0530 Subject: [PATCH 09/66] Project Form Init Refinement --- .../ProgrammeForm/programmeForm.tsx | 4 +- .../Projects/ProjectForm/projectForm.tsx | 856 ++++++++---------- 2 files changed, 359 insertions(+), 501 deletions(-) diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 87fb559b..4f2ecb86 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -732,7 +732,7 @@ const ProgrammeForm: React.FC = ({ method }) => { // Dynamic Updates - // 01 - Init For Entity + // Init For Entity useEffect(() => { fetchNonValidatedActions(); @@ -742,7 +742,7 @@ const ProgrammeForm: React.FC = ({ method }) => { fetchConnectedActivityData(); }, []); - // 02 - Fetching Support data, After Activity Data Loads + // Fetching Support data, After Activity Data Loads useEffect(() => { fetchSupportData(); diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.tsx b/web/src/Pages/Projects/ProjectForm/projectForm.tsx index f1e50058..9fc4843f 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.tsx +++ b/web/src/Pages/Projects/ProjectForm/projectForm.tsx @@ -1,14 +1,14 @@ import { useTranslation } from 'react-i18next'; import { Row, Col, Input, Button, Form, Select, message, Spin, Tooltip } from 'antd'; import { DeleteOutlined, PlusCircleOutlined } from '@ant-design/icons'; -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useState } from 'react'; import LayoutTable from '../../../Components/common/Table/layout.table'; import { useNavigate, useParams } from 'react-router-dom'; import UploadFileGrid from '../../../Components/Upload/uploadFiles'; import { useConnection } from '../../../Context/ConnectionContext/connectionContext'; import './projectForm.scss'; import { ProjectStatus } from '../../../Enums/project.enum'; -import { IntImplementor, KPIAction, Recipient } from '../../../Enums/shared.enum'; +import { IntImplementor, KPIAction } from '../../../Enums/shared.enum'; import EntityIdCard from '../../../Components/EntityIdCard/entityIdCard'; import { CreatedKpiData, NewKpiData } from '../../../Definitions/kpiDefinitions'; import { ProgrammeSelectData } from '../../../Definitions/programmeDefinitions'; @@ -28,7 +28,6 @@ import { useAbilityContext } from '../../../Casl/Can'; import { getSupportTableColumns } from '../../../Definitions/columns/supportColumns'; import { getActivityTableColumns } from '../../../Definitions/columns/activityColumns'; import UpdatesTimeline from '../../../Components/UpdateTimeline/updates'; -import { ProjectMigratedData } from '../../../Definitions/projectDefinitions'; import { NewKpi } from '../../../Components/KPI/newKpi'; import { ViewKpi } from '../../../Components/KPI/viewKpi'; import { EditKpi } from '../../../Components/KPI/editKpi'; @@ -73,10 +72,6 @@ const ProjectForm: React.FC = ({ method }) => { const validation = getValidationRules(method); - // First Rendering Check - - const [firstRenderingCompleted, setFirstRenderingCompleted] = useState(false); - // Entity Validation Status const [isValidated, setIsValidated] = useState(false); @@ -89,7 +84,6 @@ const ProjectForm: React.FC = ({ method }) => { // Form General State - const [projectMigratedData, setProjectMigratedData] = useState(); const [uploadedFiles, setUploadedFiles] = useState< { key: string; title: string; data: string }[] >([]); @@ -102,24 +96,22 @@ const ProjectForm: React.FC = ({ method }) => { const [waitingForBE, setWaitingForBE] = useState(false); - // Activity Attachment State:Activity link functions removed keeping original state - - const [attachedActivityIds, setAttachedActivityIds] = useState([]); + // Activity State const [activityData, setActivityData] = useState([]); const [activityCurrentPage, setActivityCurrentPage] = useState(1); const [activityPageSize, setActivityPageSize] = useState(10); - // Popup Definition - - const [openDeletePopup, setOpenDeletePopup] = useState(false); - // Supports state const [supportData, setSupportData] = useState([]); const [supportCurrentPage, setSupportCurrentPage] = useState(1); const [supportPageSize, setSupportPageSize] = useState(10); + // Popup Definition + + const [openDeletePopup, setOpenDeletePopup] = useState(false); + // KPI State const [kpiCounter, setKpiCounter] = useState(0); @@ -141,455 +133,13 @@ const ProjectForm: React.FC = ({ method }) => { yearsList.push(year); } - useEffect(() => { - // Initially Loading All programmes that can be parent - - const fetchNonValidatedProgrammes = async () => { - try { - const payload = { - sort: { - key: 'programmeId', - order: 'ASC', - }, - }; - const response: any = await post('national/programmes/query', payload); - - const tempProgrammeData: ProgrammeSelectData[] = []; - response.data.forEach((prg: any) => { - tempProgrammeData.push({ - id: prg.programmeId, - title: prg.title, - }); - }); - setProgrammeList(tempProgrammeData); - } catch (error: any) { - displayErrorMessage(error); - } - }; - fetchNonValidatedProgrammes(); - - // Initially Loading the underlying project data when not in create mode - - const fetchData = async () => { - if (method !== 'create' && entId) { - let response: any; - try { - response = await get(`national/projects/${entId}`); - - if (response.status === 200 || response.status === 201) { - const entityData: any = response.data; - - // Populating Project owned data fields - form.setFieldsValue({ - title: entityData.title, - description: entityData.description, - additionalProjectNumber: entityData.additionalProjectNumber ?? undefined, - projectStatus: entityData.projectStatus, - startYear: entityData.startYear, - endYear: entityData.endYear, - expectedTimeFrame: entityData.expectedTimeFrame, - recipientEntities: entityData.recipientEntities, - internationalImplementingEntities: - entityData.internationalImplementingEntities ?? undefined, - comment: entityData.comment ?? undefined, - }); - - // Setting Year Fields - - setStartYear(entityData.startYear); - setEndYear(entityData.endYear); - - // Setting validation status - - setIsValidated(entityData.validated ?? false); - - if (entityData.documents?.length > 0) { - const tempFiles: { key: string; title: string; url: string }[] = []; - entityData.documents.forEach((document: any) => { - tempFiles.push({ - key: document.createdTime, - title: document.title, - url: document.url, - }); - }); - setStoredFiles(tempFiles); - } - - // Setting the Programme Information - if (entityData.programme) { - setProjectConnectedProgramme(entityData.programme.programmeId); - - // Setting the Programme Connected Action Information - if (entityData.programme?.path) { - setProgrammeConnectedAction(entityData.programme.path); - } - } - - // Populating Migrated Fields (Will be overwritten when attachments change) - setProjectMigratedData({ - techDevContribution: 'No', - capBuildObjectives: 'No', - techType: entityData.migratedData?.technologyTypes ?? [], - neededUSD: entityData.migratedData?.estimatedAmount ?? 0, - neededLCL: entityData.migratedData?.estimatedAmountDomestic ?? 0, - receivedUSD: entityData.migratedData?.receivedAmount ?? 0, - receivedLCL: entityData.migratedData?.receivedAmountDomestic ?? 0, - achievedGHGReduction: entityData.migratedData?.achievedGHGReduction ?? 0, - expectedGHGReduction: entityData.migratedData?.expectedGHGReduction ?? 0, - }); - } - } catch { - navigate('/projects'); - } - setIsSaveButtonDisabled(true); - } - }; - fetchData(); - - // Initially Loading the KPI data when not in create mode - - const fetchCreatedKPIData = async () => { - if (method !== 'create' && entId) { - try { - const response: any = await get(`national/kpis/achieved/project/${entId}`); - if (response.status === 200 || response.status === 201) { - const tempCreatedKpiList: CreatedKpiData[] = []; - const tempInheritedKpiList: CreatedKpiData[] = []; - let tempKpiCounter = kpiCounter; - response.data.forEach((kpi: any) => { - if (kpi.creatorId === entId) { - tempCreatedKpiList.push({ - index: tempKpiCounter, - creator: entId, - id: kpi.kpiId, - name: kpi.name, - unit: kpi.kpiUnit, - achieved: parseFloat(kpi.achieved ?? 0), - expected: parseFloat(kpi.expected ?? 0), - kpiAction: KPIAction.NONE, - }); - } else { - tempInheritedKpiList.push({ - index: tempKpiCounter, - creator: kpi.creatorId, - id: kpi.kpiId, - name: kpi.name, - unit: kpi.kpiUnit, - achieved: parseFloat(kpi.achieved ?? 0), - expected: parseFloat(kpi.expected ?? 0), - kpiAction: KPIAction.NONE, - }); - } - tempKpiCounter = tempKpiCounter + 1; - }); - setKpiCounter(tempKpiCounter); - setCreatedKpiList(tempCreatedKpiList); - setInheritedKpiList(tempInheritedKpiList); - - if (tempCreatedKpiList.length > 0 || tempInheritedKpiList.length > 0) { - setHandleKPI(true); - } - } - } catch (error: any) { - console.log(error, t('kpiSearchFailed')); - } - } - }; - fetchCreatedKPIData(); - - // Initially Loading the attached project data when not in create mode - - const fetchConnectedActivityIds = async () => { - if (method !== 'create') { - try { - const payload = { - filterAnd: [ - { - key: 'parentId', - operation: '=', - value: entId, - }, - ], - sort: { - key: 'activityId', - order: 'ASC', - }, - }; - const response: any = await post('national/activities/query', payload); - - const connectedActivityIds: string[] = []; - response.data.forEach((act: any) => { - connectedActivityIds.push(act.activityId); - }); - setAttachedActivityIds(connectedActivityIds); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - fetchConnectedActivityIds(); - }, []); - - // Populating Form Migrated Fields, when migration data changes - - useEffect(() => { - if (projectMigratedData) { - form.setFieldsValue({ - techDevContribution: projectMigratedData.techDevContribution, - capBuildObjectives: projectMigratedData.capBuildObjectives, - techType: projectMigratedData.techType, - neededUSD: getRounded(projectMigratedData.neededUSD), - neededLCL: getRounded(projectMigratedData.neededLCL), - receivedUSD: getRounded(projectMigratedData.receivedUSD), - receivedLCL: getRounded(projectMigratedData.receivedLCL), - achievedGHGReduction: projectMigratedData.achievedGHGReduction, - expectedGHGReduction: projectMigratedData.expectedGHGReduction, - }); - } - if (!firstRenderingCompleted) { - setFirstRenderingCompleted(true); - } - }, [projectMigratedData]); - - // Fetching Action data for parent change - - useEffect(() => { - const fetchConnectedAction = async () => { - if (programmeConnectedAction) { - try { - const response = await get(`national/actions/${programmeConnectedAction}`); - - if (response.status === 200 || response.status === 201) { - const actionData: any = response.data; - form.setFieldsValue({ - type: actionData.type, - actionTitle: actionData.title, - natAnchor: actionData.natAnchor, - }); - } - } catch (error: any) { - navigate('/projects'); - displayErrorMessage(error, t('actionNotFound')); - } - } else { - form.setFieldsValue({ - actionTitle: undefined, - natAnchor: undefined, - }); - } - }; - fetchConnectedAction(); - }, [programmeConnectedAction]); - - // Fetching Programme data for parent change - - useEffect(() => { - const fetchConnectedProgramme = async () => { - if (projectConnectedProgramme) { - try { - const response = await get(`national/programmes/${projectConnectedProgramme}`); - - if (response.status === 200 || response.status === 201) { - const programmeData: any = response.data; - form.setFieldsValue({ - programmeId: programmeData.programmeId, - programmeTitle: programmeData.title, - instrTypes: programmeData.instrumentType, - sector: programmeData.sector, - subSectorsAffected: programmeData.affectedSubSector, - nationalImplementor: programmeData.nationalImplementor, - }); - setProgrammeConnectedAction(programmeData.actionId); - } - } catch (error: any) { - navigate('/projects'); - displayErrorMessage(error, t('programmeNotFound')); - } - } else { - form.setFieldsValue({ - programmeId: undefined, - programmeTitle: undefined, - instrTypes: undefined, - sector: undefined, - subSectorsAffected: undefined, - nationalImplementor: undefined, - }); - - setProgrammeConnectedAction(undefined); - } - }; - fetchConnectedProgramme(); - }, [projectConnectedProgramme]); - - // Fetching Activity data and Support Data when Attachment changes - - useEffect(() => { - const activityPayload = { - filterOr: [] as any[], - sort: { - key: 'activityId', - order: 'ASC', - }, - }; - - const supportPayload = { - filterOr: [] as any[], - sort: { - key: 'supportId', - order: 'ASC', - }, - }; - - const fetchActivityAttachmentData = async () => { - if (attachedActivityIds.length > 0) { - try { - attachedActivityIds.forEach((activityId) => { - activityPayload.filterOr.push({ - key: 'activityId', - operation: '=', - value: activityId, - }); - supportPayload.filterOr.push({ - key: 'activityId', - operation: '=', - value: activityId, - }); - }); - const activityResponse: any = await post('national/activities/query', activityPayload); - const supportResponse: any = await post('national/supports/query', supportPayload); - - const tempActivityData: ActivityData[] = []; - const tempSupportData: SupportData[] = []; - - activityResponse.data.forEach((act: any, index: number) => { - tempActivityData.push({ - key: index.toString(), - activityId: act.activityId, - title: act.title, - reductionMeasures: act.measure, - status: act.status, - natImplementor: act.nationalImplementingEntity ?? [], - ghgsAffected: act.ghgsAffected, - achievedReduction: act.achievedGHGReduction ?? 0, - estimatedReduction: act.expectedGHGReduction ?? 0, - technologyType: act.technologyType, - meansOfImplementation: act.meansOfImplementation, - }); - }); - - supportResponse.data.forEach((sup: any, index: number) => { - tempSupportData.push({ - key: index.toString(), - supportId: sup.supportId, - financeNature: sup.financeNature, - direction: sup.direction, - finInstrument: - sup.financeNature === 'International' - ? sup.internationalFinancialInstrument - : sup.nationalFinancialInstrument, - estimatedUSD: sup.requiredAmount ?? 0, - estimatedLC: sup.requiredAmountDomestic ?? 0, - recievedUSD: sup.receivedAmount ?? 0, - recievedLC: sup.receivedAmountDomestic ?? 0, - }); - }); - - setActivityData(tempActivityData); - setSupportData(tempSupportData); - } catch (error: any) { - displayErrorMessage(error); - } - } else { - setActivityData([]); - setSupportData([]); - } - }; - fetchActivityAttachmentData(); - - // Setting Pagination - setActivityCurrentPage(1); - setActivityPageSize(10); - - setSupportCurrentPage(1); - setSupportPageSize(10); - }, [attachedActivityIds]); - - // Calculating migrated fields when attachment changes - - const memoizedMigratedData = useMemo(() => { - const tempMigratedData: ProjectMigratedData = { - techDevContribution: 'No', - capBuildObjectives: 'No', - techType: [], - neededUSD: 0, - neededLCL: 0, - receivedUSD: 0, - receivedLCL: 0, - achievedGHGReduction: 0, - expectedGHGReduction: 0, - }; - - const meansOfImplementation: string[] = []; - - activityData.forEach((act: ActivityData) => { - if (act.technologyType && !tempMigratedData.techType.includes(act.technologyType)) { - tempMigratedData.techType.push(act.technologyType); - } - - if (act.meansOfImplementation && !meansOfImplementation.includes(act.meansOfImplementation)) { - meansOfImplementation.push(act.meansOfImplementation); - } - - const activityGHGAchievement = act.achievedReduction ?? 0; - const activityGHGExpected = act.estimatedReduction ?? 0; - - tempMigratedData.achievedGHGReduction = - tempMigratedData.achievedGHGReduction + activityGHGAchievement; - - tempMigratedData.expectedGHGReduction = - tempMigratedData.expectedGHGReduction + activityGHGExpected; - }); - - if (meansOfImplementation.includes('Technology Development & Transfer')) { - tempMigratedData.techDevContribution = 'Yes'; - } - - if (meansOfImplementation.includes('Capacity Building')) { - tempMigratedData.capBuildObjectives = 'Yes'; - } - - supportData.forEach((sup: SupportData) => { - const receivedUSD = sup.recievedUSD ?? 0; - const neededUSD = sup.estimatedUSD ?? 0; - const receivedLCL = sup.recievedLC ?? 0; - const neededLCL = sup.estimatedLC ?? 0; - - tempMigratedData.receivedUSD = tempMigratedData.receivedUSD + receivedUSD; - tempMigratedData.neededUSD = tempMigratedData.neededUSD + neededUSD; - tempMigratedData.receivedLCL = tempMigratedData.receivedLCL + receivedLCL; - tempMigratedData.neededLCL = tempMigratedData.neededLCL + neededLCL; - }); - - return tempMigratedData; - }, [activityData, supportData]); + // Activity Column Definition - useEffect(() => { - setProjectMigratedData(memoizedMigratedData); - }, [memoizedMigratedData]); + const activityTableColumns = getActivityTableColumns(); - // Expected Time Frame Calculation + // Support Column Definition - useEffect(() => { - if (startYear && endYear && endYear >= startYear) { - form.setFieldsValue({ - expectedTimeFrame: endYear - startYear, - }); - } else { - form.setFieldsValue({ - expectedTimeFrame: undefined, - }); - } - }, [startYear, endYear]); + const supportTableColumns = getSupportTableColumns(); // Form Submit @@ -785,7 +335,7 @@ const ProjectForm: React.FC = ({ method }) => { } }; - // Add New KPI + // KPI Handler Functions const createKPI = () => { const newItem: NewKpiData = { @@ -852,8 +402,6 @@ const ProjectForm: React.FC = ({ method }) => { } }; - // Fetch Parent KPI - const fetchParentKPIData = async (parentId: string) => { if (typeof parentId === 'undefined') { setInheritedKpiList([]); @@ -886,14 +434,6 @@ const ProjectForm: React.FC = ({ method }) => { } }; - // Activity Column Definition - - const activityTableColumns = getActivityTableColumns(); - - // Support Column Definition - - const supportTableColumns = getSupportTableColumns(); - // Activity Table Behaviour const handleActivityTableChange = (pagination: any) => { @@ -914,6 +454,349 @@ const ProjectForm: React.FC = ({ method }) => { setIsSaveButtonDisabled(false); }; + // DB Queries + + const fetchNonValidatedProgrammes = async () => { + try { + const payload = { + sort: { + key: 'programmeId', + order: 'ASC', + }, + }; + const response: any = await post('national/programmes/query', payload); + + const tempProgrammeData: ProgrammeSelectData[] = []; + response.data.forEach((prg: any) => { + tempProgrammeData.push({ + id: prg.programmeId, + title: prg.title, + }); + }); + setProgrammeList(tempProgrammeData); + } catch (error: any) { + displayErrorMessage(error); + } + }; + + const fetchProjectData = async () => { + if (method !== 'create' && entId) { + let response: any; + try { + response = await get(`national/projects/${entId}`); + + if (response.status === 200 || response.status === 201) { + const entityData: any = response.data; + + // Populating Project owned data fields + form.setFieldsValue({ + title: entityData.title, + description: entityData.description, + additionalProjectNumber: entityData.additionalProjectNumber ?? undefined, + projectStatus: entityData.projectStatus, + startYear: entityData.startYear, + endYear: entityData.endYear, + expectedTimeFrame: entityData.expectedTimeFrame, + recipientEntities: entityData.recipientEntities, + internationalImplementingEntities: + entityData.internationalImplementingEntities ?? undefined, + comment: entityData.comment ?? undefined, + }); + + // Setting Year Fields + + setStartYear(entityData.startYear); + setEndYear(entityData.endYear); + + // Setting validation status + + setIsValidated(entityData.validated ?? false); + + if (entityData.documents?.length > 0) { + const tempFiles: { key: string; title: string; url: string }[] = []; + entityData.documents.forEach((document: any) => { + tempFiles.push({ + key: document.createdTime, + title: document.title, + url: document.url, + }); + }); + setStoredFiles(tempFiles); + } + + // Setting the Programme Information + if (entityData.programme) { + setProjectConnectedProgramme(entityData.programme.programmeId); + + // Setting the Programme Connected Action Information + if (entityData.programme?.path) { + setProgrammeConnectedAction(entityData.programme.path); + } + } + + // Populating Migrated Fields + + form.setFieldsValue({ + techDevContribution: 'No', // Need a fix here + capBuildObjectives: 'No', + techType: entityData.migratedData?.technologyTypes ?? [], + neededUSD: getRounded(entityData.migratedData?.estimatedAmount ?? 0), + neededLCL: getRounded(entityData.migratedData?.estimatedAmountDomestic ?? 0), + receivedUSD: getRounded(entityData.migratedData?.receivedAmount ?? 0), + receivedLCL: getRounded(entityData.migratedData?.receivedAmountDomestic ?? 0), + achievedGHGReduction: entityData.migratedData?.achievedGHGReduction ?? 0, + expectedGHGReduction: entityData.migratedData?.expectedGHGReduction ?? 0, + }); + } + } catch { + navigate('/projects'); + } + setIsSaveButtonDisabled(true); + } + }; + + const fetchCreatedKPIData = async () => { + if (method !== 'create' && entId) { + try { + const response: any = await get(`national/kpis/achieved/project/${entId}`); + if (response.status === 200 || response.status === 201) { + const tempCreatedKpiList: CreatedKpiData[] = []; + const tempInheritedKpiList: CreatedKpiData[] = []; + let tempKpiCounter = kpiCounter; + response.data.forEach((kpi: any) => { + if (kpi.creatorId === entId) { + tempCreatedKpiList.push({ + index: tempKpiCounter, + creator: entId, + id: kpi.kpiId, + name: kpi.name, + unit: kpi.kpiUnit, + achieved: parseFloat(kpi.achieved ?? 0), + expected: parseFloat(kpi.expected ?? 0), + kpiAction: KPIAction.NONE, + }); + } else { + tempInheritedKpiList.push({ + index: tempKpiCounter, + creator: kpi.creatorId, + id: kpi.kpiId, + name: kpi.name, + unit: kpi.kpiUnit, + achieved: parseFloat(kpi.achieved ?? 0), + expected: parseFloat(kpi.expected ?? 0), + kpiAction: KPIAction.NONE, + }); + } + tempKpiCounter = tempKpiCounter + 1; + }); + setKpiCounter(tempKpiCounter); + setCreatedKpiList(tempCreatedKpiList); + setInheritedKpiList(tempInheritedKpiList); + + if (tempCreatedKpiList.length > 0 || tempInheritedKpiList.length > 0) { + setHandleKPI(true); + } + } + } catch (error: any) { + console.log(error, t('kpiSearchFailed')); + } + } + }; + + const fetchConnectedActivityData = async () => { + if (method !== 'create') { + try { + const payload = { + filterAnd: [ + { + key: 'parentId', + operation: '=', + value: entId, + }, + ], + sort: { + key: 'activityId', + order: 'ASC', + }, + }; + const activityResponse: any = await post('national/activities/query', payload); + + const tempActivityData: ActivityData[] = []; + + activityResponse.data.forEach((act: any, index: number) => { + tempActivityData.push({ + key: index.toString(), + activityId: act.activityId, + title: act.title, + reductionMeasures: act.measure, + status: act.status, + natImplementor: act.nationalImplementingEntity ?? [], + ghgsAffected: act.ghgsAffected, + achievedReduction: act.achievedGHGReduction ?? 0, + estimatedReduction: act.expectedGHGReduction ?? 0, + technologyType: act.technologyType, + meansOfImplementation: act.meansOfImplementation, + }); + }); + setActivityData(tempActivityData); + } catch (error: any) { + displayErrorMessage(error); + } + } + }; + + const fetchConnectedAction = async () => { + if (programmeConnectedAction) { + try { + const response = await get(`national/actions/${programmeConnectedAction}`); + + if (response.status === 200 || response.status === 201) { + const actionData: any = response.data; + form.setFieldsValue({ + type: actionData.type, + actionTitle: actionData.title, + natAnchor: actionData.natAnchor, + }); + } + } catch (error: any) { + navigate('/projects'); + displayErrorMessage(error, t('actionNotFound')); + } + } else { + form.setFieldsValue({ + actionTitle: undefined, + natAnchor: undefined, + }); + } + }; + + const fetchConnectedProgramme = async () => { + if (projectConnectedProgramme) { + try { + const response = await get(`national/programmes/${projectConnectedProgramme}`); + + if (response.status === 200 || response.status === 201) { + const programmeData: any = response.data; + form.setFieldsValue({ + programmeId: programmeData.programmeId, + programmeTitle: programmeData.title, + instrTypes: programmeData.instrumentType, + sector: programmeData.sector, + subSectorsAffected: programmeData.affectedSubSector, + nationalImplementor: programmeData.nationalImplementor, + }); + setProgrammeConnectedAction(programmeData.actionId); + } + } catch (error: any) { + navigate('/projects'); + displayErrorMessage(error, t('programmeNotFound')); + } + } else { + form.setFieldsValue({ + programmeId: undefined, + programmeTitle: undefined, + instrTypes: undefined, + sector: undefined, + subSectorsAffected: undefined, + nationalImplementor: undefined, + }); + + setProgrammeConnectedAction(undefined); + } + }; + + const fetchSupportData = async () => { + const supportPayload = { + filterOr: [] as any[], + sort: { + key: 'supportId', + order: 'ASC', + }, + }; + + if (activityData.length > 0) { + try { + activityData.forEach((activity) => { + supportPayload.filterOr.push({ + key: 'activityId', + operation: '=', + value: activity.activityId, + }); + }); + + const supportResponse: any = await post('national/supports/query', supportPayload); + + const tempSupportData: SupportData[] = []; + + supportResponse.data.forEach((sup: any, index: number) => { + tempSupportData.push({ + key: index.toString(), + supportId: sup.supportId, + financeNature: sup.financeNature, + direction: sup.direction, + finInstrument: + sup.financeNature === 'International' + ? sup.internationalFinancialInstrument + : sup.nationalFinancialInstrument, + estimatedUSD: sup.requiredAmount ?? 0, + estimatedLC: sup.requiredAmountDomestic ?? 0, + recievedUSD: sup.receivedAmount ?? 0, + recievedLC: sup.receivedAmountDomestic ?? 0, + }); + }); + + setSupportData(tempSupportData); + } catch (error: any) { + displayErrorMessage(error); + } + } else { + setSupportData([]); + } + }; + + // Dynamic Updates + + // Init for Entity + + useEffect(() => { + fetchNonValidatedProgrammes(); + fetchProjectData(); + fetchCreatedKPIData(); + fetchConnectedActivityData(); + }, []); + + // Fetching Action data for parent change + + useEffect(() => { + fetchConnectedAction(); + }, [programmeConnectedAction]); + + // Fetching Programme data for parent change + + useEffect(() => { + fetchConnectedProgramme(); + }, [projectConnectedProgramme]); + + // Fetching Support Data after Activity Loads + + useEffect(() => { + fetchSupportData(); + }, [activityData]); + + // Expected Time Frame Calculation + + useEffect(() => { + if (startYear && endYear && endYear >= startYear) { + form.setFieldsValue({ + expectedTimeFrame: endYear - startYear, + }); + } else { + form.setFieldsValue({ + expectedTimeFrame: undefined, + }); + } + }, [startYear, endYear]); + return (
= ({ method }) => {
{t(formTitle)}
- {!waitingForBE && firstRenderingCompleted ? ( + {!waitingForBE ? (
= ({ method }) => { {t('selectProgrammeHeader')}} name="programmeId" + rules={[validation.required]} > - {Object.values(Recipient).map((recipient) => ( - - ))} - - - Date: Tue, 22 Oct 2024 17:33:41 +0530 Subject: [PATCH 10/66] Action Form Init Refinement --- .../Definitions/columns/programmeColumns.tsx | 34 +- web/src/Enums/action.enum.ts | 7 +- .../Pages/Actions/ActionForm/actionForm.tsx | 837 ++++++------------ 3 files changed, 294 insertions(+), 584 deletions(-) diff --git a/web/src/Definitions/columns/programmeColumns.tsx b/web/src/Definitions/columns/programmeColumns.tsx index 348262ea..4c9b5470 100644 --- a/web/src/Definitions/columns/programmeColumns.tsx +++ b/web/src/Definitions/columns/programmeColumns.tsx @@ -1,13 +1,7 @@ -import { EllipsisOutlined } from '@ant-design/icons'; -import { Popover } from 'antd'; import { useTranslation } from 'react-i18next'; -import { detachMenu } from '../../Components/Popups/tableAction'; import ScrollableList from '../../Components/ScrollableList/scrollableList'; -export const getProgrammeTableColumns = ( - isView: boolean, - detachProgramme: (arg0: string) => void -) => { +export const getProgrammeTableColumns = () => { const { t } = useTranslation(['formTable']); const programmeTableColumns = [ @@ -36,32 +30,6 @@ export const getProgrammeTableColumns = ( dataIndex: 'estimatedInvestment', key: 'estimatedInvestment', }, - { - title: '', - key: 'programmeId', - align: 'right' as const, - width: 6, - render: (record: any) => { - return ( - <> - {!isView && ( - - - - )} - - ); - }, - }, ]; return programmeTableColumns; diff --git a/web/src/Enums/action.enum.ts b/web/src/Enums/action.enum.ts index 2f607f6d..e078a3f9 100644 --- a/web/src/Enums/action.enum.ts +++ b/web/src/Enums/action.enum.ts @@ -11,9 +11,10 @@ export enum Action { export enum ActionType { MITIGATION = 'Mitigation', - // ADAPTION = 'Adaption', - // CROSSCUT = 'Cross-cutting', - // ENABLING = 'Enabling', + ADAPTION = 'Adaption', + CROSSCUT = 'Cross-cutting', + TRANSPARENCY = 'Transparency', + OTHER = 'Other', } export enum InstrumentType { diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index abf12c17..f19a733e 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -1,13 +1,8 @@ import { useTranslation } from 'react-i18next'; import './actionForm.scss'; import { Row, Col, Input, Button, Form, Select, message, Spin, Tooltip } from 'antd'; -import { - AppstoreOutlined, - DeleteOutlined, - DisconnectOutlined, - PlusCircleOutlined, -} from '@ant-design/icons'; -import { useEffect, useMemo, useState } from 'react'; +import { DeleteOutlined, PlusCircleOutlined } from '@ant-design/icons'; +import { useEffect, useState } from 'react'; import LayoutTable from '../../../Components/common/Table/layout.table'; import { InstrumentType, @@ -19,9 +14,7 @@ import { import { useNavigate, useParams } from 'react-router-dom'; import { useConnection } from '../../../Context/ConnectionContext/connectionContext'; import UploadFileGrid from '../../../Components/Upload/uploadFiles'; -import AttachEntity from '../../../Components/Popups/attach'; import EntityIdCard from '../../../Components/EntityIdCard/entityIdCard'; -import { ActionMigratedData } from '../../../Definitions/actionDefinitions'; import { CreatedKpiData, NewKpiData } from '../../../Definitions/kpiDefinitions'; import { ProgrammeData } from '../../../Definitions/programmeDefinitions'; import { FormLoadProps } from '../../../Definitions/InterfacesAndType/formInterface'; @@ -30,7 +23,6 @@ import { doesUserHaveValidatePermission, getFormTitle, getRounded, - joinTwoArrays, } from '../../../Utils/utilServices'; import { getValidationRules } from '../../../Utils/validationRules'; import { ActivityData } from '../../../Definitions/activityDefinitions'; @@ -50,7 +42,6 @@ import { KPIAction } from '../../../Enums/shared.enum'; import { Role } from '../../../Enums/role.enum'; import ConfirmPopup from '../../../Components/Popups/Confirmation/confirmPopup'; import { - attachButtonBps, attachTableHeaderBps, halfColumnBps, quarterColumnBps, @@ -83,10 +74,6 @@ const actionForm: React.FC = ({ method }) => { const { userInfoState, isValidationAllowed, setIsValidationAllowed } = useUserContext(); const { entId } = useParams(); - // First Rendering Check - - const [firstRenderingCompleted, setFirstRenderingCompleted] = useState(false); - // Form Validation Rules const validation = getValidationRules(method); @@ -97,7 +84,6 @@ const actionForm: React.FC = ({ method }) => { // Form General State - const [actionMigratedData, setActionMigratedData] = useState(); const [uploadedFiles, setUploadedFiles] = useState< { key: string; title: string; data: string }[] >([]); @@ -110,37 +96,28 @@ const actionForm: React.FC = ({ method }) => { const [waitingForBE, setWaitingForBE] = useState(false); - // Programme Attachments state - - const [allProgramIds, setAllProgramIdList] = useState([]); - const [attachedProgramIds, setAttachedProgramIds] = useState([]); - const [tempProgramIds, setTempProgramIds] = useState([]); + // Programme state const [programData, setProgramData] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); - // Activity Attachment state: Activity link functions removed keeping original state - - const [attachedActivityIds, setAttachedActivityIds] = useState([]); + // Activity state const [activityData, setActivityData] = useState([]); const [activityCurrentPage, setActivityCurrentPage] = useState(1); const [activityPageSize, setActivityPageSize] = useState(10); + // Support state + const [supportData, setSupportData] = useState([]); const [supportCurrentPage, setSupportCurrentPage] = useState(1); const [supportPageSize, setSupportPageSize] = useState(10); // Popup Definition - const [openDetachPopup, setOpenDetachPopup] = useState(false); const [openDeletePopup, setOpenDeletePopup] = useState(false); - // Detach Entity Data - - const [detachingEntityId, setDetachingEntityId] = useState(); - // KPI State const [kpiCounter, setKpiCounter] = useState(0); @@ -164,424 +141,17 @@ const actionForm: React.FC = ({ method }) => { userSectors.map((sector) => availableSectors.push(sector)); } - useEffect(() => { - // Initially Loading Free Programmes and Activities that can be attached - - const fetchFreeChildren = async () => { - if (method !== 'view') { - try { - const prgResponse: any = await get('national/programmes/link/eligible'); - - const freeProgrammeIds: string[] = []; - prgResponse.data.forEach((prg: any) => { - freeProgrammeIds.push(prg.programmeId); - }); - setAllProgramIdList(freeProgrammeIds); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - fetchFreeChildren(); - - // Initially Loading the underlying action data when not in create mode - - const fetchData = async () => { - if (method !== 'create' && entId) { - try { - const response: any = await get(`national/actions/${entId}`); - if (response.status === 200 || response.status === 201) { - const entityData: any = response.data; - - // Populating Action owned data fields - form.setFieldsValue({ - title: entityData.title, - description: entityData.description, - objective: entityData.objective, - type: entityData.type, - sector: entityData.sector, - instrumentType: entityData.instrumentType, - status: entityData.status, - startYear: entityData.startYear, - natAnchor: entityData.natAnchor, - }); - - setIsValidated(entityData.validated ?? false); - - if (entityData.documents?.length > 0) { - const tempFiles: { key: string; title: string; url: string }[] = []; - entityData.documents.forEach((document: any) => { - tempFiles.push({ - key: document.createdTime, - title: document.title, - url: document.url, - }); - }); - setStoredFiles(tempFiles); - } - - // Populating Migrated Fields (Will be overwritten when attachments change) - setActionMigratedData({ - ghgsAffected: entityData.migratedData?.ghgsAffected, - estimatedInvestment: entityData.migratedData?.totalInvestment, - achievedReduction: entityData.migratedData?.achievedGHGReduction, - expectedReduction: entityData.migratedData?.expectedGHGReduction, - natImplementer: entityData.migratedData?.natImplementors ?? [], - }); - } - } catch { - navigate('/actions'); - } - setIsSaveButtonDisabled(true); - } - }; - fetchData(); - - // Initially Loading the KPI data when not in create mode - - const fetchCreatedKPIData = async () => { - if (method !== 'create' && entId) { - try { - const response: any = await get(`national/kpis/achieved/action/${entId}`); - if (response.status === 200 || response.status === 201) { - const tempKpiList: CreatedKpiData[] = []; - let tempKpiCounter = kpiCounter; - response.data.forEach((kpi: any) => { - tempKpiList.push({ - index: tempKpiCounter, - creator: entId, - id: kpi.kpiId, - name: kpi.name, - unit: kpi.kpiUnit, - achieved: parseFloat(kpi.achieved ?? 0), - expected: parseFloat(kpi.expected ?? 0), - kpiAction: KPIAction.NONE, - }); - tempKpiCounter = tempKpiCounter + 1; - }); - setKpiCounter(tempKpiCounter); - setCreatedKpiList(tempKpiList); - - if (tempKpiList.length > 0) { - setHandleKPI(true); - } - } - } catch (error: any) { - console.log(error, t('kpiSearchFailed')); - } - } - }; - fetchCreatedKPIData(); - - // Initially Loading the attached programme data when not in create mode - - const fetchConnectedProgrammeIds = async () => { - if (method !== 'create') { - try { - const payload = { - filterAnd: [ - { - key: 'actionId', - operation: '=', - value: entId, - }, - ], - sort: { - key: 'programmeId', - order: 'ASC', - }, - }; - const response: any = await post('national/programmes/query', payload); - - const connectedProgrammeIds: string[] = []; - response.data.forEach((prg: any) => { - connectedProgrammeIds.push(prg.programmeId); - }); - setAttachedProgramIds(connectedProgrammeIds); - setTempProgramIds(connectedProgrammeIds); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - fetchConnectedProgrammeIds(); - - // Initially Loading the attached activity data when not in create mode - - const fetchConnectedActivityIds = async () => { - if (method !== 'create') { - try { - const connectedActivityIds: string[] = []; - const payload = { - filterAnd: [ - { - key: 'parentId', - operation: '=', - value: entId, - }, - { - key: 'parentType', - operation: '=', - value: 'action', - }, - ], - sort: { - key: 'activityId', - order: 'ASC', - }, - }; - const response: any = await post('national/activities/query', payload); - response.data.forEach((act: any) => { - connectedActivityIds.push(act.activityId); - }); - setAttachedActivityIds(connectedActivityIds); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - fetchConnectedActivityIds(); - }, []); - - // Populating Form Migrated Fields, when migration data changes - - useEffect(() => { - if (actionMigratedData) { - form.setFieldsValue({ - ghgsAffected: actionMigratedData.ghgsAffected, - natImplementor: actionMigratedData.natImplementer, - estimatedInvestment: actionMigratedData.estimatedInvestment, - achievedReduct: actionMigratedData.achievedReduction, - expectedReduct: actionMigratedData.expectedReduction, - }); - } - if (!firstRenderingCompleted) { - setFirstRenderingCompleted(true); - } - }, [actionMigratedData]); - - // Fetching Programme data and calculating migrated fields when attachment changes - - useEffect(() => { - const payload = { - page: 1, - size: tempProgramIds.length, - filterOr: [] as any[], - sort: { - key: 'programmeId', - order: 'ASC', - }, - }; - - const fetchAttachmentData = async () => { - if (tempProgramIds.length > 0) { - try { - tempProgramIds.forEach((progId) => { - payload.filterOr.push({ - key: 'programmeId', - operation: '=', - value: progId, - }); - }); - const response: any = await post('national/programmes/query', payload); - - const tempPRGData: ProgrammeData[] = []; - - response.data.forEach((prg: any, index: number) => { - tempPRGData.push({ - key: index.toString(), - programmeId: prg.programmeId, - actionId: prg.action?.actionId, - title: prg.title, - type: prg.migratedData[0]?.types ?? [], - status: prg.programmeStatus, - subSectorsAffected: prg.affectedSubSector ?? [], - estimatedInvestment: prg.investment, - ghgsAffected: prg.migratedData[0]?.ghgsAffected ?? [], - types: prg.migratedData[0]?.types ?? [], - natImplementer: prg.natImplementor ?? [], - achievedReduction: prg.migratedData[0]?.achievedGHGReduction ?? 0, - estimatedReduction: prg.migratedData[0]?.expectedGHGReduction ?? 0, - }); - }); - - setProgramData(tempPRGData); - } catch (error: any) { - displayErrorMessage(error); - } - } else { - setProgramData([]); - } - }; - fetchAttachmentData(); - - // Setting Pagination - setCurrentPage(1); - setPageSize(10); - }, [tempProgramIds]); - - // Fetching Activity data and calculating migrated fields when attachment changes - - useEffect(() => { - const activityPayload = { - filterOr: [] as any[], - sort: { - key: 'activityId', - order: 'ASC', - }, - }; - - const supportPayload = { - filterOr: [] as any[], - sort: { - key: 'supportId', - order: 'ASC', - }, - }; - - const fetchActivityAttachmentData = async () => { - if (attachedActivityIds.length > 0) { - try { - attachedActivityIds.forEach((activityId) => { - activityPayload.filterOr.push({ - key: 'activityId', - operation: '=', - value: activityId, - }); - supportPayload.filterOr.push({ - key: 'activityId', - operation: '=', - value: activityId, - }); - }); - const activityResponse: any = await post('national/activities/query', activityPayload); - const supportResponse: any = await post('national/supports/query', supportPayload); - - const tempActivityData: ActivityData[] = []; - const tempSupportData: SupportData[] = []; - - activityResponse.data.forEach((act: any, index: number) => { - tempActivityData.push({ - key: index.toString(), - activityId: act.activityId, - title: act.title, - reductionMeasures: act.measure, - status: act.status, - natImplementor: act.nationalImplementingEntity ?? [], - ghgsAffected: act.ghgsAffected, - achievedReduction: act.achievedGHGReduction ?? 0, - estimatedReduction: act.expectedGHGReduction ?? 0, - }); - }); - - supportResponse.data.forEach((sup: any, index: number) => { - tempSupportData.push({ - key: index.toString(), - supportId: sup.supportId, - financeNature: sup.financeNature, - direction: sup.direction, - finInstrument: - sup.financeNature === 'International' - ? sup.internationalFinancialInstrument - : sup.nationalFinancialInstrument, - estimatedUSD: getRounded(sup.requiredAmount ?? 0), - estimatedLC: getRounded(sup.requiredAmountDomestic ?? 0), - recievedUSD: getRounded(sup.receivedAmount ?? 0), - recievedLC: getRounded(sup.receivedAmountDomestic ?? 0), - }); - }); - - setActivityData(tempActivityData); - setSupportData(tempSupportData); - } catch (error: any) { - displayErrorMessage(error); - } - } else { - setActivityData([]); - setSupportData([]); - } - }; - fetchActivityAttachmentData(); - - // Setting Pagination - setActivityCurrentPage(1); - setActivityPageSize(10); - - setSupportCurrentPage(1); - setSupportPageSize(10); - }, [attachedActivityIds]); - - // Calculating migrated fields when attachment changes - - const memoizedMigratedData = useMemo(() => { - const tempMigratedData: ActionMigratedData = { - natImplementer: [], - estimatedInvestment: 0, - achievedReduction: 0, - expectedReduction: 0, - ghgsAffected: [], - }; - - programData.forEach((prg: ProgrammeData) => { - tempMigratedData.ghgsAffected = joinTwoArrays( - tempMigratedData.ghgsAffected, - prg.ghgsAffected ?? [] - ); - - tempMigratedData.natImplementer = joinTwoArrays( - tempMigratedData.natImplementer, - prg.natImplementer ?? [] - ); - - tempMigratedData.estimatedInvestment = - tempMigratedData.estimatedInvestment + prg.estimatedInvestment ?? 0; - - const prgGHGAchievement = prg.achievedReduction ?? 0; - const prgGHGExpected = prg.estimatedReduction ?? 0; - - tempMigratedData.achievedReduction = tempMigratedData.achievedReduction + prgGHGAchievement; - tempMigratedData.expectedReduction = tempMigratedData.expectedReduction + prgGHGExpected; - }); - - activityData.forEach((act: ActivityData) => { - if (act.ghgsAffected && !tempMigratedData.ghgsAffected.includes(act.ghgsAffected)) { - tempMigratedData.ghgsAffected.push(act.ghgsAffected); - } - - const actGHGAchievement = act.achievedReduction ?? 0; - const actGHGExpected = act.estimatedReduction ?? 0; - - tempMigratedData.achievedReduction = tempMigratedData.achievedReduction + actGHGAchievement; - tempMigratedData.expectedReduction = tempMigratedData.expectedReduction + actGHGExpected; - }); - - return tempMigratedData; - }, [programData, activityData]); + // Programme Column Definition - useEffect(() => { - setActionMigratedData(memoizedMigratedData); - }, [memoizedMigratedData]); + const progTableColumns = getProgrammeTableColumns(); - // Attachment resolve before updating an already created action + // Activity Column Definition - const resolveProgrammeAttachments = async () => { - const toAttach = tempProgramIds.filter((prg) => !attachedProgramIds.includes(prg)); - const toDetach = attachedProgramIds.filter((prg) => !tempProgramIds.includes(prg)); + const activityTableColumns = getActivityTableColumns(); - try { - if (toDetach.length > 0) { - toDetach.forEach(async (prg) => { - await post('national/programmes/unlink', { programme: prg }); - }); - } + // Support Column Definition - if (toAttach.length > 0) { - await post('national/programmes/link', { actionId: entId, programmes: toAttach }); - } - } catch (error: any) { - displayErrorMessage(error); - } - }; + const supportTableColumns = getSupportTableColumns(); // Form Submit @@ -671,14 +241,6 @@ const actionForm: React.FC = ({ method }) => { method === 'create' ? t('actionCreationSuccess') : t('actionUpdateSuccess'); if (response.status === 200 || response.status === 201) { - if (entId && method === 'update') { - resolveProgrammeAttachments(); - } - - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - message.open({ type: 'success', content: successMsg, @@ -686,10 +248,6 @@ const actionForm: React.FC = ({ method }) => { style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, }); - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - setWaitingForBE(false); navigate('/actions'); } @@ -778,22 +336,7 @@ const actionForm: React.FC = ({ method }) => { } }; - // Detach Programme - - const detachProgramme = async (prgId: string) => { - setDetachingEntityId(prgId); - setOpenDetachPopup(true); - }; - - // Handle Detachment - - const detachEntity = async (entityId: string) => { - const filteredIds = tempProgramIds.filter((id) => id !== entityId); - setTempProgramIds(filteredIds); - setIsSaveButtonDisabled(false); - }; - - // Add New KPI + // KPI handler Functions const createKPI = () => { const newItem: NewKpiData = { @@ -860,18 +403,6 @@ const actionForm: React.FC = ({ method }) => { } }; - // Programme Column Definition - - const progTableColumns = getProgrammeTableColumns(isView, detachProgramme); - - // Activity Column Definition - - const activityTableColumns = getActivityTableColumns(); - - // Support Column Definition - - const supportTableColumns = getSupportTableColumns(); - // Programme Table Behaviour const handleTableChange = (pagination: any) => { @@ -899,23 +430,249 @@ const actionForm: React.FC = ({ method }) => { setIsSaveButtonDisabled(false); }; + // DB Queries + + const fetchActionData = async () => { + if (method !== 'create' && entId) { + try { + const response: any = await get(`national/actions/${entId}`); + if (response.status === 200 || response.status === 201) { + const entityData: any = response.data; + + // Populating Action owned data fields + form.setFieldsValue({ + title: entityData.title, + description: entityData.description, + objective: entityData.objective, + type: entityData.type, + sector: entityData.sector, + instrumentType: entityData.instrumentType, + status: entityData.status, + startYear: entityData.startYear, + natAnchor: entityData.natAnchor, + }); + + setIsValidated(entityData.validated ?? false); + + if (entityData.documents?.length > 0) { + const tempFiles: { key: string; title: string; url: string }[] = []; + entityData.documents.forEach((document: any) => { + tempFiles.push({ + key: document.createdTime, + title: document.title, + url: document.url, + }); + }); + setStoredFiles(tempFiles); + } + + // Populating Migrated Fields (Will be overwritten when attachments change) + + form.setFieldsValue({ + ghgsAffected: entityData.migratedData?.ghgsAffected, + natImplementor: entityData.migratedData?.natImplementors ?? [], + estimatedInvestment: entityData.migratedData?.totalInvestment, + achievedReduct: entityData.migratedData?.achievedGHGReduction, + expectedReduct: entityData.migratedData?.expectedGHGReduction, + }); + } + } catch { + navigate('/actions'); + } + setIsSaveButtonDisabled(true); + } + }; + + const fetchCreatedKPIData = async () => { + if (method !== 'create' && entId) { + try { + const response: any = await get(`national/kpis/achieved/action/${entId}`); + if (response.status === 200 || response.status === 201) { + const tempKpiList: CreatedKpiData[] = []; + let tempKpiCounter = kpiCounter; + response.data.forEach((kpi: any) => { + tempKpiList.push({ + index: tempKpiCounter, + creator: entId, + id: kpi.kpiId, + name: kpi.name, + unit: kpi.kpiUnit, + achieved: parseFloat(kpi.achieved ?? 0), + expected: parseFloat(kpi.expected ?? 0), + kpiAction: KPIAction.NONE, + }); + tempKpiCounter = tempKpiCounter + 1; + }); + setKpiCounter(tempKpiCounter); + setCreatedKpiList(tempKpiList); + + if (tempKpiList.length > 0) { + setHandleKPI(true); + } + } + } catch (error: any) { + console.log(error, t('kpiSearchFailed')); + } + } + }; + + const fetchConnectedProgrammeData = async () => { + if (method !== 'create') { + try { + const payload = { + filterAnd: [ + { + key: 'actionId', + operation: '=', + value: entId, + }, + ], + sort: { + key: 'programmeId', + order: 'ASC', + }, + }; + const response: any = await post('national/programmes/query', payload); + + const tempPRGData: ProgrammeData[] = []; + + response.data.forEach((prg: any, index: number) => { + tempPRGData.push({ + key: index.toString(), + programmeId: prg.programmeId, + actionId: prg.action?.actionId, + title: prg.title, + type: prg.migratedData[0]?.types ?? [], + status: prg.programmeStatus, + subSectorsAffected: prg.affectedSubSector ?? [], + estimatedInvestment: prg.investment, + ghgsAffected: prg.migratedData[0]?.ghgsAffected ?? [], + types: prg.migratedData[0]?.types ?? [], + natImplementer: prg.natImplementor ?? [], + achievedReduction: prg.migratedData[0]?.achievedGHGReduction ?? 0, + estimatedReduction: prg.migratedData[0]?.expectedGHGReduction ?? 0, + }); + }); + + setProgramData(tempPRGData); + } catch (error: any) { + displayErrorMessage(error); + } + } + }; + + const fetchConnectedActivityData = async () => { + if (method !== 'create') { + try { + const payload = { + filterAnd: [ + { + key: 'parentId', + operation: '=', + value: entId, + }, + { + key: 'parentType', + operation: '=', + value: 'action', + }, + ], + sort: { + key: 'activityId', + order: 'ASC', + }, + }; + + const activityResponse: any = await post('national/activities/query', payload); + const tempActivityData: ActivityData[] = []; + + activityResponse.data.forEach((act: any, index: number) => { + tempActivityData.push({ + key: index.toString(), + activityId: act.activityId, + title: act.title, + reductionMeasures: act.measure, + status: act.status, + natImplementor: act.nationalImplementingEntity ?? [], + ghgsAffected: act.ghgsAffected, + achievedReduction: act.achievedGHGReduction ?? 0, + estimatedReduction: act.expectedGHGReduction ?? 0, + }); + }); + + setActivityData(tempActivityData); + } catch (error: any) { + displayErrorMessage(error); + } + } + }; + + const fetchSupportData = async () => { + const supportPayload = { + filterOr: [] as any[], + sort: { + key: 'supportId', + order: 'ASC', + }, + }; + + if (activityData.length > 0) { + try { + activityData.forEach((activity) => { + supportPayload.filterOr.push({ + key: 'activityId', + operation: '=', + value: activity, + }); + }); + + const supportResponse: any = await post('national/supports/query', supportPayload); + + const tempSupportData: SupportData[] = []; + + supportResponse.data.forEach((sup: any, index: number) => { + tempSupportData.push({ + key: index.toString(), + supportId: sup.supportId, + financeNature: sup.financeNature, + direction: sup.direction, + finInstrument: + sup.financeNature === 'International' + ? sup.internationalFinancialInstrument + : sup.nationalFinancialInstrument, + estimatedUSD: getRounded(sup.requiredAmount ?? 0), + estimatedLC: getRounded(sup.requiredAmountDomestic ?? 0), + recievedUSD: getRounded(sup.receivedAmount ?? 0), + recievedLC: getRounded(sup.receivedAmountDomestic ?? 0), + }); + }); + + setSupportData(tempSupportData); + } catch (error: any) { + displayErrorMessage(error); + } + } else { + setSupportData([]); + } + }; + + // Dynamic Update + + useEffect(() => { + fetchActionData(); + fetchCreatedKPIData(); + fetchConnectedProgrammeData(); + fetchConnectedActivityData(); + }, []); + + // Fetching Activity data and calculating migrated fields when attachment changes + + useEffect(() => { + fetchSupportData(); + }, [activityData]); + return (
- } - isDanger={true} - content={{ - primaryMsg: `${t('detachPopup:primaryMsg')} Programme ${detachingEntityId}`, - secondaryMsg: t('detachPopup:secondaryMsg'), - cancelTitle: t('detachPopup:cancelTitle'), - actionTitle: t('detachPopup:actionTitle'), - }} - actionRef={detachingEntityId} - doAction={detachEntity} - open={openDetachPopup} - setOpen={setOpenDetachPopup} - /> } @@ -934,7 +691,7 @@ const actionForm: React.FC = ({ method }) => {
{t(formTitle)}
- {!waitingForBE && firstRenderingCompleted ? ( + {!waitingForBE ? (
= ({ method }) => { setIsSaveButtonDisabled={setIsSaveButtonDisabled} >
-
- - -
{t('programInfoTitle')}
- - - } - > - -
- - - - - -
+ {method !== 'create' && ( +
+ + +
{t('programInfoTitle')}
+ +
+ + + + + +
+ )} {method !== 'create' && (
From c4aba96cb40bb633d486b1915a0e212b4de1f078 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:36:39 +0530 Subject: [PATCH 11/66] Activity Form Init Refinement --- web/src/Definitions/activityDefinitions.tsx | 1 - .../Activities/ActivityForm/activityForm.tsx | 747 +++++++++--------- 2 files changed, 372 insertions(+), 376 deletions(-) diff --git a/web/src/Definitions/activityDefinitions.tsx b/web/src/Definitions/activityDefinitions.tsx index 92388a8b..a520743d 100644 --- a/web/src/Definitions/activityDefinitions.tsx +++ b/web/src/Definitions/activityDefinitions.tsx @@ -20,7 +20,6 @@ export type ParentData = { export type ActivityMigratedData = { description: string | undefined; type: string | undefined; - recipient: string[] | undefined; affSectors: string[] | undefined; affSubSectors: string[] | undefined; startYear: number | undefined; diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 565075e1..d9c0c1cd 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -15,7 +15,13 @@ import { ExpectedTimeline, } from '../../../Definitions/mtgTimeline.definition'; import { ActivityStatus, ImplMeans, Measure, TechnologyType } from '../../../Enums/activity.enum'; -import { GHGS, IntImplementor, KPIAction, NatImplementor } from '../../../Enums/shared.enum'; +import { + GHGS, + IntImplementor, + KPIAction, + NatImplementor, + Recipient, +} from '../../../Enums/shared.enum'; import EntityIdCard from '../../../Components/EntityIdCard/entityIdCard'; import { SupportData } from '../../../Definitions/supportDefinitions'; import { ActivityMigratedData, ParentData } from '../../../Definitions/activityDefinitions'; @@ -81,10 +87,6 @@ const ActivityForm: React.FC = ({ method }) => { const validation = getValidationRules(method); - // First Rendering Check - - const [firstRenderingCompleted, setFirstRenderingCompleted] = useState(false); - // Entity Validation Status const [isValidated, setIsValidated] = useState(false); @@ -170,136 +172,6 @@ const ActivityForm: React.FC = ({ method }) => { }); }; - // Tracking Parent selection - - useEffect(() => { - const fetchConnectedParent = async () => { - const tempMigratedData: ActivityMigratedData = { - description: undefined, - type: undefined, - recipient: undefined, - affSectors: undefined, - affSubSectors: undefined, - startYear: undefined, - endYear: undefined, - expectedTimeFrame: undefined, - }; - - if ( - (parentType === 'action' || parentType === 'programme' || parentType === 'project') && - connectedParentId - ) { - try { - const response: any = await get(`national/${parentType}s/${connectedParentId}`); - - if (parentType === 'action') { - tempMigratedData.description = response.data.description; - tempMigratedData.affSectors = response.data.sector ?? undefined; - tempMigratedData.startYear = response.data.startYear; - tempMigratedData.type = response.data.type; - } else if (parentType === 'programme') { - tempMigratedData.description = response.data.description; - tempMigratedData.recipient = response.data.recipientEntity; - tempMigratedData.affSectors = response.data.sector ?? undefined; - tempMigratedData.affSubSectors = response.data.affectedSubSector; - tempMigratedData.startYear = response.data.startYear; - tempMigratedData.type = response.data.type; - } else { - tempMigratedData.description = response.data.description; - tempMigratedData.recipient = response.data.recipientEntities; - tempMigratedData.affSectors = response.data.sector ?? undefined; - tempMigratedData.affSubSectors = response.data.programme?.affectedSubSector ?? []; - tempMigratedData.startYear = response.data.startYear; - tempMigratedData.endYear = response.data.endYear; - tempMigratedData.expectedTimeFrame = response.data.expectedTimeFrame; - tempMigratedData.type = response.data.programme?.action?.type; - } - if (method === 'create') { - setMtgStartYear(response.data.startYear); - } - } catch (error: any) { - displayErrorMessage(error); - } - } - setActivityMigratedData(tempMigratedData); - }; - fetchConnectedParent(); - - const fetchParentKPIData = async () => { - if (typeof connectedParentId === 'undefined') { - setInheritedKpiList([]); - } else if (method !== 'view' && parentType && connectedParentId && shouldFetchParentKpi) { - try { - const response: any = await get( - `national/kpis/entities/${parentType}/${connectedParentId}` - ); - if (response.status === 200 || response.status === 201) { - const tempInheritedKpiList: CreatedKpiData[] = []; - let tempKpiCounter = kpiCounter; - response.data.forEach((kpi: any) => { - tempInheritedKpiList.push({ - index: tempKpiCounter, - creator: kpi.creatorId, - id: kpi.kpiId, - name: kpi.name, - unit: kpi.kpiUnit, - achieved: 0, - expected: parseFloat(kpi.expected ?? 0), - kpiAction: KPIAction.NONE, - }); - - tempKpiCounter = tempKpiCounter + 1; - }); - setKpiCounter(tempKpiCounter); - setInheritedKpiList(tempInheritedKpiList); - } - } catch (error: any) { - console.log(error, t('kpiSearchFailed')); - } - } - }; - fetchParentKPIData(); - }, [connectedParentId]); - - // Loading All Entities that can be parent on Parent Type select - - useEffect(() => { - const fetchNonValidatedParents = async () => { - if (parentType === 'action' || parentType === 'programme' || parentType === 'project') { - try { - const payload = { - sort: { - key: `${parentType}Id`, - order: 'ASC', - }, - }; - const response: any = await post(`national/${parentType}s/query`, payload); - - const tempParentData: ParentData[] = []; - response.data.forEach((parent: any) => { - tempParentData.push({ - id: - parentType === 'action' - ? parent.actionId - : parentType === 'programme' - ? parent.programmeId - : parent.projectId, - title: parent.title, - }); - }); - setParentList(tempParentData); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - fetchNonValidatedParents(); - }, [parentType]); - - // Initializing Section - - //fetch GWP settings - const fetchGwpSettings = async () => { let response: any; try { @@ -316,217 +188,6 @@ const ActivityForm: React.FC = ({ method }) => { } }; - useEffect(() => { - // Initially Loading the underlying Activity data when not in create mode - - const fetchData = async () => { - if (method !== 'create' && entId) { - let response: any; - try { - response = await get(`national/activities/${entId}`); - - if (response.status === 200 || response.status === 201) { - const entityData: any = response.data; - - // Populating Action owned data fields - form.setFieldsValue({ - title: entityData.title, - description: entityData.description, - status: entityData.status, - measure: entityData.measure ?? undefined, - nationalImplementingEntity: entityData.nationalImplementingEntity ?? undefined, - internationalImplementingEntity: - entityData.internationalImplementingEntity ?? undefined, - anchoredInNationalStrategy: entityData.anchoredInNationalStrategy ?? undefined, - meansOfImplementation: entityData.meansOfImplementation ?? undefined, - technologyType: entityData.technologyType ?? undefined, - etfDescription: entityData.etfDescription ?? undefined, - comment: entityData.comment ?? undefined, - ghgsAffected: entityData.ghgsAffected ?? undefined, - achievedGHGReduction: entityData.achievedGHGReduction, - expectedGHGReduction: entityData.expectedGHGReduction, - }); - - // Populating Mitigation data fields - form.setFieldsValue({ - mtgMethodName: entityData.mitigationInfo?.mitigationMethodology ?? undefined, - mtgMethodDesc: - entityData.mitigationInfo?.mitigationMethodologyDescription ?? undefined, - mtgCalculateEntity: entityData.mitigationInfo?.mitigationCalcEntity ?? undefined, - mtgComments: entityData.mitigationInfo?.comments ?? undefined, - }); - - // Parent Data Update - - if (entityData.parentType) { - form.setFieldsValue({ - parentType: entityData.parentType, - parentId: entityData.parentId, - }); - setParentType(entityData.parentType ?? undefined); - setConnectedParentId(entityData.parentId ?? undefined); - } - - // Setting validation status - - setIsValidated(entityData.validated ?? false); - - // Setting selected ghg - - setSelectedGhg(entityData.ghgsAffected ?? undefined); - - // Setting up uploaded files - - if (entityData.documents?.length > 0) { - const tempFiles: { key: string; title: string; url: string }[] = []; - entityData.documents.forEach((document: any) => { - tempFiles.push({ - key: document.createdTime, - title: document.title, - url: document.url, - }); - }); - setStoredFiles(tempFiles); - } - - if (entityData.mitigationInfo?.methodologyDocuments?.length > 0) { - const tempFiles: { key: string; title: string; url: string }[] = []; - entityData.mitigationInfo?.methodologyDocuments.forEach((document: any) => { - tempFiles.push({ - key: document.createdTime, - title: document.title, - url: document.url, - }); - }); - setStoredMthFiles(tempFiles); - } - - if (entityData.mitigationInfo?.resultDocuments?.length > 0) { - const tempFiles: { key: string; title: string; url: string }[] = []; - entityData.mitigationInfo?.resultDocuments.forEach((document: any) => { - tempFiles.push({ - key: document.createdTime, - title: document.title, - url: document.url, - }); - }); - setStoredRstFiles(tempFiles); - } - } - } catch { - navigate('/activities'); - } - setIsSaveButtonDisabled(true); - } - }; - fetchData(); - - // Initially Loading the underlying Support data when not in create mode - - const fetchSupportData = async () => { - if (method !== 'create') { - try { - const tempSupportData: SupportData[] = []; - - const payload = { - filterAnd: [ - { - key: 'activityId', - operation: '=', - value: entId, - }, - ], - sort: { - key: 'supportId', - order: 'ASC', - }, - }; - - const response: any = await post('national/supports/query', payload); - - response.data.forEach((sup: any, index: number) => { - tempSupportData.push({ - key: index.toString(), - supportId: sup.supportId, - financeNature: sup.financeNature, - direction: sup.direction, - finInstrument: - sup.financeNature === 'International' - ? sup.internationalFinancialInstrument - : sup.nationalFinancialInstrument, - estimatedUSD: getRounded(sup.requiredAmount ?? 0), - estimatedLC: getRounded(sup.requiredAmountDomestic ?? 0), - recievedUSD: getRounded(sup.receivedAmount ?? 0), - recievedLC: getRounded(sup.receivedAmountDomestic ?? 0), - }); - }); - setSupportData(tempSupportData); - } catch { - setSupportData([]); - } - } - }; - fetchSupportData(); - - const fetchCreatedKPIData = async () => { - if (method !== 'create' && entId) { - try { - const response: any = await get(`national/kpis/entities/activity/${entId}`); - if (response.status === 200 || response.status === 201) { - const tempInheritedKpiList: CreatedKpiData[] = []; - let tempKpiCounter = kpiCounter; - response.data.forEach((kpi: any) => { - tempInheritedKpiList.push({ - index: tempKpiCounter, - creator: kpi.creatorId, - id: kpi.kpiId, - name: kpi.name, - unit: kpi.kpiUnit, - achieved: parseFloat( - kpi.achievements?.find((achEntity: any) => achEntity.activityId === entId) - ?.achieved ?? 0 - ), - expected: parseFloat(kpi.expected ?? 0), - kpiAction: KPIAction.NONE, - }); - - tempKpiCounter = tempKpiCounter + 1; - }); - setKpiCounter(tempKpiCounter); - setInheritedKpiList(tempInheritedKpiList); - } - } catch (error: any) { - console.log(error, t('kpiSearchFailed')); - } - } - }; - fetchCreatedKPIData(); - - // Fetch GWP Settings - - fetchGwpSettings(); - }, []); - - // Populating Form Migrated Fields, when migration data changes - - useEffect(() => { - if (activityMigratedData) { - form.setFieldsValue({ - parentDescription: activityMigratedData.description, - supportType: activityMigratedData.type, - recipient: activityMigratedData.recipient, - sector: activityMigratedData.affSectors, - affSubSectors: activityMigratedData.affSubSectors, - startYear: activityMigratedData.startYear, - endYear: activityMigratedData.endYear, - expectedTimeFrame: activityMigratedData.expectedTimeFrame, - }); - } - if (!firstRenderingCompleted) { - setFirstRenderingCompleted(true); - } - }, [activityMigratedData]); - // KPI Achievement Resolve const resolveKPIAchievements = async (activityId: string | undefined) => { @@ -921,16 +582,6 @@ const ActivityForm: React.FC = ({ method }) => { } }; - // Initializing mtg timeline data - - useEffect(() => { - if (method === 'create') { - setDefaultTimelineValues(); - } else { - fetchMtgTimelineData(); - } - }, [mtgStartYear, selectedGhg]); - // Form Submit const handleSubmit = async (payload: any) => { @@ -1098,6 +749,346 @@ const ActivityForm: React.FC = ({ method }) => { } }; + // DB Queries + + const fetchConnectedParent = async () => { + const tempMigratedData: ActivityMigratedData = { + description: undefined, + type: undefined, + affSectors: undefined, + affSubSectors: undefined, + startYear: undefined, + endYear: undefined, + expectedTimeFrame: undefined, + }; + + if ( + (parentType === 'action' || parentType === 'programme' || parentType === 'project') && + connectedParentId + ) { + try { + const response: any = await get(`national/${parentType}s/${connectedParentId}`); + + if (parentType === 'action') { + tempMigratedData.description = response.data.description; + tempMigratedData.affSectors = response.data.sector ?? undefined; + tempMigratedData.startYear = response.data.startYear; + tempMigratedData.type = response.data.type; + } else if (parentType === 'programme') { + tempMigratedData.description = response.data.description; + tempMigratedData.affSectors = response.data.sector ?? undefined; + tempMigratedData.affSubSectors = response.data.affectedSubSector; + tempMigratedData.startYear = response.data.startYear; + tempMigratedData.type = response.data.type; + } else { + tempMigratedData.description = response.data.description; + tempMigratedData.affSectors = response.data.sector ?? undefined; + tempMigratedData.affSubSectors = response.data.programme?.affectedSubSector ?? []; + tempMigratedData.startYear = response.data.startYear; + tempMigratedData.endYear = response.data.endYear; + tempMigratedData.expectedTimeFrame = response.data.expectedTimeFrame; + tempMigratedData.type = response.data.programme?.action?.type; + } + if (method === 'create') { + setMtgStartYear(response.data.startYear); + } + } catch (error: any) { + displayErrorMessage(error); + } + } + setActivityMigratedData(tempMigratedData); + }; + + const fetchParentKPIData = async () => { + if (typeof connectedParentId === 'undefined') { + setInheritedKpiList([]); + } else if (method !== 'view' && parentType && connectedParentId && shouldFetchParentKpi) { + try { + const response: any = await get( + `national/kpis/entities/${parentType}/${connectedParentId}` + ); + if (response.status === 200 || response.status === 201) { + const tempInheritedKpiList: CreatedKpiData[] = []; + let tempKpiCounter = kpiCounter; + response.data.forEach((kpi: any) => { + tempInheritedKpiList.push({ + index: tempKpiCounter, + creator: kpi.creatorId, + id: kpi.kpiId, + name: kpi.name, + unit: kpi.kpiUnit, + achieved: 0, + expected: parseFloat(kpi.expected ?? 0), + kpiAction: KPIAction.NONE, + }); + + tempKpiCounter = tempKpiCounter + 1; + }); + setKpiCounter(tempKpiCounter); + setInheritedKpiList(tempInheritedKpiList); + } + } catch (error: any) { + console.log(error, t('kpiSearchFailed')); + } + } + }; + + const fetchNonValidatedParents = async () => { + if (parentType === 'action' || parentType === 'programme' || parentType === 'project') { + try { + const payload = { + sort: { + key: `${parentType}Id`, + order: 'ASC', + }, + }; + const response: any = await post(`national/${parentType}s/query`, payload); + + const tempParentData: ParentData[] = []; + response.data.forEach((parent: any) => { + tempParentData.push({ + id: + parentType === 'action' + ? parent.actionId + : parentType === 'programme' + ? parent.programmeId + : parent.projectId, + title: parent.title, + }); + }); + setParentList(tempParentData); + } catch (error: any) { + displayErrorMessage(error); + } + } + }; + + const fetchActivityData = async () => { + if (method !== 'create' && entId) { + let response: any; + try { + response = await get(`national/activities/${entId}`); + + if (response.status === 200 || response.status === 201) { + const entityData: any = response.data; + + // Populating Action owned data fields + form.setFieldsValue({ + title: entityData.title, + description: entityData.description, + status: entityData.status, + measure: entityData.measure ?? undefined, + nationalImplementingEntity: entityData.nationalImplementingEntity ?? undefined, + internationalImplementingEntity: + entityData.internationalImplementingEntity ?? undefined, + anchoredInNationalStrategy: entityData.anchoredInNationalStrategy ?? undefined, + meansOfImplementation: entityData.meansOfImplementation ?? undefined, + technologyType: entityData.technologyType ?? undefined, + etfDescription: entityData.etfDescription ?? undefined, + comment: entityData.comment ?? undefined, + ghgsAffected: entityData.ghgsAffected ?? undefined, + achievedGHGReduction: entityData.achievedGHGReduction, + expectedGHGReduction: entityData.expectedGHGReduction, + recipientEntities: entityData.recipientEntities ?? [], + }); + + // Populating Mitigation data fields + form.setFieldsValue({ + mtgMethodName: entityData.mitigationInfo?.mitigationMethodology ?? undefined, + mtgMethodDesc: entityData.mitigationInfo?.mitigationMethodologyDescription ?? undefined, + mtgCalculateEntity: entityData.mitigationInfo?.mitigationCalcEntity ?? undefined, + mtgComments: entityData.mitigationInfo?.comments ?? undefined, + }); + + // Parent Data Update + + if (entityData.parentType) { + form.setFieldsValue({ + parentType: entityData.parentType, + parentId: entityData.parentId, + }); + setParentType(entityData.parentType ?? undefined); + setConnectedParentId(entityData.parentId ?? undefined); + } + + // Setting validation status + + setIsValidated(entityData.validated ?? false); + + // Setting selected ghg + + setSelectedGhg(entityData.ghgsAffected ?? undefined); + + // Setting up uploaded files + + if (entityData.documents?.length > 0) { + const tempFiles: { key: string; title: string; url: string }[] = []; + entityData.documents.forEach((document: any) => { + tempFiles.push({ + key: document.createdTime, + title: document.title, + url: document.url, + }); + }); + setStoredFiles(tempFiles); + } + + if (entityData.mitigationInfo?.methodologyDocuments?.length > 0) { + const tempFiles: { key: string; title: string; url: string }[] = []; + entityData.mitigationInfo?.methodologyDocuments.forEach((document: any) => { + tempFiles.push({ + key: document.createdTime, + title: document.title, + url: document.url, + }); + }); + setStoredMthFiles(tempFiles); + } + + if (entityData.mitigationInfo?.resultDocuments?.length > 0) { + const tempFiles: { key: string; title: string; url: string }[] = []; + entityData.mitigationInfo?.resultDocuments.forEach((document: any) => { + tempFiles.push({ + key: document.createdTime, + title: document.title, + url: document.url, + }); + }); + setStoredRstFiles(tempFiles); + } + } + } catch { + navigate('/activities'); + } + setIsSaveButtonDisabled(true); + } + }; + + const fetchSupportData = async () => { + if (method !== 'create') { + try { + const tempSupportData: SupportData[] = []; + + const payload = { + filterAnd: [ + { + key: 'activityId', + operation: '=', + value: entId, + }, + ], + sort: { + key: 'supportId', + order: 'ASC', + }, + }; + + const response: any = await post('national/supports/query', payload); + + response.data.forEach((sup: any, index: number) => { + tempSupportData.push({ + key: index.toString(), + supportId: sup.supportId, + financeNature: sup.financeNature, + direction: sup.direction, + finInstrument: + sup.financeNature === 'International' + ? sup.internationalFinancialInstrument + : sup.nationalFinancialInstrument, + estimatedUSD: getRounded(sup.requiredAmount ?? 0), + estimatedLC: getRounded(sup.requiredAmountDomestic ?? 0), + recievedUSD: getRounded(sup.receivedAmount ?? 0), + recievedLC: getRounded(sup.receivedAmountDomestic ?? 0), + }); + }); + setSupportData(tempSupportData); + } catch { + setSupportData([]); + } + } + }; + + const fetchCreatedKPIData = async () => { + if (method !== 'create' && entId) { + try { + const response: any = await get(`national/kpis/entities/activity/${entId}`); + if (response.status === 200 || response.status === 201) { + const tempInheritedKpiList: CreatedKpiData[] = []; + let tempKpiCounter = kpiCounter; + response.data.forEach((kpi: any) => { + tempInheritedKpiList.push({ + index: tempKpiCounter, + creator: kpi.creatorId, + id: kpi.kpiId, + name: kpi.name, + unit: kpi.kpiUnit, + achieved: parseFloat( + kpi.achievements?.find((achEntity: any) => achEntity.activityId === entId) + ?.achieved ?? 0 + ), + expected: parseFloat(kpi.expected ?? 0), + kpiAction: KPIAction.NONE, + }); + + tempKpiCounter = tempKpiCounter + 1; + }); + setKpiCounter(tempKpiCounter); + setInheritedKpiList(tempInheritedKpiList); + } + } catch (error: any) { + console.log(error, t('kpiSearchFailed')); + } + } + }; + + // Dynamic Updates + + // Initializing mtg timeline data + + useEffect(() => { + if (method === 'create') { + setDefaultTimelineValues(); + } else { + fetchMtgTimelineData(); + } + }, [mtgStartYear, selectedGhg]); + + // Tracking Parent selection + + useEffect(() => { + fetchConnectedParent(); + fetchParentKPIData(); + }, [connectedParentId]); + + // Loading All Entities that can be parent on Parent Type select + + useEffect(() => { + fetchNonValidatedParents(); + }, [parentType]); + + // Populating Form Migrated Fields, when migration data changes + + useEffect(() => { + if (activityMigratedData) { + form.setFieldsValue({ + parentDescription: activityMigratedData.description, + supportType: activityMigratedData.type, + sector: activityMigratedData.affSectors, + affSubSectors: activityMigratedData.affSubSectors, + startYear: activityMigratedData.startYear, + endYear: activityMigratedData.endYear, + expectedTimeFrame: activityMigratedData.expectedTimeFrame, + }); + } + }, [activityMigratedData]); + + useEffect(() => { + fetchActivityData(); + fetchSupportData(); + fetchCreatedKPIData(); + fetchGwpSettings(); + }, []); + return (
= ({ method }) => {
{t(formTitle)}
- {!waitingForBE && firstRenderingCompleted ? ( + {!waitingForBE ? (
= ({ method }) => { )} - {(parentType === 'programme' || parentType === 'project') && ( - - - {t('formHeader:recipientEntityHeader')} - - } - name="recipient" + + + {t('formHeader:recipientEntityHeader')} + + } + name="recipientEntities" + rules={[validation.required]} + > + - - - )} + {Object.values(Recipient).map((recipient) => ( + + ))} + + + {parentType === 'project' && ( Date: Wed, 23 Oct 2024 11:29:42 +0530 Subject: [PATCH 12/66] ACH and EXP Value fix in Activity Entity --- .../services/src/activity/activity.service.ts | 9 +- .../src/dtos/mitigationTimeline.dto.ts | 11 ++- .../services/src/entities/activity.entity.ts | 2 +- .../Activities/ActivityForm/activityForm.tsx | 90 +++++++++++++------ 4 files changed, 83 insertions(+), 29 deletions(-) diff --git a/backend/services/src/activity/activity.service.ts b/backend/services/src/activity/activity.service.ts index 21a91546..1456adef 100644 --- a/backend/services/src/activity/activity.service.ts +++ b/backend/services/src/activity/activity.service.ts @@ -1450,7 +1450,7 @@ export class ActivityService { //MARK: update mitigation timeline Data async updateMitigationTimeline(mitigationTimelineDto: mitigationTimelineDto, user: User) { - const { activityId, mitigationTimeline } = mitigationTimelineDto; + const { activityId, mitigationTimeline, expectedGHGReduction, achievedGHGReduction } = mitigationTimelineDto; const activity = await this.linkUnlinkService.findActivityByIdWithSupports(activityId); if (!activity) { @@ -1529,7 +1529,12 @@ export class ActivityService { await em .createQueryBuilder() .update(ActivityEntity) - .set({ mitigationTimeline: updatedMitigationTimeline, validated: activity.validated}) + .set( + { mitigationTimeline: updatedMitigationTimeline, + validated: activity.validated, + achievedGHGReduction: achievedGHGReduction, + expectedGHGReduction: expectedGHGReduction + }) .where('activityId = :activityId', { activityId }) .execute(); diff --git a/backend/services/src/dtos/mitigationTimeline.dto.ts b/backend/services/src/dtos/mitigationTimeline.dto.ts index 5471dc1b..7f7f6ba2 100644 --- a/backend/services/src/dtos/mitigationTimeline.dto.ts +++ b/backend/services/src/dtos/mitigationTimeline.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsArray, IsNotEmpty, IsString, ValidateNested } from "class-validator"; +import { IsArray, IsNotEmpty, IsNumber, IsString, ValidateNested } from "class-validator"; export class mitigationTimelineDto { @@ -37,4 +37,13 @@ export class mitigationTimelineDto { }; } + @IsNotEmpty() + @IsNumber() + @ApiProperty() + achievedGHGReduction: number; + + @IsNotEmpty() + @IsNumber() + @ApiProperty() + expectedGHGReduction: number; } \ No newline at end of file diff --git a/backend/services/src/entities/activity.entity.ts b/backend/services/src/entities/activity.entity.ts index ccd8c821..aa64e0c3 100644 --- a/backend/services/src/entities/activity.entity.ts +++ b/backend/services/src/entities/activity.entity.ts @@ -43,7 +43,7 @@ export class ActivityEntity implements EntitySubject { nationalImplementingEntity: NatImplementor[]; @Column("varchar", { array: true, nullable: false }) - recipientEntities: Recipient[]; + recipientEntities: Recipient[]; @Column({ nullable: true }) anchoredInNationalStrategy: boolean; diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index d9c0c1cd..335a6a9f 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -63,6 +63,7 @@ const { TextArea } = Input; const gutterSize = 30; const inputFontSize = '13px'; +const currentYear = new Date().getFullYear(); const ActivityForm: React.FC = ({ method }) => { const [form] = Form.useForm(); @@ -157,7 +158,8 @@ const ActivityForm: React.FC = ({ method }) => { const handleParentIdSelect = (id: string) => { setConnectedParentId(id); setShouldFetchParentKpi(true); - if (id === undefined) { + + if (id === undefined && method === 'create') { setMtgStartYear(0); } }; @@ -165,7 +167,11 @@ const ActivityForm: React.FC = ({ method }) => { const handleParentTypeSelect = (value: string) => { setParentType(value); setConnectedParentId(undefined); - setMtgStartYear(0); + + if (method === 'create') { + setMtgStartYear(0); + } + form.setFieldsValue({ parentId: '', parentDescription: '', @@ -532,7 +538,7 @@ const ActivityForm: React.FC = ({ method }) => { // MTG timeline update - const MtgTimelineUpdate = async () => { + const updateMtgTimeline = async () => { try { if (entId) { const payload = { @@ -563,6 +569,8 @@ const ActivityForm: React.FC = ({ method }) => { }, }, }, + achievedGHGReduction: parseFloat(form.getFieldValue('achievedGHGReduction')), + expectedGHGReduction: parseFloat(form.getFieldValue('expectedGHGReduction')), }; const response: any = await put('national/activities/mitigation/update', payload); @@ -1082,6 +1090,50 @@ const ActivityForm: React.FC = ({ method }) => { } }, [activityMigratedData]); + // Finding Achieved and Expected Emission Values + + useEffect(() => { + if (expectedTimeline && expectedTimeline.length === 5) { + const expectedValues = expectedTimeline[3].values; + const mostRecentYearIndex = Math.max(0, Math.min(mtgRange, currentYear - mtgStartYear)); + + let expectedValue = 0; + + for (let i = mostRecentYearIndex; i >= 0; i--) { + if (expectedValues[i] !== 0) { + expectedValue = expectedValues[i]; + break; + } + } + + form.setFieldsValue({ + expectedGHGReduction: expectedValue ?? 0, + }); + } + }, [expectedTimeline]); + + useEffect(() => { + if (actualTimeline && actualTimeline.length === 3) { + const actualValues = actualTimeline[2].values; + const mostRecentYearIndex = Math.max(0, Math.min(mtgRange, currentYear - mtgStartYear)); + + let actualValue = 0; + + for (let i = mostRecentYearIndex; i >= 0; i--) { + if (actualValues[i] !== 0) { + actualValue = actualValues[i]; + break; + } + } + + form.setFieldsValue({ + achievedGHGReduction: actualValue ?? 0, + }); + } + }, [actualTimeline]); + + // Init Run + useEffect(() => { fetchActivityData(); fetchSupportData(); @@ -1539,11 +1591,13 @@ const ActivityForm: React.FC = ({ method }) => { showSearch onChange={(value: GHGS) => setSelectedGhg(value)} > - {Object.values(GHGS).map((ghg) => ( - - ))} + {Object.values(GHGS) + .filter((ghg) => ghg !== GHGS.COE) + .map((ghg) => ( + + ))} @@ -1553,30 +1607,16 @@ const ActivityForm: React.FC = ({ method }) => { {t('formHeader:achieved')}} name="achievedGHGReduction" - rules={[validation.required]} > - + {t('formHeader:expected')}} name="expectedGHGReduction" - rules={[validation.required]} > - + @@ -1741,7 +1781,7 @@ const ActivityForm: React.FC = ({ method }) => { size="large" block onClick={() => { - MtgTimelineUpdate(); + updateMtgTimeline(); }} disabled={!isMtgButtonEnabled} > From 647bac0c08dd1af8f13ec22e8812fc9d237c3570 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:34:39 +0530 Subject: [PATCH 13/66] Action Form Conditional table render fixed --- web/src/Definitions/columns/programmeColumns.tsx | 9 +-------- web/src/Pages/Actions/ActionForm/actionForm.tsx | 10 +++++----- web/src/Pages/Activities/ActivityList/activityList.tsx | 2 +- web/src/Pages/Projects/ProjectList/projectList.tsx | 6 ++++-- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/web/src/Definitions/columns/programmeColumns.tsx b/web/src/Definitions/columns/programmeColumns.tsx index 4c9b5470..d4e70d9d 100644 --- a/web/src/Definitions/columns/programmeColumns.tsx +++ b/web/src/Definitions/columns/programmeColumns.tsx @@ -8,14 +8,7 @@ export const getProgrammeTableColumns = () => { { title: t('programmeId'), dataIndex: 'programmeId', key: 'programmeId' }, { title: t('actionId'), dataIndex: 'actionId', key: 'actionId' }, { title: t('programmeTitle'), dataIndex: 'title', key: 'title' }, - { - title: t('programmeType'), - width: 100, - // eslint-disable-next-line no-unused-vars - render: (_: any, record: any) => { - return ; - }, - }, + { title: t('programmeType'), dataIndex: 'type', key: 'title' }, { title: t('programmeStatus'), dataIndex: 'status', key: 'status' }, { title: t('subSectorAffected'), diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index f19a733e..d36aaeec 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -471,7 +471,7 @@ const actionForm: React.FC = ({ method }) => { form.setFieldsValue({ ghgsAffected: entityData.migratedData?.ghgsAffected, natImplementor: entityData.migratedData?.natImplementors ?? [], - estimatedInvestment: entityData.migratedData?.totalInvestment, + estimatedInvestment: entityData.migratedData?.financeNeeded ?? 0, achievedReduct: entityData.migratedData?.achievedGHGReduction, expectedReduct: entityData.migratedData?.expectedGHGReduction, }); @@ -542,7 +542,7 @@ const actionForm: React.FC = ({ method }) => { programmeId: prg.programmeId, actionId: prg.action?.actionId, title: prg.title, - type: prg.migratedData[0]?.types ?? [], + type: prg.action?.type, status: prg.programmeStatus, subSectorsAffected: prg.affectedSubSector ?? [], estimatedInvestment: prg.investment, @@ -931,7 +931,7 @@ const actionForm: React.FC = ({ method }) => { setIsSaveButtonDisabled={setIsSaveButtonDisabled} >
- {method !== 'create' && ( + {method !== 'create' && programData.length > 0 && (
@@ -965,7 +965,7 @@ const actionForm: React.FC = ({ method }) => {
)} - {method !== 'create' && ( + {method !== 'create' && activityData.length > 0 && (
@@ -998,7 +998,7 @@ const actionForm: React.FC = ({ method }) => {
)} - {method !== 'create' && ( + {method !== 'create' && supportData.length > 0 && (
diff --git a/web/src/Pages/Activities/ActivityList/activityList.tsx b/web/src/Pages/Activities/ActivityList/activityList.tsx index 0f6c5cf9..f52cb04b 100644 --- a/web/src/Pages/Activities/ActivityList/activityList.tsx +++ b/web/src/Pages/Activities/ActivityList/activityList.tsx @@ -156,7 +156,7 @@ const activityList = () => { : tempParentType === 'project' ? unstructuredData[i].migratedData?.programme?.action?.type : '', - recipientEntity: unstructuredData[i].migratedData?.recipientEntities ?? [], + recipientEntity: unstructuredData[i].recipientEntities ?? [], intImplementingEntity: unstructuredData[i].internationalImplementingEntity ?? [], validationStatus: unstructuredData[i].validated ? 'validated' : 'pending', natImplementingEntity: unstructuredData[i].nationalImplementingEntity ?? [], diff --git a/web/src/Pages/Projects/ProjectList/projectList.tsx b/web/src/Pages/Projects/ProjectList/projectList.tsx index aff8a3fc..205d1e25 100644 --- a/web/src/Pages/Projects/ProjectList/projectList.tsx +++ b/web/src/Pages/Projects/ProjectList/projectList.tsx @@ -194,11 +194,13 @@ const projectList = () => { programmeId: unstructuredData[i].programme?.programmeId ?? '', title: unstructuredData[i].title, projectStatus: unstructuredData[i].projectStatus, - recipientEntity: unstructuredData[i].recipientEntities ?? [], + recipientEntity: unstructuredData[i].migratedData[0]?.recipientEntities ?? [], intImplementingEntity: unstructuredData[i].internationalImplementingEntities ?? [], validationStatus: unstructuredData[i].validated ? 'validated' : 'pending', natImplementingEntity: unstructuredData[i].programme?.natImplementor ?? [], - estimatedInvestment: Math.round(unstructuredData[i].programme?.investment ?? 0), + estimatedInvestment: Math.round( + unstructuredData[i].migratedData[0]?.estimatedAmount ?? 0 + ), }); } setTableData(structuredData); From ad4acf33df3fbd0a1564ecf44f102bc0e4bda6fe Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:32:57 +0530 Subject: [PATCH 14/66] Entity Attachment Fix for table pages --- web/src/Components/Popups/tableAction.tsx | 23 +-- .../Pages/Actions/ActionList/actionList.tsx | 134 +----------------- .../ProgrammeList/programmeList.tsx | 127 +---------------- .../Projects/ProjectList/projectList.tsx | 130 +---------------- 4 files changed, 8 insertions(+), 406 deletions(-) diff --git a/web/src/Components/Popups/tableAction.tsx b/web/src/Components/Popups/tableAction.tsx index f6730319..fe266f15 100644 --- a/web/src/Components/Popups/tableAction.tsx +++ b/web/src/Components/Popups/tableAction.tsx @@ -5,12 +5,7 @@ import { ProgrammeEntity } from '../../Entities/programme'; import { ProjectEntity } from '../../Entities/project'; import { ActivityEntity } from '../../Entities/activity'; import { SupportEntity } from '../../Entities/support'; -import { - DisconnectOutlined, - EditOutlined, - InfoCircleOutlined, - PlusOutlined, -} from '@ant-design/icons'; +import { DisconnectOutlined, EditOutlined, InfoCircleOutlined } from '@ant-design/icons'; export const actionMenuWithAttaching = ( calledIn: 'action' | 'programme' | 'project', @@ -18,9 +13,6 @@ export const actionMenuWithAttaching = ( entity: ActionEntity | ProgrammeEntity | ProjectEntity, recordId: string, validationStatus: 'pending' | 'validated', - getAttachedEntityIds: (recordId: string) => void, - setOpenAttaching: React.Dispatch>, - setSelectedEntityId: React.Dispatch>, navigate: any, t: any ) => { @@ -45,19 +37,6 @@ export const actionMenuWithAttaching = ( } }, }, - { - text: t(`tableAction:${calledIn}Attach`), - icon: , - // calledIn === 'project' used to prevent attaching activities - isDisabled: !ability.can(Action.Update, entity) || calledIn === 'project', - click: () => { - { - setOpenAttaching(true); - setSelectedEntityId(recordId); - getAttachedEntityIds(recordId); - } - }, - }, { text: t(`tableAction:${calledIn}Edit`), icon: , diff --git a/web/src/Pages/Actions/ActionList/actionList.tsx b/web/src/Pages/Actions/ActionList/actionList.tsx index b78836c9..1080084c 100644 --- a/web/src/Pages/Actions/ActionList/actionList.tsx +++ b/web/src/Pages/Actions/ActionList/actionList.tsx @@ -3,21 +3,14 @@ import '../../../Styles/app.scss'; import LayoutTable from '../../../Components/common/Table/layout.table'; import './actionList.scss'; import { Action } from '../../../Enums/action.enum'; -import { Button, Col, Row, Input, Dropdown, Popover, message, Radio, Space, MenuProps } from 'antd'; -import { - AppstoreOutlined, - EllipsisOutlined, - FilterOutlined, - PlusOutlined, - SearchOutlined, -} from '@ant-design/icons'; +import { Button, Col, Row, Input, Dropdown, Popover, Radio, Space, MenuProps } from 'antd'; +import { EllipsisOutlined, FilterOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons'; import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAbilityContext } from '../../../Casl/Can'; import { ActionEntity } from '../../../Entities/action'; import { useConnection } from '../../../Context/ConnectionContext/connectionContext'; import StatusChip from '../../../Components/StatusChip/statusChip'; -import SimpleAttachEntity from '../../../Components/Popups/simpleAttach'; import ScrollableList from '../../../Components/ScrollableList/scrollableList'; import { actionMenuWithAttaching } from '../../../Components/Popups/tableAction'; import { displayErrorMessage } from '../../../Utils/errorMessageHandler'; @@ -49,7 +42,7 @@ interface Filter { const actionList = () => { const navigate = useNavigate(); - const { get, post } = useConnection(); + const { post } = useConnection(); const ability = useAbilityContext(); const { t } = useTranslation(['actionList', 'tableAction', 'columnHeader', 'entityAction']); @@ -87,60 +80,6 @@ const actionList = () => { const [tempSearchValue, setTempSearchValue] = useState(''); const [searchValue, setSearchValue] = useState(''); - // Programme Attachment State - - const [openAttaching, setOpenAttaching] = useState(false); - const [allFreeProgrammeIds, setAllFreeProgrammeIds] = useState([]); - - const [selectedActionId, setSelectedActionId] = useState(); - const [attachedProgrammeIds, setAttachedProgrammeIds] = useState([]); - const [toBeAttached, setToBeAttached] = useState([]); - - // Free Prg Read from DB - - const getFreeProgrammeIds = async () => { - try { - const response: any = await get('national/programmes/link/eligible'); - - const freeProgrammeIds: string[] = []; - response.data.forEach((prg: any) => { - freeProgrammeIds.push(prg.programmeId); - }); - setAllFreeProgrammeIds(freeProgrammeIds); - } catch (error: any) { - displayErrorMessage(error); - } - }; - - // Get Attached Programmes - - const getAttachedProgrammeIds = async (actionId: string) => { - try { - const payload = { - filterAnd: [ - { - key: 'actionId', - operation: '=', - value: actionId, - }, - ], - sort: { - key: 'programmeId', - order: 'ASC', - }, - }; - const response: any = await post('national/programmes/query', payload); - - const freeProgrammeIds: string[] = []; - response.data.forEach((prg: any) => { - freeProgrammeIds.push(prg.programmeId); - }); - setAttachedProgrammeIds(freeProgrammeIds); - } catch (error: any) { - displayErrorMessage(error); - } - }; - // Data Read from DB const getAllData = async () => { @@ -216,38 +155,6 @@ const actionList = () => { } }; - // Attach Multiple Programmes for an Action - - const attachProgrammes = async () => { - try { - const payload = { - actionId: selectedActionId, - programmes: toBeAttached, - }; - const response: any = await post('national/programmes/link', payload); - if (response.status === 200 || response.status === 201) { - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - - message.open({ - type: 'success', - content: t('programmeLinkSuccess'), - duration: 3, - style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, - }); - - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - - getAllData(); - } - } catch (error: any) { - displayErrorMessage(error); - } - }; - // Handling Table Pagination and Sorting Changes // eslint-disable-next-line no-unused-vars @@ -298,27 +205,10 @@ const actionList = () => { // State Management - useEffect(() => { - getFreeProgrammeIds(); - if (!openAttaching) { - setAttachedProgrammeIds([]); - } - }, [openAttaching]); - useEffect(() => { getAllData(); }, [currentPage, pageSize, sortField, sortOrder, searchValue, appliedFilterValue]); - // Children Attachment Functionality - - useEffect(() => { - if (toBeAttached.length > 0) { - attachProgrammes(); - setToBeAttached([]); - setSelectedActionId(undefined); - } - }, [toBeAttached]); - // Action List Table Columns const columns = [ @@ -388,9 +278,6 @@ const actionList = () => { ActionEntity, record.actionId, record.validationStatus ?? 'pending', - getAttachedProgrammeIds, - setOpenAttaching, - setSelectedActionId, navigate, t )} @@ -518,21 +405,6 @@ const actionList = () => {
{t('viewTitle')}
- } - >
diff --git a/web/src/Pages/Programmes/ProgrammeList/programmeList.tsx b/web/src/Pages/Programmes/ProgrammeList/programmeList.tsx index 0169ec8d..a1999021 100644 --- a/web/src/Pages/Programmes/ProgrammeList/programmeList.tsx +++ b/web/src/Pages/Programmes/ProgrammeList/programmeList.tsx @@ -3,16 +3,14 @@ import '../../../Styles/app.scss'; import LayoutTable from '../../../Components/common/Table/layout.table'; import './programmeList.scss'; import { Action } from '../../../Enums/action.enum'; -import { Button, Col, Row, Input, Dropdown, Popover, message, Radio, Space, MenuProps } from 'antd'; +import { Button, Col, Row, Input, Dropdown, Popover, Radio, Space, MenuProps } from 'antd'; import { EllipsisOutlined, FilterOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons'; import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAbilityContext } from '../../../Casl/Can'; import { useConnection } from '../../../Context/ConnectionContext/connectionContext'; import StatusChip from '../../../Components/StatusChip/statusChip'; -import SimpleAttachEntity from '../../../Components/Popups/simpleAttach'; import { ProgrammeEntity } from '../../../Entities/programme'; -import { Layers } from 'react-bootstrap-icons'; import ScrollableList from '../../../Components/ScrollableList/scrollableList'; import { actionMenuWithAttaching } from '../../../Components/Popups/tableAction'; import { displayErrorMessage } from '../../../Utils/errorMessageHandler'; @@ -43,7 +41,7 @@ interface Filter { const programmeList = () => { const navigate = useNavigate(); - const { get, post } = useConnection(); + const { post } = useConnection(); const ability = useAbilityContext(); const { t } = useTranslation(['programmeList', 'tableAction', 'columnHeader', 'entityAction']); @@ -81,60 +79,6 @@ const programmeList = () => { const [tempSearchValue, setTempSearchValue] = useState(''); const [searchValue, setSearchValue] = useState(''); - // Project Attachment State - - const [openAttaching, setOpenAttaching] = useState(false); - const [allFreeProjectIds, setAllFreeProjectIds] = useState([]); - - const [selectedProgrammeId, setSelectedProgrammeId] = useState(); - const [attachedProjectIds, setAttachedProjectIds] = useState([]); - const [toBeAttached, setToBeAttached] = useState([]); - - // Free Prg Read from DB - - const getFreeProjectIds = async () => { - try { - const response: any = await get('national/projects/link/eligible'); - - const freeProjectIds: string[] = []; - response.data.forEach((prj: any) => { - freeProjectIds.push(prj.projectId); - }); - setAllFreeProjectIds(freeProjectIds); - } catch (error: any) { - displayErrorMessage(error); - } - }; - - // Get Attached Projects - - const getAttachedProjectIds = async (programmeId: string) => { - try { - const payload = { - filterAnd: [ - { - key: 'programmeId', - operation: '=', - value: programmeId, - }, - ], - sort: { - key: 'projectId', - order: 'ASC', - }, - }; - const response: any = await post('national/projects/query', payload); - - const freeProjectIds: string[] = []; - response.data.forEach((prj: any) => { - freeProjectIds.push(prj.projectId); - }); - setAttachedProjectIds(freeProjectIds); - } catch (error: any) { - displayErrorMessage(error); - } - }; - // Data Read from DB const getAllData = async () => { @@ -209,38 +153,6 @@ const programmeList = () => { } }; - // Attach Multiple Projects for a Project - - const attachProjects = async () => { - try { - const payload = { - programmeId: selectedProgrammeId, - projectIds: toBeAttached, - }; - const response: any = await post('national/projects/link', payload); - if (response.status === 200 || response.status === 201) { - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - - message.open({ - type: 'success', - content: t('projectLinkSuccess'), - duration: 3, - style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, - }); - - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - - getAllData(); - } - } catch (error: any) { - displayErrorMessage(error); - } - }; - // Handling Table Pagination and Sorting Changes // eslint-disable-next-line no-unused-vars @@ -291,27 +203,10 @@ const programmeList = () => { // State Management - useEffect(() => { - getFreeProjectIds(); - if (!openAttaching) { - setAttachedProjectIds([]); - } - }, [openAttaching]); - useEffect(() => { getAllData(); }, [currentPage, pageSize, sortField, sortOrder, searchValue, appliedFilterValue]); - // Children Attachment Functionality - - useEffect(() => { - if (toBeAttached.length > 0) { - attachProjects(); - setToBeAttached([]); - setSelectedProgrammeId(undefined); - } - }, [toBeAttached]); - // Action List Table Columns const columns = [ @@ -377,9 +272,6 @@ const programmeList = () => { ProgrammeEntity, record.programmeId, record.validationStatus ?? 'pending', - getAttachedProjectIds, - setOpenAttaching, - setSelectedProgrammeId, navigate, t )} @@ -507,21 +399,6 @@ const programmeList = () => {
{t('viewTitle')}
- } - >
diff --git a/web/src/Pages/Projects/ProjectList/projectList.tsx b/web/src/Pages/Projects/ProjectList/projectList.tsx index 205d1e25..9fb55241 100644 --- a/web/src/Pages/Projects/ProjectList/projectList.tsx +++ b/web/src/Pages/Projects/ProjectList/projectList.tsx @@ -3,7 +3,7 @@ import '../../../Styles/app.scss'; import LayoutTable from '../../../Components/common/Table/layout.table'; import './projectList.scss'; import { Action } from '../../../Enums/action.enum'; -import { Button, Col, Row, Input, Dropdown, Popover, message, Radio, Space, MenuProps } from 'antd'; +import { Button, Col, Row, Input, Dropdown, Popover, Radio, Space, MenuProps } from 'antd'; import { EllipsisOutlined, FilterOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons'; import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -11,9 +11,7 @@ import { useAbilityContext } from '../../../Casl/Can'; import { ActionEntity } from '../../../Entities/action'; import { useConnection } from '../../../Context/ConnectionContext/connectionContext'; import StatusChip from '../../../Components/StatusChip/statusChip'; -import SimpleAttachEntity from '../../../Components/Popups/simpleAttach'; import ScrollableList from '../../../Components/ScrollableList/scrollableList'; -import { GraphUpArrow } from 'react-bootstrap-icons'; import { actionMenuWithAttaching } from '../../../Components/Popups/tableAction'; import { ProjectEntity } from '../../../Entities/project'; import { displayErrorMessage } from '../../../Utils/errorMessageHandler'; @@ -45,7 +43,7 @@ interface Filter { const projectList = () => { const navigate = useNavigate(); - const { get, post } = useConnection(); + const { post } = useConnection(); const ability = useAbilityContext(); const { t } = useTranslation(['projectList', 'tableAction', 'columnHeader', 'entityAction']); @@ -83,60 +81,6 @@ const projectList = () => { const [tempSearchValue, setTempSearchValue] = useState(''); const [searchValue, setSearchValue] = useState(''); - // Programme Attachment State - - const [openAttaching, setOpenAttaching] = useState(false); - const [allFreeActivityIds, setAllFreeActivityIds] = useState([]); - - const [selectedProjectId, setSelectedProjectId] = useState(); - const [attachedActivityIds, setAttachedActivityIds] = useState([]); - const [toBeAttached, setToBeAttached] = useState([]); - - // Free Act Read from DB - - const getFreeActivityIds = async () => { - try { - const response: any = await get('national/activities/link/eligible'); - - const freeActivityIds: string[] = []; - response.data.forEach((act: any) => { - freeActivityIds.push(act.activityId); - }); - setAllFreeActivityIds(freeActivityIds); - } catch (error: any) { - displayErrorMessage(error); - } - }; - - // Get Attached Programmes - - const getAttachedActivityIds = async (projectId: string) => { - try { - const payload = { - filterAnd: [ - { - key: 'parentId', - operation: '=', - value: projectId, - }, - ], - sort: { - key: 'activityId', - order: 'ASC', - }, - }; - const response: any = await post('national/activities/query', payload); - - const attachedActIds: string[] = []; - response.data.forEach((act: any) => { - attachedActIds.push(act.activityId); - }); - setAttachedActivityIds(attachedActIds); - } catch (error: any) { - displayErrorMessage(error); - } - }; - // Data Read from DB const getAllData = async () => { @@ -213,41 +157,6 @@ const projectList = () => { } }; - // Attach Multiple Activities for a Project - - const attachActivities = async () => { - if (toBeAttached.length > 0) { - try { - const payload = { - parentType: 'project', - parentId: selectedProjectId, - activityIds: toBeAttached, - }; - const response: any = await post('national/activities/link', payload); - if (response.status === 200 || response.status === 201) { - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - - message.open({ - type: 'success', - content: t('activityLinkSuccess'), - duration: 3, - style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, - }); - - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - - getAllData(); - } - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - // Handling Table Pagination and Sorting Changes // eslint-disable-next-line no-unused-vars @@ -298,27 +207,10 @@ const projectList = () => { // State Management - useEffect(() => { - getFreeActivityIds(); - if (!openAttaching) { - setAttachedActivityIds([]); - } - }, [openAttaching]); - useEffect(() => { getAllData(); }, [currentPage, pageSize, sortField, sortOrder, searchValue, appliedFilterValue]); - // Children Attachment Functionality - - useEffect(() => { - if (toBeAttached.length > 0) { - attachActivities(); - setToBeAttached([]); - setSelectedProjectId(undefined); - } - }, [toBeAttached]); - // Action List Table Columns const columns = [ @@ -393,9 +285,6 @@ const projectList = () => { ProjectEntity, record.projectId, record.validationStatus ?? 'pending', - getAttachedActivityIds, - setOpenAttaching, - setSelectedProjectId, navigate, t )} @@ -523,21 +412,6 @@ const projectList = () => {
{t('viewTitle')}
- } - >
From 64bfd01e18d77c19aa9413e600d35a222af15426 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:58:21 +0530 Subject: [PATCH 15/66] Program View fixed to provide ghg values --- .../services/src/dtos/programme.view.dto.ts | 6 ++++ .../src/programme/programme.service.ts | 28 ++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/backend/services/src/dtos/programme.view.dto.ts b/backend/services/src/dtos/programme.view.dto.ts index e632a5d7..d1b0cf62 100644 --- a/backend/services/src/dtos/programme.view.dto.ts +++ b/backend/services/src/dtos/programme.view.dto.ts @@ -40,4 +40,10 @@ export class ProgrammeViewDto { validated: boolean; + achievedGHGReduction?: number; + + expectedGHGReduction?: number; + + ghgsAffected?: string[] + } \ No newline at end of file diff --git a/backend/services/src/programme/programme.service.ts b/backend/services/src/programme/programme.service.ts index 32eff87a..8d3912fc 100644 --- a/backend/services/src/programme/programme.service.ts +++ b/backend/services/src/programme/programme.service.ts @@ -988,11 +988,6 @@ export class ProgrammeService { if (programme.projects && programme.projects.length > 0) { for (const project of programme.projects) { - // if (project.recipientEntities) { - // project.recipientEntities.forEach(recipient => { - // recipientEntitySet.add(recipient); - // }); - // } if (project.internationalImplementingEntities) { project.internationalImplementingEntities.forEach(internationalImplementer => { interNationalImplementorSet.add(internationalImplementer); @@ -1002,24 +997,24 @@ export class ProgrammeService { } if (migratedData && migratedData.length > 0) { - for (const data of migratedData) { - if (data.recipientEntities) { - data.recipientEntities.forEach((recipientEntity) => { - recipientEntitySet.add(recipientEntity); - }); - } - } - } + for (const data of migratedData) { + if (data.recipientEntities) { + data.recipientEntities.forEach((recipientEntity) => { + recipientEntitySet.add(recipientEntity); + }); + } + } + } if (programme.action) { type = programme.action.type; } - const recipientEntity: string[] = Array.from(recipientEntitySet); const interNationalImplementor: string[] = Array.from(interNationalImplementorSet); const programmeViewDto = new ProgrammeViewDto(); + programmeViewDto.programmeId = programme.programmeId; programmeViewDto.actionId = programme.action?.actionId; programmeViewDto.type = type; @@ -1035,12 +1030,13 @@ export class ProgrammeService { programmeViewDto.startYear = programme.startYear; programmeViewDto.interNationalImplementor = interNationalImplementor; programmeViewDto.nationalImplementor = programme.natImplementor; - // programmeViewDto.investment = programme.investment; programmeViewDto.documents = programme.documents; programmeViewDto.comments = programme.comments; programmeViewDto.validated = programme.validated; + programmeViewDto.ghgsAffected = migratedData[0]?.ghgsAffected ?? []; + programmeViewDto.achievedGHGReduction = migratedData[0]?.achievedGHGReduction ?? 0; + programmeViewDto.expectedGHGReduction = migratedData[0]?.expectedGHGReduction ?? 0; return programmeViewDto; - } } \ No newline at end of file From 63b5bc34de32d688a07b28373770596372e0d7ae Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:14:09 +0530 Subject: [PATCH 16/66] Tech Dev and Capacity Building Fix --- web/src/Pages/Projects/ProjectForm/projectForm.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.tsx b/web/src/Pages/Projects/ProjectForm/projectForm.tsx index 9fc4843f..2c5209c9 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.tsx +++ b/web/src/Pages/Projects/ProjectForm/projectForm.tsx @@ -536,9 +536,13 @@ const ProjectForm: React.FC = ({ method }) => { // Populating Migrated Fields + const meansOfImplementation = entityData.migratedData?.meansOfImplementation ?? []; + form.setFieldsValue({ - techDevContribution: 'No', // Need a fix here - capBuildObjectives: 'No', + techDevContribution: meansOfImplementation.includes('Technology Development & Transfer') + ? 'Yes' + : 'No', + capBuildObjectives: meansOfImplementation.includes('Capacity Building') ? 'Yes' : 'No', techType: entityData.migratedData?.technologyTypes ?? [], neededUSD: getRounded(entityData.migratedData?.estimatedAmount ?? 0), neededLCL: getRounded(entityData.migratedData?.estimatedAmountDomestic ?? 0), From 4ca9a20031747ef4af43eb034bec6c9c9cf1b286 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:05:35 +0530 Subject: [PATCH 17/66] Warning for invalid parent actions selection added --- backend/services/src/action/action.service.ts | 183 +++++++++++------- .../src/national-api/action.controller.ts | 7 + web/src/Definitions/actionDefinitions.tsx | 1 + web/src/Definitions/activityDefinitions.tsx | 1 + .../Activities/ActivityForm/activityForm.tsx | 23 ++- .../ProgrammeForm/programmeForm.tsx | 21 +- 6 files changed, 153 insertions(+), 83 deletions(-) diff --git a/backend/services/src/action/action.service.ts b/backend/services/src/action/action.service.ts index 6fc350a6..955e438d 100644 --- a/backend/services/src/action/action.service.ts +++ b/backend/services/src/action/action.service.ts @@ -184,6 +184,47 @@ export class ActionService { .getOne(); } + async findAllActionsForAttaching() { + const allActions = await this.actionRepo.find({ + select: ["actionId", "title", "instrumentType", "sector", "type"] + }); + + const actionIds = allActions.map(action => action.actionId); + + const programmeCounts = await this.programmeRepo + .createQueryBuilder("programme") + .select("programme.actionId", "actionId") + .addSelect("COUNT(*)", "count") + .where("programme.actionId IN (:...actionIds)", { actionIds }) + .groupBy("programme.actionId") + .getRawMany(); + + const activityCounts = await this.activityRepo + .createQueryBuilder("activity") + .select("activity.parentId", "parentId") + .addSelect("COUNT(*)", "count") + .where("activity.parentId IN (:...actionIds)", { actionIds }) + .groupBy("activity.parentId") + .getRawMany(); + + const programmeCountMap = new Map(programmeCounts.map(pc => [pc.actionId, parseInt(pc.count, 10)])); + const activityCountMap = new Map(activityCounts.map(ac => [ac.parentId, parseInt(ac.count, 10)])); + + const attachingActionsDto = allActions.map(action => { + const countOfChildProgrammes = programmeCountMap.get(action.actionId) || 0; + const countOfChildActivities = activityCountMap.get(action.actionId) || 0; + + return { + ...action, + hasChildProgrammes: countOfChildProgrammes > 0, + hasChildActivities: countOfChildActivities > 0, + }; + }); + + return attachingActionsDto; + } + + async findActionViewById(actionId: string) { return await this.actionViewRepo.findOneBy({ id: actionId @@ -492,83 +533,83 @@ export class ActionService { ); } - //MARK: Delete Action - async deleteAction(deleteDto: DeleteDto, user: User) { - if (user.role !== Role.Admin && user.role !== Role.Root) { - throw new HttpException( - this.helperService.formatReqMessagesString( - "user.userUnAUth", - [] - ), - HttpStatus.FORBIDDEN - ); - } - - const action = await this.findActionByIdWithAllLinkedChildren(deleteDto.entityId); - if (!action) { + //MARK: Delete Action + async deleteAction(deleteDto: DeleteDto, user: User) { + if (user.role !== Role.Admin && user.role !== Role.Root) { + throw new HttpException( + this.helperService.formatReqMessagesString( + "user.userUnAUth", + [] + ), + HttpStatus.FORBIDDEN + ); + } + + const action = await this.findActionByIdWithAllLinkedChildren(deleteDto.entityId); + if (!action) { + throw new HttpException( + this.helperService.formatReqMessagesString( + "action.actionNotFound", + [deleteDto.entityId] + ), + HttpStatus.BAD_REQUEST + ); + } + + if (!this.helperService.doesUserHaveSectorPermission(user, action.sector)) { + throw new HttpException( + this.helperService.formatReqMessagesString( + "action.permissionDeniedForSector", + [action.actionId] + ), + HttpStatus.FORBIDDEN + ); + } + + const actionKPIs = await this.kpiService.getKpisByCreatorTypeAndCreatorId(EntityType.ACTION, action.actionId); + + const actionKpiIds = actionKPIs?.map(kpi => kpi.kpiId); + + const linkedActivityIds = action.activities?.map(activity => activity.activityId); + + + const act = await this.entityManager + .transaction(async (em) => { + + // related parent and children entity un-validation happens when projects are unlinking + await this.linkUnlinkService.unlinkProgrammesFromAction(action.programmes, action, action.actionId, user, this.entityManager, [], true); + const result = await em.delete(ActionEntity, action.actionId); + + if (result.affected > 0) { + if (linkedActivityIds && linkedActivityIds.length > 0) { + await em.delete(ActivityEntity, linkedActivityIds); + } + + if (actionKpiIds && actionKpiIds.length > 0) { + await em.delete(KpiEntity, actionKpiIds); + } + } + return result; + }) + .catch((err: any) => { + console.log(err); throw new HttpException( this.helperService.formatReqMessagesString( - "action.actionNotFound", - [deleteDto.entityId] + "action.actionDeletionFailed", + [err] ), HttpStatus.BAD_REQUEST ); - } - - if (!this.helperService.doesUserHaveSectorPermission(user, action.sector)) { - throw new HttpException( - this.helperService.formatReqMessagesString( - "action.permissionDeniedForSector", - [action.actionId] - ), - HttpStatus.FORBIDDEN - ); - } - - const actionKPIs = await this.kpiService.getKpisByCreatorTypeAndCreatorId(EntityType.ACTION, action.actionId); - - const actionKpiIds = actionKPIs?.map(kpi => kpi.kpiId); - - const linkedActivityIds = action.activities?.map(activity => activity.activityId); + }); - - const act = await this.entityManager - .transaction(async (em) => { - - // related parent and children entity un-validation happens when projects are unlinking - await this.linkUnlinkService.unlinkProgrammesFromAction(action.programmes, action, action.actionId, user, this.entityManager, [], true); - const result = await em.delete(ActionEntity, action.actionId); - - if (result.affected > 0) { - if (linkedActivityIds && linkedActivityIds.length > 0) { - await em.delete(ActivityEntity, linkedActivityIds); - } - - if (actionKpiIds && actionKpiIds.length > 0) { - await em.delete(KpiEntity, actionKpiIds); - } - } - return result; - }) - .catch((err: any) => { - console.log(err); - throw new HttpException( - this.helperService.formatReqMessagesString( - "action.actionDeletionFailed", - [err] - ), - HttpStatus.BAD_REQUEST - ); - }); - - await this.helperService.refreshMaterializedViews(this.entityManager); - return new DataResponseMessageDto( - HttpStatus.OK, - this.helperService.formatReqMessagesString("action.deleteActionSuccess", []), - null - ); - - } + await this.helperService.refreshMaterializedViews(this.entityManager); + return new DataResponseMessageDto( + HttpStatus.OK, + this.helperService.formatReqMessagesString("action.deleteActionSuccess", []), + null + ); + + } async validateAction(validateDto: ValidateDto, user: User) { diff --git a/backend/services/src/national-api/action.controller.ts b/backend/services/src/national-api/action.controller.ts index e7797018..0363f3bb 100644 --- a/backend/services/src/national-api/action.controller.ts +++ b/backend/services/src/national-api/action.controller.ts @@ -46,6 +46,13 @@ export class ActionController { return this.actionService.query(query, req.abilityCondition); } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard, PoliciesGuardEx(true, Action.Read, ActionEntity, true)) + @Get("attach/query") + queryActionForAttaching() { + return this.actionService.findAllActionsForAttaching(); + } + @ApiBearerAuth() @UseGuards(JwtAuthGuard, PoliciesGuardEx(true, Action.Read, ActionEntity, true)) @Get('/:id') diff --git a/web/src/Definitions/actionDefinitions.tsx b/web/src/Definitions/actionDefinitions.tsx index 18155f38..01dc9cdc 100644 --- a/web/src/Definitions/actionDefinitions.tsx +++ b/web/src/Definitions/actionDefinitions.tsx @@ -12,4 +12,5 @@ export type ActionSelectData = { instrumentType: string; sector: string; type: string; + hasChildActivities: boolean; }; diff --git a/web/src/Definitions/activityDefinitions.tsx b/web/src/Definitions/activityDefinitions.tsx index a520743d..a970c44e 100644 --- a/web/src/Definitions/activityDefinitions.tsx +++ b/web/src/Definitions/activityDefinitions.tsx @@ -15,6 +15,7 @@ export type ActivityData = { export type ParentData = { id: string; title: string; + hasChildProgrammes: boolean; }; export type ActivityMigratedData = { diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 335a6a9f..a124171d 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -850,7 +850,14 @@ const ActivityForm: React.FC = ({ method }) => { order: 'ASC', }, }; - const response: any = await post(`national/${parentType}s/query`, payload); + + let response: any; + + if (parentType !== 'action') { + response = await post(`national/${parentType}s/query`, payload); + } else { + response = await get('national/actions/attach/query'); + } const tempParentData: ParentData[] = []; response.data.forEach((parent: any) => { @@ -862,8 +869,10 @@ const ActivityForm: React.FC = ({ method }) => { ? parent.programmeId : parent.projectId, title: parent.title, + hasChildProgrammes: parent.hasChildProgrammes ?? false, }); }); + setParentList(tempParentData); } catch (error: any) { displayErrorMessage(error); @@ -1264,8 +1273,16 @@ const ActivityForm: React.FC = ({ method }) => { onChange={handleParentIdSelect} > {parentList.map((parent) => ( - ))} diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 4f2ecb86..0c3a2559 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -462,13 +462,7 @@ const ProgrammeForm: React.FC = ({ method }) => { const fetchNonValidatedActions = async () => { try { - const payload = { - sort: { - key: 'actionId', - order: 'ASC', - }, - }; - const response: any = await post('national/actions/query', payload); + const response: any = await get('national/actions/attach/query'); const tempActionData: ActionSelectData[] = []; response.data.forEach((action: any) => { @@ -478,6 +472,7 @@ const ProgrammeForm: React.FC = ({ method }) => { instrumentType: action.instrumentType, sector: action.sector, type: action.type, + hasChildActivities: action.hasChildActivities, }); }); setActionList(tempActionData); @@ -809,8 +804,16 @@ const ProgrammeForm: React.FC = ({ method }) => { }} > {actionList.map((action) => ( - ))} From 8bc9da7a38869d89bff8373374b29fda552517d9 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:11:56 +0530 Subject: [PATCH 18/66] Error Fix in action form --- backend/services/src/action/action.service.ts | 4 ++++ web/src/Pages/Actions/ActionForm/actionForm.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/services/src/action/action.service.ts b/backend/services/src/action/action.service.ts index 955e438d..fe7546eb 100644 --- a/backend/services/src/action/action.service.ts +++ b/backend/services/src/action/action.service.ts @@ -188,6 +188,10 @@ export class ActionService { const allActions = await this.actionRepo.find({ select: ["actionId", "title", "instrumentType", "sector", "type"] }); + + if (allActions.length === 0){ + return []; + } const actionIds = allActions.map(action => action.actionId); diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index d36aaeec..c10f51ab 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -622,7 +622,7 @@ const actionForm: React.FC = ({ method }) => { supportPayload.filterOr.push({ key: 'activityId', operation: '=', - value: activity, + value: activity.activityId, }); }); From 15a676f6d5cb285cbdde9416083c051b2207465f Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:49:33 +0530 Subject: [PATCH 19/66] KPI Unit Moved to text field --- web/src/Components/KPI/editKpi.tsx | 22 ++++++---------------- web/src/Components/KPI/newKpi.tsx | 22 ++++++---------------- web/src/Components/KPI/viewKpi.tsx | 13 ++----------- web/src/Enums/kpi.enum.ts | 7 ------- 4 files changed, 14 insertions(+), 50 deletions(-) delete mode 100644 web/src/Enums/kpi.enum.ts diff --git a/web/src/Components/KPI/editKpi.tsx b/web/src/Components/KPI/editKpi.tsx index bb8462bd..55ca350f 100644 --- a/web/src/Components/KPI/editKpi.tsx +++ b/web/src/Components/KPI/editKpi.tsx @@ -1,6 +1,5 @@ -import { Form, Input, Row, Col, Select, Card } from 'antd'; +import { Form, Input, Row, Col, Card } from 'antd'; import './kpiGrid.scss'; -import { KpiUnits } from '../../Enums/kpi.enum'; import { CreatedKpiData } from '../../Definitions/kpiDefinitions'; import { DeleteOutlined } from '@ant-design/icons'; @@ -15,8 +14,6 @@ interface Props { removeKPI?: (kpiId: number, inWhich: 'created' | 'new') => void; } -const { Option } = Select; - export const EditKpi: React.FC = ({ rules, index, @@ -55,22 +52,15 @@ export const EditKpi: React.FC = ({ rules={isFromActivity ? [] : rules} initialValue={kpi?.unit} > - { if (updateKPI) { - updateKPI(index, 'unit', selectedValue, 'created'); + updateKPI(index, 'unit', e.target.value, 'created'); } }} disabled={isFromActivity} - > - {Object.values(KpiUnits).map((unit) => ( - - ))} - + /> diff --git a/web/src/Components/KPI/newKpi.tsx b/web/src/Components/KPI/newKpi.tsx index dbdf0803..2d075784 100644 --- a/web/src/Components/KPI/newKpi.tsx +++ b/web/src/Components/KPI/newKpi.tsx @@ -1,7 +1,6 @@ import { DeleteOutlined } from '@ant-design/icons'; -import { Form, Input, Row, Col, Card, Select } from 'antd'; +import { Form, Input, Row, Col, Card } from 'antd'; import './kpiGrid.scss'; -import { KpiUnits } from '../../Enums/kpi.enum'; interface Props { form: any; @@ -12,8 +11,6 @@ interface Props { removeKPI?: (kpiId: number, inWhich: 'created' | 'new') => void; } -const { Option } = Select; - export const NewKpi: React.FC = ({ rules, index, headerNames, updateKPI, removeKPI }) => { return ( @@ -41,21 +38,14 @@ export const NewKpi: React.FC = ({ rules, index, headerNames, updateKPI, name={`kpi_unit_${index}`} rules={rules} > - { if (updateKPI) { - updateKPI(index, 'unit', selectedValue, 'new'); + updateKPI(index, 'unit', e.target.value, 'new'); } }} - > - {Object.values(KpiUnits).map((unit) => ( - - ))} - + /> diff --git a/web/src/Components/KPI/viewKpi.tsx b/web/src/Components/KPI/viewKpi.tsx index e902cca7..9d87c159 100644 --- a/web/src/Components/KPI/viewKpi.tsx +++ b/web/src/Components/KPI/viewKpi.tsx @@ -1,6 +1,5 @@ -import { Form, Input, Row, Col, Select, Tooltip } from 'antd'; +import { Form, Input, Row, Col, Tooltip } from 'antd'; import './kpiGrid.scss'; -import { KpiUnits } from '../../Enums/kpi.enum'; import { CreatedKpiData } from '../../Definitions/kpiDefinitions'; import { UserOutlined, UserSwitchOutlined } from '@ant-design/icons'; @@ -13,8 +12,6 @@ interface Props { ownerEntityId: string | undefined; } -const { Option } = Select; - export const ViewKpi: React.FC = ({ index, inherited, @@ -42,13 +39,7 @@ export const ViewKpi: React.FC = ({ name={`kpi_unit_${index}`} initialValue={kpi?.unit} > - + diff --git a/web/src/Enums/kpi.enum.ts b/web/src/Enums/kpi.enum.ts deleted file mode 100644 index cb6341d7..00000000 --- a/web/src/Enums/kpi.enum.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum KpiUnits { - Wp_INSTALLED = 'Wp-installed', - mWp_INSTALLED = 'mWp-installed', - kWh_INSTALLED = 'kWh-installed', - MWp_INSTALLED = 'MWp-installed', - GWp_INSTALLED = 'GWp-installed', -} From f257a52d8a0e5ff80e8ed4b18f991275422c0785 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:04:26 +0530 Subject: [PATCH 20/66] Summary Section Header refinement --- web/src/Pages/Actions/ActionForm/actionForm.tsx | 2 +- web/src/Pages/Activities/ActivityForm/activityForm.tsx | 2 +- web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx | 2 +- web/src/Pages/Projects/ProjectForm/projectForm.tsx | 2 +- web/src/locales/i18n/activityForm/en.json | 1 - web/src/locales/i18n/activityForm/fr.json | 1 - web/src/locales/i18n/activityForm/sin.json | 1 - web/src/locales/i18n/formHeader/en.json | 7 +++++-- web/src/locales/i18n/formHeader/fr.json | 7 +++++-- web/src/locales/i18n/formHeader/sin.json | 7 +++++-- web/src/locales/i18n/projectForm/en.json | 1 - web/src/locales/i18n/projectForm/fr.json | 1 - web/src/locales/i18n/projectForm/sin.json | 1 - 13 files changed, 19 insertions(+), 16 deletions(-) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index c10f51ab..b32d6650 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -1030,7 +1030,7 @@ const actionForm: React.FC = ({ method }) => {
)}
-
{t('formHeader:mitigationInfoTitle')}
+
{t('formHeader:actionResultsInfoTitle')}
= ({ method }) => { -
{t('mitigationInfoTitle')}
+
{t('formHeader:activityResultsInfoTitle')}
{t('formHeader:emissionInfoTitle')}
diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 0c3a2559..916ef4c5 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -1160,7 +1160,7 @@ const ProgrammeForm: React.FC = ({ method }) => {
)}
-
{t('formHeader:mitigationInfoTitle')}
+
{t('formHeader:programmeResultsInfoTitle')}
= ({ method }) => { setRemovedFiles={setFilesToRemove} setIsSaveButtonDisabled={setIsSaveButtonDisabled} > -
{t('mitigationInfoTitle')}
+
{t('formHeader:projectResultsInfoTitle')}
{t('formHeader:emissionInfoTitle')}
diff --git a/web/src/locales/i18n/activityForm/en.json b/web/src/locales/i18n/activityForm/en.json index f6549456..ec18d67e 100644 --- a/web/src/locales/i18n/activityForm/en.json +++ b/web/src/locales/i18n/activityForm/en.json @@ -13,7 +13,6 @@ "parentDescTitle": "Short Description of", "activityStatusTitle": "Activity Status", "activityCommentsTitle": "Comments on the Activity", - "mitigationInfoTitle": "Summary of Activities Results", "activityCreationSuccess": "Activity Added Successfully", "activityUpdateSuccess": "Activity Updated Successfully", "activityValidateSuccess": "Activity Validated Successfully", diff --git a/web/src/locales/i18n/activityForm/fr.json b/web/src/locales/i18n/activityForm/fr.json index b1482e62..14595b79 100644 --- a/web/src/locales/i18n/activityForm/fr.json +++ b/web/src/locales/i18n/activityForm/fr.json @@ -13,7 +13,6 @@ "parentDescTitle": "Description courte de", "activityStatusTitle": "Statut de l'activité", "activityCommentsTitle": "Commentaires sur l'activité", - "mitigationInfoTitle": "Résumé des résultats des activités", "activityCreationSuccess": "Activité ajoutée avec succès", "activityUpdateSuccess": "Activité mise à jour avec succès", "activityDeleteSuccess": "Activité supprimé avec succès", diff --git a/web/src/locales/i18n/activityForm/sin.json b/web/src/locales/i18n/activityForm/sin.json index f4133ffd..43d2c524 100644 --- a/web/src/locales/i18n/activityForm/sin.json +++ b/web/src/locales/i18n/activityForm/sin.json @@ -13,7 +13,6 @@ "parentDescTitle": "සොයාගැනීමේ ශීර්ෂය", "activityStatusTitle": "ක්‍රියාකාරකම් තත්වය", "activityCommentsTitle": "ක්‍රියාකාරකම් පරික්ෂාවන්", - "mitigationInfoTitle": "ක්‍රියාකාරකම් ප්‍රතිස්ථාපන සාරාංශ", "activityCreationSuccess": "ක්‍රියාකාරකම සාර්ථකව එකතු කරන ලදි", "activityUpdateSuccess": "ක්‍රියාකාරකම සාර්ථකව යාවත්කාලීන කරන ලදි", "activityDeleteSuccess": "ක්‍රියාකාරකම ඉවත් කිරීම සාර්ථකයි", diff --git a/web/src/locales/i18n/formHeader/en.json b/web/src/locales/i18n/formHeader/en.json index e3ed786d..e2b9084c 100644 --- a/web/src/locales/i18n/formHeader/en.json +++ b/web/src/locales/i18n/formHeader/en.json @@ -10,7 +10,6 @@ "kpiUnit" : "KPI Unit", "kpiName": "KPI Name", "updatesInfoTitle": "Updates", - "mitigationInfoTitle": "Mitigation Information", "attachActivity": "Attach Activities", "supportTypeTitle": "Type of Support", "measuresTitle": "Reduction Measures", @@ -39,5 +38,9 @@ "capBuildHeader": "Contribution to Capacity-Building Objectives", "noSupportMessage": "No Supports Connected", "noActivityMessage": "No Activities Connected", - "kpiSearchFailed": "Searching for KPIs Failed" + "kpiSearchFailed": "Searching for KPIs Failed", + "actionResultsInfoTitle": "Summary of Action Results", + "programmeResultsInfoTitle": "Summary of Programme Results", + "projectResultsInfoTitle": "Summary of Project Results", + "activityResultsInfoTitle": "Summary of Activity Results" } \ No newline at end of file diff --git a/web/src/locales/i18n/formHeader/fr.json b/web/src/locales/i18n/formHeader/fr.json index b99b6be5..2cf951b3 100644 --- a/web/src/locales/i18n/formHeader/fr.json +++ b/web/src/locales/i18n/formHeader/fr.json @@ -10,7 +10,6 @@ "kpiUnit": "Unité de KPI", "kpiName": "Nom du KPI", "updatesInfoTitle": "Mises à jour", - "mitigationInfoTitle": "Informations sur la mitigation", "attachProgramme": "Joindre des programmes", "attachActivity": "Joindre des activités", "supportTypeTitle": "Type de support", @@ -40,5 +39,9 @@ "capBuildHeader": "Contribution aux objectifs de renforcement des capacités", "noSupportMessage": "Aucun support connecté", "noActivityMessage": "Aucune activité connectée", - "kpiSearchFailed": "La recherche des KPI a échoué" + "kpiSearchFailed": "La recherche des KPI a échoué", + "actionResultsInfoTitle": "Résumé des résultats de l'action", + "programmeResultsInfoTitle": "Résumé des résultats du programme", + "projectResultsInfoTitle": "Résumé des résultats du projet", + "activityResultsInfoTitle": "Résumé des résultats de l'activité" } \ No newline at end of file diff --git a/web/src/locales/i18n/formHeader/sin.json b/web/src/locales/i18n/formHeader/sin.json index addaee68..aa969cca 100644 --- a/web/src/locales/i18n/formHeader/sin.json +++ b/web/src/locales/i18n/formHeader/sin.json @@ -11,7 +11,6 @@ "kpiUnit" : "KPI ඒකකය", "kpiName": "KPI නම", "updatesInfoTitle": "යාවත්කාලීන කිරීම්", - "mitigationInfoTitle": "අහස තහවුරු කිරීමේ තොරතුරු", "attachActivity": "සම්බන්ධ ක්‍රියාවලිය", "supportTypeTitle": "සහාය වර්ගය", "measuresTitle": "වැඩසටහන් ක්‍රියාවලිය", @@ -40,5 +39,9 @@ "capBuildHeader": "ස්ථාන ගැනීම් සඳහා තහවුරු", "noSupportMessage": "කිසිවක් සම්බන්ධ නොමැත", "noActivityMessage": "කිසිවක් සම්බන්ධ නොමැත", - "kpiSearchFailed": "KPI සොයා ගැනීම අසාර්ථක විය" + "kpiSearchFailed": "KPI සොයා ගැනීම අසාර්ථක විය", + "actionResultsInfoTitle": "ක්‍රියා මූලස්ථාන ප්‍රතිඵල සාරාංශය", + "programmeResultsInfoTitle": "වැඩසටහන් ප්‍රතිඵල සාරාංශය", + "projectResultsInfoTitle": "ප්‍රෝජේක්ට් ප්‍රතිඵල සාරාංශය", + "activityResultsInfoTitle": "ක්‍රියාකාරකම් ප්‍රතිඵල සාරාංශය" } \ No newline at end of file diff --git a/web/src/locales/i18n/projectForm/en.json b/web/src/locales/i18n/projectForm/en.json index c25153e8..88647e2b 100644 --- a/web/src/locales/i18n/projectForm/en.json +++ b/web/src/locales/i18n/projectForm/en.json @@ -10,7 +10,6 @@ "actionTitleHeader": "Title of Action", "programmeTitleHeader": "Title of Programme", "projectStatusHeader": "Project Status", - "mitigationInfoTitle": "Project Results ", "programmeCommentsTitle": "Comments on the Project", "financeInfoTitle": "Project Finance", "neededUSDHeader": "Estimated Amount Needed (USD)", diff --git a/web/src/locales/i18n/projectForm/fr.json b/web/src/locales/i18n/projectForm/fr.json index 1baba82f..d0d439b3 100644 --- a/web/src/locales/i18n/projectForm/fr.json +++ b/web/src/locales/i18n/projectForm/fr.json @@ -10,7 +10,6 @@ "actionTitleHeader": "Titre de l'action", "programmeTitleHeader": "Titre du programme", "projectStatusHeader": "Statut du projet", - "mitigationInfoTitle": "Résultats du projet", "programmeCommentsTitle": "Commentaires sur le projet", "financeInfoTitle": "Finance du projet", "neededUSDHeader": "Montant estimé nécessaire (USD)", diff --git a/web/src/locales/i18n/projectForm/sin.json b/web/src/locales/i18n/projectForm/sin.json index 74f7d1a3..764665b2 100644 --- a/web/src/locales/i18n/projectForm/sin.json +++ b/web/src/locales/i18n/projectForm/sin.json @@ -10,7 +10,6 @@ "actionTitleHeader": "ක්‍රියා මාතෘකාව", "programmeTitleHeader": "ව්‍යාපෘතියේ මාතෘකාව", "projectStatusHeader": "ව්‍යාපෘතියේ තත්ත්වය", - "mitigationInfoTitle": "ව්‍යාපෘතිය ප්‍රතිශත ප්‍රතිඵල", "programmeCommentsTitle": "ව්‍යාපෘතිය අදහස්", "financeInfoTitle": "ව්‍යාපෘති ප්‍රතිශත ප්‍රතිඵල", "neededUSDHeader": "අයිතිකරු අවශ්‍ය මුදල (USD)", From 0e38865c7c27187c6198dae00f4359cab1b48610 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:23:10 +0530 Subject: [PATCH 21/66] Form section main and sub header style update --- web/src/Pages/Actions/ActionForm/actionForm.scss | 2 ++ web/src/Pages/Activities/ActivityForm/activityForm.scss | 2 ++ web/src/Pages/Configurations/configurations.scss | 1 + web/src/Pages/Programmes/ProgrammeForm/programmeForm.scss | 2 ++ web/src/Pages/Projects/ProjectForm/projectForm.scss | 2 ++ web/src/Pages/Support/SupportForm/supportForm.scss | 2 ++ 6 files changed, 11 insertions(+) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.scss b/web/src/Pages/Actions/ActionForm/actionForm.scss index 2fb1a936..26b3007b 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.scss +++ b/web/src/Pages/Actions/ActionForm/actionForm.scss @@ -23,6 +23,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-bottom: 25px; font-weight: bold; + font-size: 15px; } .form-section-sub-header { @@ -30,6 +31,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-top: 10px; margin-bottom: 10px; + font-weight: 550; } .form-input-box { diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.scss b/web/src/Pages/Activities/ActivityForm/activityForm.scss index 6802bc70..f60cdc65 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.scss +++ b/web/src/Pages/Activities/ActivityForm/activityForm.scss @@ -20,6 +20,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-bottom: 25px; font-weight: bold; + font-size: 15px; } .form-section-sub-header { @@ -27,6 +28,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-top: 10px; margin-bottom: 10px; + font-weight: 550; } .form-input-box { diff --git a/web/src/Pages/Configurations/configurations.scss b/web/src/Pages/Configurations/configurations.scss index e7a8529f..581c445e 100644 --- a/web/src/Pages/Configurations/configurations.scss +++ b/web/src/Pages/Configurations/configurations.scss @@ -13,6 +13,7 @@ opacity: 0.8; margin-bottom: 35px; font-weight: bold; + font-size: 15px; } .form-item-header { diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.scss b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.scss index eba78cab..b20be06a 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.scss +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.scss @@ -20,6 +20,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-bottom: 25px; font-weight: bold; + font-size: 15px; } .form-section-sub-header { @@ -27,6 +28,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-top: 10px; margin-bottom: 10px; + font-weight: 550; } .form-input-box { diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.scss b/web/src/Pages/Projects/ProjectForm/projectForm.scss index 39fa48d2..caefe672 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.scss +++ b/web/src/Pages/Projects/ProjectForm/projectForm.scss @@ -20,6 +20,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-bottom: 25px; font-weight: bold; + font-size: 15px; } .form-section-sub-header { @@ -27,6 +28,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-top: 10px; margin-bottom: 10px; + font-weight: 550; } .form-input-box { diff --git a/web/src/Pages/Support/SupportForm/supportForm.scss b/web/src/Pages/Support/SupportForm/supportForm.scss index 8a2261e7..21b9144f 100644 --- a/web/src/Pages/Support/SupportForm/supportForm.scss +++ b/web/src/Pages/Support/SupportForm/supportForm.scss @@ -26,6 +26,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-bottom: 25px; font-weight: bold; + font-size: 15px; } .form-section-sub-header { @@ -33,6 +34,7 @@ body .ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) opacity: 0.8; margin-top: 10px; margin-bottom: 10px; + font-weight: 550; } .form-input-box { From 6d21efab506266c474b1461aadd49af6e555e771 Mon Sep 17 00:00:00 2001 From: tharindugayanga Date: Thu, 24 Oct 2024 12:40:23 +0530 Subject: [PATCH 22/66] programme-view estimateAmount aggregated --- .../src/entities/programme.view.entity.ts | 66 +++++++++++++------ 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/backend/services/src/entities/programme.view.entity.ts b/backend/services/src/entities/programme.view.entity.ts index 9052a1c3..ba0c8277 100644 --- a/backend/services/src/entities/programme.view.entity.ts +++ b/backend/services/src/entities/programme.view.entity.ts @@ -1,4 +1,4 @@ -import { Index, ViewColumn, ViewEntity } from "typeorm" +import { Index, ViewColumn, ViewEntity } from "typeorm"; export const programmeViewSQL = ` WITH fullprj AS ( @@ -9,6 +9,8 @@ WITH fullprj AS ( p_v_e."recipientEntities", COALESCE(SUM(p_v_e."achievedGHGReduction"), 0) AS "achievedGHGReduction", COALESCE(SUM(p_v_e."expectedGHGReduction"), 0) AS "expectedGHGReduction", + COALESCE(SUM(p_v_e."estimatedAmount"), 0) AS "estimatedAmount", + COALESCE(SUM(p_v_e."receivedAmount"), 0) AS "receivedAmount", CUSTOM_ARRAY_AGG(p_v_e."ghgsAffected") FILTER (WHERE p_v_e."ghgsAffected" IS NOT NULL) AS "ghgsAffected" FROM project prj @@ -18,7 +20,9 @@ WITH fullprj AS ( "achievedGHGReduction", "expectedGHGReduction", "ghgsAffected", - "recipientEntities" + "recipientEntities", + "estimatedAmount", + "receivedAmount" FROM project_view_entity ) p_v_e ON prj."projectId" = p_v_e.id @@ -30,9 +34,22 @@ act AS ( a."parentId" AS "programmeId", COALESCE(SUM(a."achievedGHGReduction"), 0) AS "achievedGHGReduction", COALESCE(SUM(a."expectedGHGReduction"), 0) AS "expectedGHGReduction", - ARRAY_AGG(a."ghgsAffected") FILTER (WHERE a."ghgsAffected" IS NOT NULL)::character varying[] AS "ghgsAffected" + COALESCE(SUM(sup."requiredAmount"), 0) AS "requiredAmount", + COALESCE(SUM(sup."receivedAmount"), 0) AS "receivedAmount", + ARRAY_AGG(a."ghgsAffected") FILTER (WHERE a."ghgsAffected" IS NOT NULL)::character varying[] AS "ghgsAffected", + CUSTOM_ARRAY_AGG(a."recipientEntities") FILTER (WHERE a."recipientEntities" IS NOT NULL)::character varying[] AS "recipientEntities" FROM activity a + LEFT JOIN ( + SELECT + "activityId", + SUM("requiredAmount") AS "requiredAmount", + SUM("receivedAmount") AS "receivedAmount" + FROM + support + GROUP BY + "activityId" + ) sup ON a."activityId" = sup."activityId" WHERE a."parentType" = 'programme' GROUP BY @@ -41,41 +58,48 @@ act AS ( SELECT p."programmeId" AS id, CUSTOM_ARRAY_AGG(fullprj."internationalImplementingEntities") FILTER (WHERE fullprj."internationalImplementingEntities" IS NOT NULL) AS "internationalImplementingEntities", - CUSTOM_ARRAY_AGG(fullprj."recipientEntities") FILTER (WHERE fullprj."recipientEntities" IS NOT NULL) AS "recipientEntities", + CUSTOM_ARRAY_AGG(DISTINCT COALESCE(fullprj."recipientEntities", '{}') || COALESCE(act."recipientEntities", '{}')) FILTER (WHERE (fullprj."recipientEntities" IS NOT NULL OR act."recipientEntities" IS NOT NULL)) AS "recipientEntities", COALESCE(SUM(fullprj."achievedGHGReduction"), 0) + COALESCE(act."achievedGHGReduction", 0) AS "achievedGHGReduction", COALESCE(SUM(fullprj."expectedGHGReduction"), 0) + COALESCE(act."expectedGHGReduction", 0) AS "expectedGHGReduction", - CUSTOM_ARRAY_AGG(DISTINCT COALESCE(fullprj."ghgsAffected", '{}') || COALESCE(act."ghgsAffected", '{}')) FILTER (WHERE (fullprj."ghgsAffected" IS NOT NULL OR act."ghgsAffected" IS NOT NULL)) AS "ghgsAffected" + CUSTOM_ARRAY_AGG(DISTINCT COALESCE(fullprj."ghgsAffected", '{}') || COALESCE(act."ghgsAffected", '{}')) FILTER (WHERE (fullprj."ghgsAffected" IS NOT NULL OR act."ghgsAffected" IS NOT NULL)) AS "ghgsAffected", + MAX(COALESCE(fullprj."estimatedAmount", 0) + COALESCE(act."requiredAmount", 0)) as "estimatedAmount", + MAX(COALESCE(fullprj."receivedAmount", 0) + COALESCE(act."receivedAmount", 0)) as "receivedAmount" FROM programme p LEFT JOIN fullprj ON p."programmeId" = fullprj."programmeId" LEFT JOIN act ON p."programmeId" = act."programmeId" GROUP BY - p."programmeId", act."achievedGHGReduction", act."expectedGHGReduction";` + p."programmeId", act."achievedGHGReduction", act."expectedGHGReduction";`; @ViewEntity({ - name: 'programme_view_entity', - materialized: true, - expression: programmeViewSQL, - synchronize: false, + name: "programme_view_entity", + materialized: true, + expression: programmeViewSQL, + synchronize: false, }) @Index("idx_programme_view_entity_id") export class ProgrammeViewEntity { + @ViewColumn() + id: string; - @ViewColumn() - id: string; + @ViewColumn() + internationalImplementingEntities: string[]; - @ViewColumn() - internationalImplementingEntities: string[]; + @ViewColumn() + recipientEntities: string[]; - @ViewColumn() - recipientEntities: string[]; + @ViewColumn() + achievedGHGReduction: number; - @ViewColumn() - achievedGHGReduction: number; + @ViewColumn() + expectedGHGReduction: number; + + @ViewColumn() + ghgsAffected: string[]; @ViewColumn() - expectedGHGReduction: number; + estimatedAmount: number @ViewColumn() - ghgsAffected: string[] -} \ No newline at end of file + receivedAmount: number +} From c8e37f6142a52739dabdd03c3098227d0e296f48 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:49:44 +0530 Subject: [PATCH 23/66] ACT and PRG Forms refined to limit flow conditions --- web/src/Definitions/actionDefinitions.tsx | 4 +- .../Pages/Actions/ActionForm/actionForm.tsx | 95 ++++++++------- .../ProgrammeForm/programmeForm.tsx | 111 +++++++++++------- web/src/Utils/utilServices.tsx | 13 ++ 4 files changed, 138 insertions(+), 85 deletions(-) diff --git a/web/src/Definitions/actionDefinitions.tsx b/web/src/Definitions/actionDefinitions.tsx index 01dc9cdc..12c946ab 100644 --- a/web/src/Definitions/actionDefinitions.tsx +++ b/web/src/Definitions/actionDefinitions.tsx @@ -1,3 +1,5 @@ +import { ActionType } from '../Enums/action.enum'; + export type ActionMigratedData = { ghgsAffected: string[]; natImplementer: string[]; @@ -11,6 +13,6 @@ export type ActionSelectData = { title: string; instrumentType: string; sector: string; - type: string; + type: ActionType; hasChildActivities: boolean; }; diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index b32d6650..e91b1085 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -23,6 +23,7 @@ import { doesUserHaveValidatePermission, getFormTitle, getRounded, + isGasFlowCheck, } from '../../../Utils/utilServices'; import { getValidationRules } from '../../../Utils/validationRules'; import { ActivityData } from '../../../Definitions/activityDefinitions'; @@ -92,6 +93,8 @@ const actionForm: React.FC = ({ method }) => { const [isSaveButtonDisabled, setIsSaveButtonDisabled] = useState(true); + const [isGasFlow, setIsGasFlow] = useState(false); + // Spinner For Form Submit const [waitingForBE, setWaitingForBE] = useState(false); @@ -721,6 +724,7 @@ const actionForm: React.FC = ({ method }) => { allowClear disabled={isView} showSearch + onChange={(selectedValue) => setIsGasFlow(isGasFlowCheck(selectedValue))} > {Object.values(ActionType).map((aType) => (
)}
-
{t('formHeader:actionResultsInfoTitle')}
- - - {t('formHeader:ghgAffected')} - } - name="ghgsAffected" - > - - - - -
{t('formHeader:emissionInfoTitle')}
- - - {t('formHeader:achieved')}} - name="achievedReduct" - > - - - - - {t('formHeader:expected')}} - name="expectedReduct" - > - - - - +
+ {isGasFlow ? t('formHeader:actionResultsInfoTitle') : t('formHeader:kpiInfoTitle')} +
+ {isGasFlow && ( + <> + + + {t('formHeader:ghgAffected')} + } + name="ghgsAffected" + > + + + + +
{t('formHeader:emissionInfoTitle')}
+ + + {t('formHeader:achieved')} + } + name="achievedReduct" + > + + + + + {t('formHeader:expected')} + } + name="expectedReduct" + > + + + + + + )} {(method === 'create' || method === 'update' || - (method === 'view' && createdKpiList.length > 0)) && ( -
{t('formHeader:kpiInfoTitle')}
- )} + (method === 'view' && createdKpiList.length > 0)) && + isGasFlow && ( +
{t('formHeader:kpiInfoTitle')}
+ )} {method === 'view' && createdKpiList.map((createdKPI: CreatedKpiData) => ( = ({ method }) => { const [isSaveButtonDisabled, setIsSaveButtonDisabled] = useState(true); + const [isGasFlow, setIsGasFlow] = useState(false); + // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); @@ -512,6 +515,12 @@ const ProgrammeForm: React.FC = ({ method }) => { setIsValidated(entityData.validated ?? false); + // Setting Gas Flow Type + + setIsGasFlow(isGasFlowCheck(entityData.type)); + + // Document Handling + if (entityData.documents?.length > 0) { const tempFiles: { key: string; title: string; url: string }[] = []; entityData.documents.forEach((document: any) => { @@ -794,13 +803,14 @@ const ProgrammeForm: React.FC = ({ method }) => { disabled={isView} showSearch onChange={(value: any) => { + const selectedAction = actionList.find((action) => action.id === value); form.setFieldsValue({ - instrumentType: actionList.find((action) => action.id === value) - ?.instrumentType, - sector: actionList.find((action) => action.id === value)?.sector, - type: actionList.find((action) => action.id === value)?.type, + instrumentType: selectedAction?.instrumentType, + sector: selectedAction?.sector, + type: selectedAction?.type, }); fetchParentKPIData(value); + setIsGasFlow(isGasFlowCheck(selectedAction?.type)); }} > {actionList.map((action) => ( @@ -1160,49 +1170,62 @@ const ProgrammeForm: React.FC = ({ method }) => {
)}
-
{t('formHeader:programmeResultsInfoTitle')}
- - - {t('formHeader:ghgAffected')} - } - name="ghgsAffected" - > - - - - -
{t('formHeader:emissionInfoTitle')}
- - - {t('formHeader:achieved')}} - name="achievedReduct" - > - - - - - {t('formHeader:expected')}} - name="expectedReduct" - > - - - - +
+ {isGasFlow + ? t('formHeader:programmeResultsInfoTitle') + : t('formHeader:kpiInfoTitle')} +
+ {isGasFlow && ( + <> + + + {t('formHeader:ghgAffected')} + } + name="ghgsAffected" + > + + + + +
{t('formHeader:emissionInfoTitle')}
+ + + {t('formHeader:achieved')} + } + name="achievedReduct" + > + + + + + {t('formHeader:expected')} + } + name="expectedReduct" + > + + + + + + )} {(method === 'create' || method === 'update' || (method === 'view' && - (inheritedKpiList.length > 0 || createdKpiList.length > 0))) && ( -
{t('formHeader:kpiInfoTitle')}
- )} + (inheritedKpiList.length > 0 || createdKpiList.length > 0))) && + isGasFlow && ( +
{t('formHeader:kpiInfoTitle')}
+ )} {inheritedKpiList.length > 0 && inheritedKpiList.map((createdKPI: CreatedKpiData) => ( { return ( @@ -215,3 +216,15 @@ export const calculateArraySum = (array: number[]) => { } return arrSum; }; + +export const isGasFlowCheck = (type: ActionType | undefined): boolean => { + if (!type) { + return false; + } + + if ([ActionType.MITIGATION, ActionType.CROSSCUT].includes(type)) { + return true; + } + + return false; +}; From 330b758f14216925fc1a1b2c51ee882a700c0582 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:44:37 +0530 Subject: [PATCH 24/66] PRJ and ACT Forms refined to limit flow conditions --- backend/services/src/dtos/activity.dto.ts | 8 +- .../services/src/dtos/activityUpdate.dto.ts | 3 + .../services/src/entities/activity.entity.ts | 6 +- web/src/Definitions/activityDefinitions.tsx | 4 +- .../Activities/ActivityForm/activityForm.tsx | 177 ++++++++++-------- .../Projects/ProjectForm/projectForm.tsx | 57 ++++-- web/src/locales/i18n/activityForm/en.json | 2 +- web/src/locales/i18n/activityForm/fr.json | 2 +- web/src/locales/i18n/activityForm/sin.json | 2 +- 9 files changed, 152 insertions(+), 109 deletions(-) diff --git a/backend/services/src/dtos/activity.dto.ts b/backend/services/src/dtos/activity.dto.ts index 3cce591d..24c1b7a9 100644 --- a/backend/services/src/dtos/activity.dto.ts +++ b/backend/services/src/dtos/activity.dto.ts @@ -133,6 +133,7 @@ export class ActivityDto { ) documents: DocumentDto[]; + @ValidateIf((c) => c.ghgsAffected) @IsNotEmpty() @ApiProperty({ enum: GHGS }) @IsEnum(GHGS, { @@ -140,10 +141,12 @@ export class ActivityDto { }) ghgsAffected: GHGS; + @ValidateIf((c) => c.achievedGHGReduction) @IsNumber() @ApiProperty() achievedGHGReduction: number; + @ValidateIf((c) => c.expectedGHGReduction) @IsNumber() @ApiProperty() expectedGHGReduction: number; @@ -177,10 +180,7 @@ export class ActivityDto { }) mitigationInfo: any; - @ValidateIf((o) => o.startYear && o.ghgsAffected) - @IsNotEmpty({ - message: "Mitigation timeline is required when startYear and ghgsAffected are provided." - }) + @IsOptional() @ApiPropertyOptional({ type: "object", example: { diff --git a/backend/services/src/dtos/activityUpdate.dto.ts b/backend/services/src/dtos/activityUpdate.dto.ts index 03a1ea15..b65c68ae 100644 --- a/backend/services/src/dtos/activityUpdate.dto.ts +++ b/backend/services/src/dtos/activityUpdate.dto.ts @@ -145,6 +145,7 @@ export class ActivityUpdateDto { ) removedDocuments: string[]; + @ValidateIf((c) => c.ghgsAffected) @IsNotEmpty() @ApiProperty({ enum: GHGS }) @IsEnum(GHGS, { @@ -152,10 +153,12 @@ export class ActivityUpdateDto { }) ghgsAffected: GHGS; + @ValidateIf((c) => c.achievedGHGReduction) @IsNumber() @ApiProperty() achievedGHGReduction: number; + @ValidateIf((c) => c.expectedGHGReduction) @IsNumber() @ApiProperty() expectedGHGReduction: number; diff --git a/backend/services/src/entities/activity.entity.ts b/backend/services/src/entities/activity.entity.ts index aa64e0c3..8fd3b853 100644 --- a/backend/services/src/entities/activity.entity.ts +++ b/backend/services/src/entities/activity.entity.ts @@ -60,13 +60,13 @@ export class ActivityEntity implements EntitySubject { @Column({ nullable: true }) etfDescription: string; - @Column({ type: "enum", enum: GHGS, nullable: false }) + @Column({ type: "enum", enum: GHGS, nullable: true }) ghgsAffected: string; - @Column({ type: 'double precision' }) + @Column({ type: 'double precision', nullable: true }) achievedGHGReduction: number; - @Column({ type: 'double precision' }) + @Column({ type: 'double precision', nullable: true }) expectedGHGReduction: number; @Column({ nullable: true }) diff --git a/web/src/Definitions/activityDefinitions.tsx b/web/src/Definitions/activityDefinitions.tsx index a970c44e..a5fa536d 100644 --- a/web/src/Definitions/activityDefinitions.tsx +++ b/web/src/Definitions/activityDefinitions.tsx @@ -1,3 +1,5 @@ +import { ActionType } from '../Enums/action.enum'; + export type ActivityData = { key: string; activityId: string; @@ -20,7 +22,7 @@ export type ParentData = { export type ActivityMigratedData = { description: string | undefined; - type: string | undefined; + type: ActionType | undefined; affSectors: string[] | undefined; affSubSectors: string[] | undefined; startYear: number | undefined; diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 75c4fb12..bf247b5e 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -33,6 +33,7 @@ import { doesUserHaveValidatePermission, getFormTitle, getRounded, + isGasFlowCheck, subtractTwoArrays, } from '../../../Utils/utilServices'; import { Action } from '../../../Enums/action.enum'; @@ -107,6 +108,8 @@ const ActivityForm: React.FC = ({ method }) => { const [isSaveButtonDisabled, setIsSaveButtonDisabled] = useState(true); + const [isGasFlow, setIsGasFlow] = useState(false); + // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); @@ -172,6 +175,8 @@ const ActivityForm: React.FC = ({ method }) => { setMtgStartYear(0); } + setIsGasFlow(false); + form.setFieldsValue({ parentId: '', parentDescription: '', @@ -802,7 +807,11 @@ const ActivityForm: React.FC = ({ method }) => { } } catch (error: any) { displayErrorMessage(error); + } finally { + setIsGasFlow(isGasFlowCheck(tempMigratedData.type)); } + } else { + setIsGasFlow(false); } setActivityMigratedData(tempMigratedData); }; @@ -1358,20 +1367,23 @@ const ActivityForm: React.FC = ({ method }) => { {t('formHeader:measuresTitle')} + } - name="measure" + name="recipientEntities" + rules={[validation.required]} > @@ -1444,31 +1456,30 @@ const ActivityForm: React.FC = ({ method }) => { )} - - - {t('formHeader:recipientEntityHeader')} - - } - name="recipientEntities" - rules={[validation.required]} - > - - - + + + + )} {parentType === 'project' && ( = ({ method }) => { -
{t('formHeader:activityResultsInfoTitle')}
-
{t('formHeader:emissionInfoTitle')}
- - - {t('formHeader:ghgAffected')} - } - name="ghgsAffected" - rules={[validation.required]} - > - - - - - - - {t('formHeader:achieved')}} - name="achievedGHGReduction" - > - - - - - {t('formHeader:expected')}} - name="expectedGHGReduction" - > - - - - + {isGasFlow && ( + <> +
+ {t('formHeader:activityResultsInfoTitle')} +
+
{t('formHeader:emissionInfoTitle')}
+ + + {t('formHeader:ghgAffected')} + } + name="ghgsAffected" + rules={[validation.required]} + > + + + + + + + {t('formHeader:achieved')} + } + name="achievedGHGReduction" + > + + + + + {t('formHeader:expected')} + } + name="expectedGHGReduction" + > + + + + + + )} {inheritedKpiList.length > 0 && (
{t('formHeader:kpiInfoTitle')}
)} diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.tsx b/web/src/Pages/Projects/ProjectForm/projectForm.tsx index 5efbd9d2..5f7a4555 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.tsx +++ b/web/src/Pages/Projects/ProjectForm/projectForm.tsx @@ -21,6 +21,7 @@ import { doesUserHaveValidatePermission, getFormTitle, getRounded, + isGasFlowCheck, } from '../../../Utils/utilServices'; import { Action } from '../../../Enums/action.enum'; import { ProjectEntity } from '../../../Entities/project'; @@ -92,6 +93,8 @@ const ProjectForm: React.FC = ({ method }) => { const [isSaveButtonDisabled, setIsSaveButtonDisabled] = useState(true); + const [isGasFlow, setIsGasFlow] = useState(false); + // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); @@ -661,6 +664,10 @@ const ProjectForm: React.FC = ({ method }) => { actionTitle: actionData.title, natAnchor: actionData.natAnchor, }); + + // Setting Gas Flow + + setIsGasFlow(isGasFlowCheck(actionData.type)); } } catch (error: any) { navigate('/projects'); @@ -1197,26 +1204,36 @@ const ProjectForm: React.FC = ({ method }) => { setRemovedFiles={setFilesToRemove} setIsSaveButtonDisabled={setIsSaveButtonDisabled} > -
{t('formHeader:projectResultsInfoTitle')}
-
{t('formHeader:emissionInfoTitle')}
- - - {t('formHeader:achieved')}} - name="achievedGHGReduction" - > - - - - - {t('formHeader:expected')}} - name="expectedGHGReduction" - > - - - - + {isGasFlow && ( + <> +
+ {t('formHeader:projectResultsInfoTitle')} +
+
{t('formHeader:emissionInfoTitle')}
+ + + {t('formHeader:achieved')} + } + name="achievedGHGReduction" + > + + + + + {t('formHeader:expected')} + } + name="expectedGHGReduction" + > + + + + + + )} {(method === 'create' || method === 'update' || (method === 'view' && diff --git a/web/src/locales/i18n/activityForm/en.json b/web/src/locales/i18n/activityForm/en.json index ec18d67e..cbaef76b 100644 --- a/web/src/locales/i18n/activityForm/en.json +++ b/web/src/locales/i18n/activityForm/en.json @@ -20,7 +20,7 @@ "activityDeleteSuccess": "Activity Deleted Successfully", "noSupportsMessage": "No Supports Attached", "supportTableHeader": "List of Support under the Activity", - "mtgInfoTitle": "Mitigation Information", + "mtgInfoTitle": "Additional Information", "mtgMethodName": "Name of Mitigation Methodology", "mtgDescTitle": "Short Description of Mitigation Methodology", "mtgCalculateEntityTitle": "Entity Completing the Mitigation Calculations", diff --git a/web/src/locales/i18n/activityForm/fr.json b/web/src/locales/i18n/activityForm/fr.json index 14595b79..e704870d 100644 --- a/web/src/locales/i18n/activityForm/fr.json +++ b/web/src/locales/i18n/activityForm/fr.json @@ -18,7 +18,7 @@ "activityDeleteSuccess": "Activité supprimé avec succès", "noSupportsMessage": "Aucun support attaché", "supportTableHeader": "Liste des supports sous l'activité", - "mtgInfoTitle": "Informations sur la mitigation", + "mtgInfoTitle": "Informations supplémentaire", "mtgMethodName": "Nom de la méthodologie de mitigation", "mtgDescTitle": "Description courte de la méthodologie de mitigation", "mtgCalculateEntityTitle": "Entité réalisant les calculs de mitigation", diff --git a/web/src/locales/i18n/activityForm/sin.json b/web/src/locales/i18n/activityForm/sin.json index 43d2c524..4e076d4b 100644 --- a/web/src/locales/i18n/activityForm/sin.json +++ b/web/src/locales/i18n/activityForm/sin.json @@ -18,7 +18,7 @@ "activityDeleteSuccess": "ක්‍රියාකාරකම ඉවත් කිරීම සාර්ථකයි", "noSupportsMessage": "සහායකයින් සම්බන්ධයෙන් නැත", "supportTableHeader": "ක්‍රියාකාරකම් සම්බන්ධය ලැයිස්තුව", - "mtgInfoTitle": "හිරිකම සාරාංශ", + "mtgInfoTitle": "අතිරේක සාරාංශ", "mtgMethodName": "හිරිකම ක්‍රමයේ නම", "mtgDescTitle": "හිරිකම ක්‍රමයේ ශීර්ෂය", "mtgCalculateEntityTitle": "හිරිකම ගණන කිරීමේ කර්තෘ අයින්ගේ නම", From ae4b1e812885a4b1c25cdb7085ea942214267f80 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:01:21 +0530 Subject: [PATCH 25/66] Added Estimated Investment Needs in Program Entity --- backend/services/src/dtos/programme.view.dto.ts | 5 +++-- backend/services/src/entities/programme.view.entity.ts | 8 ++++---- backend/services/src/programme/programme.service.ts | 2 ++ web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx | 2 +- web/src/Pages/Programmes/ProgrammeList/programmeList.tsx | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/backend/services/src/dtos/programme.view.dto.ts b/backend/services/src/dtos/programme.view.dto.ts index d1b0cf62..1a8d3757 100644 --- a/backend/services/src/dtos/programme.view.dto.ts +++ b/backend/services/src/dtos/programme.view.dto.ts @@ -32,8 +32,6 @@ export class ProgrammeViewDto { nationalImplementor: NatImplementor[]; - // investment: number; - documents: DocumentDto[]; comments: string; @@ -46,4 +44,7 @@ export class ProgrammeViewDto { ghgsAffected?: string[] + estimatedAmount: number + + receivedAmount: number } \ No newline at end of file diff --git a/backend/services/src/entities/programme.view.entity.ts b/backend/services/src/entities/programme.view.entity.ts index ba0c8277..2bbea930 100644 --- a/backend/services/src/entities/programme.view.entity.ts +++ b/backend/services/src/entities/programme.view.entity.ts @@ -97,9 +97,9 @@ export class ProgrammeViewEntity { @ViewColumn() ghgsAffected: string[]; - @ViewColumn() - estimatedAmount: number + @ViewColumn() + estimatedAmount: number - @ViewColumn() - receivedAmount: number + @ViewColumn() + receivedAmount: number } diff --git a/backend/services/src/programme/programme.service.ts b/backend/services/src/programme/programme.service.ts index 8d3912fc..5ad121d4 100644 --- a/backend/services/src/programme/programme.service.ts +++ b/backend/services/src/programme/programme.service.ts @@ -1036,6 +1036,8 @@ export class ProgrammeService { programmeViewDto.ghgsAffected = migratedData[0]?.ghgsAffected ?? []; programmeViewDto.achievedGHGReduction = migratedData[0]?.achievedGHGReduction ?? 0; programmeViewDto.expectedGHGReduction = migratedData[0]?.expectedGHGReduction ?? 0; + programmeViewDto.estimatedAmount = migratedData[0]?.estimatedAmount ?? 0; + programmeViewDto.receivedAmount = migratedData[0]?.receivedAmount ?? 0; return programmeViewDto; } diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 1cfc7af9..8d660b73 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -507,7 +507,7 @@ const ProgrammeForm: React.FC = ({ method }) => { sector: entityData.sector, affectedSubSector: entityData.affectedSubSector, natImplementor: entityData.nationalImplementor, - investment: entityData.investment, + investment: entityData.estimatedAmount ?? 0, comments: entityData.comments ?? undefined, }); diff --git a/web/src/Pages/Programmes/ProgrammeList/programmeList.tsx b/web/src/Pages/Programmes/ProgrammeList/programmeList.tsx index a1999021..220e4d60 100644 --- a/web/src/Pages/Programmes/ProgrammeList/programmeList.tsx +++ b/web/src/Pages/Programmes/ProgrammeList/programmeList.tsx @@ -139,7 +139,7 @@ const programmeList = () => { status: unstructuredData[i].programmeStatus, validationStatus: unstructuredData[i].validated ? 'validated' : 'pending', subSectorsAffected: unstructuredData[i].affectedSubSector, - investment: Math.round(unstructuredData[i].investment ?? 0), + investment: Math.round(unstructuredData[i].migratedData[0]?.estimatedAmount ?? 0), type: unstructuredData[i].action?.type, }); } From 845a26b34827fbe05f6f3c4fd2d4baaac2c142b2 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:18:37 +0530 Subject: [PATCH 26/66] Delete warning added to prevent delete with children cascade --- web/src/Pages/Actions/ActionForm/actionForm.tsx | 15 ++++++++++++--- .../Programmes/ProgrammeForm/programmeForm.tsx | 11 ++++++++++- web/src/locales/i18n/error/en.json | 4 +++- web/src/locales/i18n/error/fr.json | 4 +++- web/src/locales/i18n/error/sin.json | 4 +++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index e91b1085..4469ec0f 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -303,7 +303,16 @@ const actionForm: React.FC = ({ method }) => { // Entity Delete const deleteClicked = () => { - setOpenDeletePopup(true); + if (activityData.length > 0 || programData.length > 0) { + message.open({ + type: 'error', + content: t('error:actionDeletePrevented'), + duration: 3, + style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, + }); + } else { + setOpenDeletePopup(true); + } }; const deleteEntity = async () => { @@ -716,13 +725,13 @@ const actionForm: React.FC = ({ method }) => { {t('formHeader:typeHeader')}} name="type" - rules={[validation.required]} + rules={method === 'create' ? [validation.required] : undefined} > + {Object.values(Recipient).map((recipient) => ( + + ))} + + + Date: Fri, 25 Oct 2024 15:13:31 +0530 Subject: [PATCH 31/66] Bar Charts added for Negative values --- .../Components/Charts/BarChart/barChart.tsx | 94 ++++++ .../Components/Charts/PieChart/pieChart.tsx | 4 +- web/src/Definitions/dashboard.definitions.tsx | 2 +- web/src/Pages/Dashboard/dashboard.tsx | 294 +++++++++--------- web/src/locales/i18n/dashboard/en.json | 8 +- web/src/locales/i18n/dashboard/fr.json | 8 +- 6 files changed, 255 insertions(+), 155 deletions(-) create mode 100644 web/src/Components/Charts/BarChart/barChart.tsx diff --git a/web/src/Components/Charts/BarChart/barChart.tsx b/web/src/Components/Charts/BarChart/barChart.tsx new file mode 100644 index 00000000..844c4b91 --- /dev/null +++ b/web/src/Components/Charts/BarChart/barChart.tsx @@ -0,0 +1,94 @@ +import { Empty, Tag } from 'antd'; +import Chart from 'react-apexcharts'; +import { CustomFormatDate } from '../../../Utils/utilServices'; +import { ChartData, chartColorMappings } from '../../../Definitions/dashboard.definitions'; +import { useEffect, useState } from 'react'; + +interface Props { + chart: ChartData; + t: any; + chartHeight: number; +} + +const BarChart: React.FC = ({ chart, t, chartHeight }) => { + // Bar Chart General State + + const [chartColorMapping, setChartColorMapping] = useState([]); + + // Setting the Color Mapping + + useEffect(() => { + const tempChartColorMapping: string[] = [1, 2, 5, 6].includes(chart.chartId) + ? chartColorMappings.sectors + : chart.chartId === 5 + ? chartColorMappings.support + : chartColorMappings.finance; + + setChartColorMapping(tempChartColorMapping); + }, [chart.chartId]); + + return ( +
+ {chart.values.length > 0 ? ( + <> + + +
+ {CustomFormatDate(chart.lastUpdatedTime)} +
+ + ) : ( + + )} +
+ ); +}; + +export default BarChart; diff --git a/web/src/Components/Charts/PieChart/pieChart.tsx b/web/src/Components/Charts/PieChart/pieChart.tsx index bfa28106..a3e6e1c7 100644 --- a/web/src/Components/Charts/PieChart/pieChart.tsx +++ b/web/src/Components/Charts/PieChart/pieChart.tsx @@ -5,11 +5,11 @@ import { DashboardTotalFormatter, getArraySum, } from '../../../Utils/utilServices'; -import { PieChartData, chartColorMappings } from '../../../Definitions/dashboard.definitions'; +import { ChartData, chartColorMappings } from '../../../Definitions/dashboard.definitions'; import { useEffect, useState } from 'react'; interface Props { - chart: PieChartData; + chart: ChartData; t: any; chartWidth: number; } diff --git a/web/src/Definitions/dashboard.definitions.tsx b/web/src/Definitions/dashboard.definitions.tsx index 586e9c14..9f49854d 100644 --- a/web/src/Definitions/dashboard.definitions.tsx +++ b/web/src/Definitions/dashboard.definitions.tsx @@ -1,4 +1,4 @@ -export type PieChartData = { +export type ChartData = { chartId: 1 | 2 | 3 | 4 | 5 | 6; categories: string[]; values: number[]; diff --git a/web/src/Pages/Dashboard/dashboard.tsx b/web/src/Pages/Dashboard/dashboard.tsx index 48cfa2b3..d1852348 100644 --- a/web/src/Pages/Dashboard/dashboard.tsx +++ b/web/src/Pages/Dashboard/dashboard.tsx @@ -3,15 +3,15 @@ import './dashboard.scss'; import { InfoCircleOutlined } from '@ant-design/icons'; import ChartInformation from '../../Components/Popups/chartInformation'; import { useEffect, useState } from 'react'; -import { DashboardActionItem, PieChartData } from '../../Definitions/dashboard.definitions'; +import { DashboardActionItem, ChartData } from '../../Definitions/dashboard.definitions'; import LayoutTable from '../../Components/common/Table/layout.table'; import { useTranslation } from 'react-i18next'; import { useConnection } from '../../Context/ConnectionContext/connectionContext'; - import { getActionTableColumns } from '../../Definitions/columns/actionColumns'; import PieChart from '../../Components/Charts/PieChart/pieChart'; import { dashboardHalfColumnBps } from '../../Definitions/breakpoints/breakpoints'; import { displayErrorMessage } from '../../Utils/errorMessageHandler'; +import BarChart from '../../Components/Charts/BarChart/barChart'; const { Option } = Select; const { useBreakpoint } = Grid; @@ -43,13 +43,17 @@ const Dashboard = () => { // Individual Chart Data + const [actionChart, setActionChart] = useState(); + const [projectChart, setProjectChart] = useState(); + const [supportChart, setSupportChart] = useState(); + const [financeChart, setFinanceChart] = useState(); + const [mitigationRecentChart, setMitigationRecentChart] = useState(); + const [mitigationIndividualChart, setMitigationIndividualChart] = useState(); + + // Chart Dimensions + const [chartWidth, setChartWidth] = useState(450); - const [actionChart, setActionChart] = useState(); - const [projectChart, setProjectChart] = useState(); - const [supportChart, setSupportChart] = useState(); - const [financeChart, setFinanceChart] = useState(); - const [mitigationRecentChart, setMitigationRecentChart] = useState(); - const [mitigationIndividualChart, setMitigationIndividualChart] = useState(); + const [chartHeight, setChartHeight] = useState(225); // Year List to be shown in the Year Selector in Chart 5 @@ -64,16 +68,20 @@ const Dashboard = () => { useEffect(() => { if (screens.xxl) { setChartWidth(560); + setChartHeight(303); } else if (screens.xl) { setChartWidth(480); + setChartHeight(223); } else if (screens.lg) { setChartWidth(550); + setChartHeight(300); } else { setChartWidth(450); + setChartHeight(200); } }, [screens]); - // BE Call to fetch Action Data + // BE Call to fetch Data const getAllData = async () => { setLoading(true); @@ -114,134 +122,19 @@ const Dashboard = () => { } }; - // Data Fetching for GHG MTG Selected Year - - useEffect(() => { - const getIndividualMitigationChartData = async () => { - if (mtgYear) { - try { - const response: any = await get( - `stats/analytics/ghgMitigationSummaryForYear/${mtgYear}`, - undefined, - statServerUrl - ); - const mitigationIndividualChartData = response.data; - setMitigationIndividualChart({ - chartId: 5, - chartTitle: t('mtgIndividualChartTitle'), - chartDescription: t('mtgIndividualChartDescription'), - categories: mitigationIndividualChartData.stats.sectors.map((sector: string) => - sector === null ? 'No Sector Attached' : sector - ), - values: mitigationIndividualChartData.stats.totals.map((count: string) => - parseInt(count, 10) - ), - lastUpdatedTime: mitigationIndividualChartData.lastUpdate, - }); - } catch (error: any) { - displayErrorMessage(error); - } - } - }; - getIndividualMitigationChartData(); - }, [mtgYear]); - - // Data Fetching at the Initial Loading - - useEffect(() => { - const getClimateActionChartData = async () => { - try { - const response: any = await get('stats/analytics/actionsSummery', undefined, statServerUrl); - const actionChartData = response.data; - setActionChart({ - chartId: 1, - chartTitle: t('actionChartTitle'), - chartDescription: t('actionChartDescription'), - categories: actionChartData.stats.sectors, - values: actionChartData.stats.counts.map((count: string) => parseInt(count, 10)), - lastUpdatedTime: actionChartData.lastUpdate, - }); - } catch (error: any) { - displayErrorMessage(error); - } - }; - getClimateActionChartData(); - - const getProjectChartData = async () => { - try { - const response: any = await get('stats/analytics/projectSummary', undefined, statServerUrl); - const projectChartData = response.data; - setProjectChart({ - chartId: 2, - chartTitle: t('projectChartTitle'), - chartDescription: t('projectChartDescription'), - categories: projectChartData.stats.sectors.map((sector: string) => - sector === null ? 'No Sector Attached' : sector - ), - values: projectChartData.stats.counts.map((count: string) => parseInt(count, 10)), - lastUpdatedTime: projectChartData.lastUpdate, - }); - } catch (error: any) { - displayErrorMessage(error); - } - }; - getProjectChartData(); - - const getSupportChartData = async () => { - try { - const response: any = await get('stats/analytics/supportSummary', undefined, statServerUrl); - const supportChartData = response.data; - setSupportChart({ - chartId: 3, - chartTitle: t('supportChartTitle'), - chartDescription: t('supportChartDescription'), - categories: ['Support Received', 'Support Needed'], - values: [ - supportChartData.stats.supportReceivedActivities, - supportChartData.stats.supportNeededActivities, - ], - lastUpdatedTime: supportChartData.lastUpdate, - }); - } catch (error: any) { - displayErrorMessage(error); - } - }; - getSupportChartData(); - - const getFinanceChartData = async () => { - try { - const response: any = await get( - 'stats/analytics/supportFinanceSummary', - undefined, - statServerUrl - ); - const financeChartData = response.data; - setFinanceChart({ - chartId: 4, - chartTitle: t('financeChartTitle'), - chartDescription: t('financeChartDescription'), - categories: ['Support Received', 'Support Needed'], - values: [financeChartData.stats.supportReceived, financeChartData.stats.supportNeeded], - lastUpdatedTime: financeChartData.lastUpdate, - }); - } catch (error: any) { - displayErrorMessage(error); - } - }; - getFinanceChartData(); - - const getRecentMitigationChartData = async () => { + const getIndividualMitigationChartData = async () => { + if (mtgYear) { try { const response: any = await get( - 'stats/analytics/getGhgMitigationSummary', + `stats/analytics/ghgMitigationSummaryForYear/${mtgYear}`, undefined, statServerUrl ); const mitigationIndividualChartData = response.data; - setMitigationRecentChart({ - chartId: 6, - chartTitle: t('mtgRecentChartTitle'), - chartDescription: t('mtgRecentChartDescription'), + setMitigationIndividualChart({ + chartId: 5, + chartTitle: t('mtgIndividualChartTitle'), + chartDescription: t('mtgIndividualChartDescription'), categories: mitigationIndividualChartData.stats.sectors.map((sector: string) => sector === null ? 'No Sector Attached' : sector ), @@ -252,10 +145,127 @@ const Dashboard = () => { }); } catch (error: any) { displayErrorMessage(error); - } finally { - setChartLoading(false); } - }; + } + }; + + const getClimateActionChartData = async () => { + try { + const response: any = await get('stats/analytics/actionsSummery', undefined, statServerUrl); + const actionChartData = response.data; + setActionChart({ + chartId: 1, + chartTitle: t('actionChartTitle'), + chartDescription: t('actionChartDescription'), + categories: actionChartData.stats.sectors, + values: actionChartData.stats.counts.map((count: string) => parseInt(count, 10)), + lastUpdatedTime: actionChartData.lastUpdate, + }); + } catch (error: any) { + displayErrorMessage(error); + } + }; + + const getProjectChartData = async () => { + try { + const response: any = await get('stats/analytics/projectSummary', undefined, statServerUrl); + const projectChartData = response.data; + setProjectChart({ + chartId: 2, + chartTitle: t('projectChartTitle'), + chartDescription: t('projectChartDescription'), + categories: projectChartData.stats.sectors.map((sector: string) => + sector === null ? 'No Sector Attached' : sector + ), + values: projectChartData.stats.counts.map((count: string) => parseInt(count, 10)), + lastUpdatedTime: projectChartData.lastUpdate, + }); + } catch (error: any) { + displayErrorMessage(error); + } + }; + + const getSupportChartData = async () => { + try { + const response: any = await get('stats/analytics/supportSummary', undefined, statServerUrl); + const supportChartData = response.data; + setSupportChart({ + chartId: 3, + chartTitle: t('supportChartTitle'), + chartDescription: t('supportChartDescription'), + categories: ['Support Received', 'Support Needed'], + values: [ + supportChartData.stats.supportReceivedActivities, + supportChartData.stats.supportNeededActivities, + ], + lastUpdatedTime: supportChartData.lastUpdate, + }); + } catch (error: any) { + displayErrorMessage(error); + } + }; + + const getFinanceChartData = async () => { + try { + const response: any = await get( + 'stats/analytics/supportFinanceSummary', + undefined, + statServerUrl + ); + const financeChartData = response.data; + setFinanceChart({ + chartId: 4, + chartTitle: t('financeChartTitle'), + chartDescription: t('financeChartDescription'), + categories: ['Support Received', 'Support Needed'], + values: [financeChartData.stats.supportReceived, financeChartData.stats.supportNeeded], + lastUpdatedTime: financeChartData.lastUpdate, + }); + } catch (error: any) { + displayErrorMessage(error); + } + }; + + const getRecentMitigationChartData = async () => { + try { + const response: any = await get( + 'stats/analytics/getGhgMitigationSummary', + undefined, + statServerUrl + ); + const mitigationIndividualChartData = response.data; + setMitigationRecentChart({ + chartId: 6, + chartTitle: t('mtgRecentChartTitle'), + chartDescription: t('mtgRecentChartDescription'), + categories: mitigationIndividualChartData.stats.sectors.map((sector: string) => + sector === null ? 'No Sector Attached' : sector + ), + values: mitigationIndividualChartData.stats.totals.map((count: string) => + parseInt(count, 10) + ), + lastUpdatedTime: mitigationIndividualChartData.lastUpdate, + }); + } catch (error: any) { + displayErrorMessage(error); + } finally { + setChartLoading(false); + } + }; + + // Data Fetching for GHG MTG Selected Year + + useEffect(() => { + getIndividualMitigationChartData(); + }, [mtgYear]); + + // Data Fetching at the Initial Loading + + useEffect(() => { + getClimateActionChartData(); + getProjectChartData(); + getSupportChartData(); + getFinanceChartData(); getRecentMitigationChartData(); }, []); @@ -310,7 +320,7 @@ const Dashboard = () => {
- +
)} @@ -333,7 +343,7 @@ const Dashboard = () => {
- +
)} @@ -356,7 +366,7 @@ const Dashboard = () => {
- +
)} @@ -379,7 +389,7 @@ const Dashboard = () => {
- +
)} @@ -419,11 +429,7 @@ const Dashboard = () => {
- +
)} @@ -449,7 +455,7 @@ const Dashboard = () => {
- +
)} diff --git a/web/src/locales/i18n/dashboard/en.json b/web/src/locales/i18n/dashboard/en.json index 084d2779..4519bee3 100644 --- a/web/src/locales/i18n/dashboard/en.json +++ b/web/src/locales/i18n/dashboard/en.json @@ -7,10 +7,10 @@ "supportChartDescription": "Activities Supported Description", "financeChartTitle" : "Financing for Activities", "financeChartDescription": "Financing for Activities Description", - "mtgIndividualChartTitle" : "GHG Mitigation (tCO2e)", - "mtgIndividualChartDescription": "GHG Mitigation (tCO2e) Year Description", - "mtgRecentChartTitle" : "GHG Mitigation (tCO2e) Most Recent Year", - "mtgRecentChartDescription": "GHG Mitigation (tCO2e) Most Recent Year Description", + "mtgIndividualChartTitle" : "GHG Mitigation (ktCO2e)", + "mtgIndividualChartDescription": "GHG Mitigation (ktCO2e) Year Description", + "mtgRecentChartTitle" : "GHG Mitigation (ktCO2e) Most Recent Year", + "mtgRecentChartDescription": "GHG Mitigation (ktCO2e) Most Recent Year Description", "noChartDataAvailable": "No Statistics Available", "noActionsAvailable": "No Actions Available", "recentYear": "2023" diff --git a/web/src/locales/i18n/dashboard/fr.json b/web/src/locales/i18n/dashboard/fr.json index 3820dbcc..f84e853e 100644 --- a/web/src/locales/i18n/dashboard/fr.json +++ b/web/src/locales/i18n/dashboard/fr.json @@ -7,10 +7,10 @@ "supportChartDescription": "Description des activités soutenues", "financeChartTitle": "Financement des activités", "financeChartDescription": "Description du financement des activités", - "mtgIndividualChartTitle": "Mitigation des GES (tCO2e)", - "mtgIndividualChartDescription": "Description annuelle de la mitigation des GES (tCO2e)", - "mtgRecentChartTitle": "Mitigation des GES (tCO2e) année la plus récente", - "mtgRecentChartDescription": "Description de la mitigation des GES (tCO2e) année la plus récente", + "mtgIndividualChartTitle": "Mitigation des GES (ktCO2e)", + "mtgIndividualChartDescription": "Description annuelle de la mitigation des GES (ktCO2e)", + "mtgRecentChartTitle": "Mitigation des GES (ktCO2e) année la plus récente", + "mtgRecentChartDescription": "Description de la mitigation des GES (ktCO2e) année la plus récente", "noChartDataAvailable": "Aucune statistique disponible", "noActionsAvailable": "Pas d'actions disponibles", "recentYear": "2023" From 611be4ce934dc9cd667bffa9f2cb2baef395a351 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:48:36 +0530 Subject: [PATCH 32/66] Negative Value Fix in MTG Timeline --- web/src/Components/Timeline/timeline.tsx | 38 +++++++++---------- .../Activities/ActivityForm/activityForm.tsx | 2 +- web/src/Utils/utilServices.tsx | 6 ++- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/web/src/Components/Timeline/timeline.tsx b/web/src/Components/Timeline/timeline.tsx index d1a0ff09..389355f8 100644 --- a/web/src/Components/Timeline/timeline.tsx +++ b/web/src/Components/Timeline/timeline.tsx @@ -1,4 +1,4 @@ -import { Table, TableProps, Input, Grid } from 'antd'; +import { Table, TableProps, Grid, InputNumber } from 'antd'; import { ActualRows, ActualTimeline, @@ -108,7 +108,7 @@ const TimelineTable: React.FC = ({ expectedTableColumns.push({ title: year.toString(), dataIndex: 'values', - width: 80, + width: 120, align: 'center', render: (colValue: any, record: any) => { const isDisabled = @@ -116,17 +116,15 @@ const TimelineTable: React.FC = ({ record.topic === ExpectedRows.ROW_FIVE[1] || isView; return ( - { - const inputValue = event.target.value; - const regex = /^\d*$/; - if (regex.test(inputValue) || inputValue === '') { - onValueEnter('expected', record.topic, year.toString(), inputValue); - } + value={colValue[year - mtgStartYear]} + decimalSeparator="." + controls={false} + style={{ width: '100%', height: '30px' }} + onChange={(value: any) => { + onValueEnter('expected', record.topic, year.toString(), value); }} - className="input-box" /> ); }, @@ -135,22 +133,20 @@ const TimelineTable: React.FC = ({ actualTableColumns.push({ title: year.toString(), dataIndex: 'values', - width: 80, + width: 120, align: 'center', render: (colValue: any, record: any) => { const isDisabled = record.topic === ActualRows.ROW_THREE[1] || isView; return ( - { - const inputValue = event.target.value; - const regex = /^\d*$/; - if (regex.test(inputValue) || inputValue === '') { - onValueEnter('actual', record.topic, year.toString(), event.target.value); - } + value={colValue[year - mtgStartYear]} + decimalSeparator="." + controls={false} + style={{ width: '100%', height: '30px' }} + onChange={(value: any) => { + onValueEnter('actual', record.topic, year.toString(), value); }} - className="input-box" /> ); }, diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index bf247b5e..1c07125b 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -486,7 +486,7 @@ const ActivityForm: React.FC = ({ method }) => { year: any, value: string ) => { - const newValue = value ? parseInt(value) : 0; + const newValue = value ? parseFloat(parseFloat(value).toFixed(2)) : 0; if (tableType === 'expected') { const updatedTimeline = expectedTimeline.map((entry) => { diff --git a/web/src/Utils/utilServices.tsx b/web/src/Utils/utilServices.tsx index 2ad8699f..2936fe86 100644 --- a/web/src/Utils/utilServices.tsx +++ b/web/src/Utils/utilServices.tsx @@ -206,7 +206,9 @@ export const subtractTwoArrays = ( multiplier?: number ): number[] => { const processedMultiplier = multiplier ?? 1; - return array1.map((value, index) => (value - array2[index]) * processedMultiplier); + return array1.map((value, index) => + parseFloat(((value - array2[index]) * processedMultiplier).toFixed(2)) + ); }; export const calculateArraySum = (array: number[]) => { @@ -214,7 +216,7 @@ export const calculateArraySum = (array: number[]) => { for (let index = 0; index <= array.length; index++) { arrSum += array[index] || 0; } - return arrSum; + return parseFloat(arrSum.toFixed(2)); }; export const isGasFlowCheck = (type: ActionType | undefined): boolean => { From ebfd8470297810ae08df3556a1d7f3aab366cbb8 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:00:44 +0530 Subject: [PATCH 33/66] Negative Value allowed in MTG Entries --- web/src/Components/Inventory/baselineForm.tsx | 1 - web/src/Components/Inventory/emissionForm.tsx | 4 ---- web/src/Components/Inventory/projectionForm.tsx | 1 - 3 files changed, 6 deletions(-) diff --git a/web/src/Components/Inventory/baselineForm.tsx b/web/src/Components/Inventory/baselineForm.tsx index 67e23a55..d4af3ad5 100644 --- a/web/src/Components/Inventory/baselineForm.tsx +++ b/web/src/Components/Inventory/baselineForm.tsx @@ -217,7 +217,6 @@ export const BaselineForm: React.FC = ({ index, projectionType }) => { enteredValue ? parseToTwoDecimals(enteredValue) : 0 ); }} - min={value === GrowthRateProperties.GR ? undefined : 0} decimalSeparator="." controls={false} disabled={userInfoState?.userRole !== Role.Root} diff --git a/web/src/Components/Inventory/emissionForm.tsx b/web/src/Components/Inventory/emissionForm.tsx index bfa49d74..cbefd4c0 100644 --- a/web/src/Components/Inventory/emissionForm.tsx +++ b/web/src/Components/Inventory/emissionForm.tsx @@ -565,7 +565,6 @@ export const EmissionForm: React.FC = ({ unit ) } - min={0} decimalSeparator="." controls={false} className="input-emission" @@ -630,7 +629,6 @@ export const EmissionForm: React.FC = ({ unit ) } - min={0} decimalSeparator="." controls={false} className="input-emission" @@ -658,7 +656,6 @@ export const EmissionForm: React.FC = ({ onChange={(value) => setIndividualEntry(value ?? undefined, 'eqWithout', null, null, unit) } - min={0} decimalSeparator="." controls={false} className="input-emission" @@ -678,7 +675,6 @@ export const EmissionForm: React.FC = ({ onChange={(value) => setIndividualEntry(value ?? undefined, 'eqWith', null, null, unit) } - min={0} decimalSeparator="." controls={false} className="input-emission" diff --git a/web/src/Components/Inventory/projectionForm.tsx b/web/src/Components/Inventory/projectionForm.tsx index da1d5a45..abff2298 100644 --- a/web/src/Components/Inventory/projectionForm.tsx +++ b/web/src/Components/Inventory/projectionForm.tsx @@ -355,7 +355,6 @@ export const ProjectionForm: React.FC = ({ index, projectionType }) => { enteredValue ? parseToTwoDecimals(enteredValue) : 0 ); }} - min={0} decimalSeparator="." controls={false} className={ From 420b3bb635779f5b3fe67fcc8fb88d32fdb60396 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:07:04 +0530 Subject: [PATCH 34/66] Removed Negative validation checks in Emissions --- backend/services/src/emission/emission.service.ts | 3 --- backend/services/src/projection/projection.service.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/services/src/emission/emission.service.ts b/backend/services/src/emission/emission.service.ts index a20326c5..74a57b88 100644 --- a/backend/services/src/emission/emission.service.ts +++ b/backend/services/src/emission/emission.service.ts @@ -190,9 +190,6 @@ export class GhgEmissionsService { if (typeof emissionData[key] === 'string') { throw new HttpException(this.helperService.formatReqMessagesString("ghgInventory.invalidDataType", []), HttpStatus.BAD_REQUEST); } - if (typeof emissionData[key] === 'number' && emissionData[key] < 0) { - throw new HttpException(this.helperService.formatReqMessagesString("ghgInventory.negativeValuesNotAllowed", []), HttpStatus.BAD_REQUEST); - } } } diff --git a/backend/services/src/projection/projection.service.ts b/backend/services/src/projection/projection.service.ts index 0e716ef1..9f363318 100644 --- a/backend/services/src/projection/projection.service.ts +++ b/backend/services/src/projection/projection.service.ts @@ -31,7 +31,7 @@ export class GhgProjectionService { const projection: ProjectionEntity = this.toProjection(projectionDto); - let savedProjection; + let savedProjection: any; const result = await this.getActualProjection(projection.projectionType, user); if (result) { From 3d94a2fce8013c46c709a3e975015cc4df6db350 Mon Sep 17 00:00:00 2001 From: tharindugayanga Date: Mon, 28 Oct 2024 08:52:32 +0530 Subject: [PATCH 35/66] Removing not needed type property --- .../services/src/activity/activity.service.ts | 14 +++--- .../services/src/entities/activity.entity.ts | 4 +- .../services/src/entities/programme.entity.ts | 4 +- .../services/src/entities/project.entity.ts | 4 +- .../services/src/entities/support.entity.ts | 4 +- .../src/programme/programme.service.ts | 6 +-- .../services/src/project/project.service.ts | 4 +- .../services/src/support/support.service.ts | 4 +- .../services/src/util/linkUnlink.service.ts | 48 +++++++++---------- 9 files changed, 46 insertions(+), 46 deletions(-) diff --git a/backend/services/src/activity/activity.service.ts b/backend/services/src/activity/activity.service.ts index 1456adef..53fb169f 100644 --- a/backend/services/src/activity/activity.service.ts +++ b/backend/services/src/activity/activity.service.ts @@ -79,7 +79,7 @@ export class ActivityService { action = await this.isActionValid(activityDto.parentId, user); activity.path = `${activityDto.parentId}._._`; activity.sector = action.sector; - activity.type = action.type; + // activity.type = action.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.ACTION, activityDto.parentId, user.id, activity.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_ACTION, EntityType.ACTIVITY, activity.activityId, user.id, activityDto.parentId); @@ -91,7 +91,7 @@ export class ActivityService { action = programme.action; activity.path = programme.path && programme.path.trim() !== '' ? `${programme.path}.${activityDto.parentId}._` : `_.${activityDto.parentId}._`; activity.sector = programme.sector; - activity.type = programme.type; + // activity.type = programme.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.PROGRAMME, activityDto.parentId, user.id, activity.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROGRAMME, EntityType.ACTIVITY, activity.activityId, user.id, activityDto.parentId); @@ -104,7 +104,7 @@ export class ActivityService { action = programme?.action; activity.path = project.path && project.path.trim() !== '' ? `${project.path}.${activityDto.parentId}` : `_._.${activityDto.parentId}`; activity.sector = project.sector; - activity.type = project.type; + // activity.type = project.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.PROJECT, activityDto.parentId, user.id, activity.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROJECT, EntityType.ACTIVITY, activity.activityId, user.id, activityDto.parentId); @@ -461,7 +461,7 @@ export class ActivityService { activityUpdate.parentId = null; activityUpdate.parentType = null; activityUpdate.sector = null; - activityUpdate.type = null; + // activityUpdate.type = null; activityUpdate.path = '_._._'; } } @@ -503,7 +503,7 @@ export class ActivityService { activityUpdate.path = `${activityUpdateDto.parentId}._._`; activityUpdate.sector = action.sector; - activityUpdate.type = action.type; + // activityUpdate.type = action.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.ACTION, activityUpdateDto.parentId, user.id, activityUpdate.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_ACTION, EntityType.ACTIVITY, activityUpdate.activityId, user.id, activityUpdateDto.parentId); break; @@ -542,7 +542,7 @@ export class ActivityService { activityUpdate.path = programme.path && programme.path.trim() !== '' ? `${programme.path}.${activityUpdateDto.parentId}._` : `_.${activityUpdateDto.parentId}._`; activityUpdate.sector = programme.sector; - activityUpdate.type = programme.type; + // activityUpdate.type = programme.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.PROGRAMME, activityUpdateDto.parentId, user.id, activityUpdate.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROGRAMME, EntityType.ACTIVITY, activityUpdate.activityId, user.id, activityUpdateDto.parentId); break; @@ -595,7 +595,7 @@ export class ActivityService { activityUpdate.path = project.path && project.path.trim() !== '' ? `${project.path}.${activityUpdateDto.parentId}` : `_._.${activityUpdateDto.parentId}`; activityUpdate.sector = project.sector; - activityUpdate.type = project.type; + // activityUpdate.type = project.type; this.addEventLogEntry(eventLog, LogEventType.ACTIVITY_LINKED, EntityType.PROJECT, activityUpdateDto.parentId, user.id, activityUpdate.activityId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROJECT, EntityType.ACTIVITY, activityUpdate.activityId, user.id, activityUpdateDto.parentId); break; diff --git a/backend/services/src/entities/activity.entity.ts b/backend/services/src/entities/activity.entity.ts index aa64e0c3..7d72422f 100644 --- a/backend/services/src/entities/activity.entity.ts +++ b/backend/services/src/entities/activity.entity.ts @@ -108,8 +108,8 @@ export class ActivityEntity implements EntitySubject { @Column({ type: "enum", enum: Sector, nullable: true }) sector: Sector; - @Column({ type: "enum", enum: ActionType,nullable: false }) - type: string; + // @Column({ type: "enum", enum: ActionType,nullable: false }) + // type: string; @Column({ type: "ltree" }) path: string; diff --git a/backend/services/src/entities/programme.entity.ts b/backend/services/src/entities/programme.entity.ts index aac53b95..17abf641 100644 --- a/backend/services/src/entities/programme.entity.ts +++ b/backend/services/src/entities/programme.entity.ts @@ -35,8 +35,8 @@ export class ProgrammeEntity { @Column({ type: "enum", enum: Sector, nullable: true }) sector: Sector; - @Column({ type: "enum", enum: ActionType,nullable: false }) - type: string; + // @Column({ type: "enum", enum: ActionType,nullable: false }) + // type: string; @Column("varchar", { array: true, nullable: false }) affectedSubSector: SubSector[]; diff --git a/backend/services/src/entities/project.entity.ts b/backend/services/src/entities/project.entity.ts index 9ec14c66..4f8b8864 100644 --- a/backend/services/src/entities/project.entity.ts +++ b/backend/services/src/entities/project.entity.ts @@ -47,8 +47,8 @@ export class ProjectEntity { @Column({ type: "enum", enum: Sector, nullable: true }) sector: Sector; - @Column({ type: "enum", enum: ActionType,nullable: false }) - type: string; + // @Column({ type: "enum", enum: ActionType,nullable: false }) + // type: string; @Column({ type: "ltree" }) path: string; diff --git a/backend/services/src/entities/support.entity.ts b/backend/services/src/entities/support.entity.ts index 7b9e991e..868cbceb 100644 --- a/backend/services/src/entities/support.entity.ts +++ b/backend/services/src/entities/support.entity.ts @@ -59,8 +59,8 @@ export class SupportEntity { @Column({ nullable: true }) sector: Sector; - @Column({ type: "enum", enum: ActionType,nullable: false }) - type: string; + // @Column({ type: "enum", enum: ActionType,nullable: false }) + // type: string; @ManyToOne(() => ActivityEntity, (activity) => activity.support, { nullable: false, diff --git a/backend/services/src/programme/programme.service.ts b/backend/services/src/programme/programme.service.ts index 8d3912fc..2ac0de79 100644 --- a/backend/services/src/programme/programme.service.ts +++ b/backend/services/src/programme/programme.service.ts @@ -128,7 +128,7 @@ export class ProgrammeService { programme.action = action; programme.path = programmeDto.actionId; programme.sector = action.sector; - programme.type = action.type; + // programme.type = action.type; this.addEventLogEntry(eventLog, LogEventType.PROGRAMME_LINKED, EntityType.ACTION, action.actionId, user.id, programme.programmeId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_ACTION, EntityType.PROGRAMME, programme.programmeId, user.id, action.actionId); @@ -320,7 +320,7 @@ export class ProgrammeService { programmeUpdate.action = currentProgramme.action; programmeUpdate.path = currentProgramme.path; programmeUpdate.sector = currentProgramme.sector; - programmeUpdate.type = currentProgramme.type; + // programmeUpdate.type = currentProgramme.type; programmeUpdate.projects = currentProgramme.projects; programmeUpdate.activities = currentProgramme.activities; @@ -1023,7 +1023,7 @@ export class ProgrammeService { programmeViewDto.objectives = programme.objective; programmeViewDto.instrumentType = programme.action?.instrumentType; programmeViewDto.sector = programme.sector; - programmeViewDto.type = programme.type; + // programmeViewDto.type = programme.type; programmeViewDto.affectedSubSector = programme.affectedSubSector; programmeViewDto.programmeStatus = programme.programmeStatus; programmeViewDto.recipientEntity = recipientEntity; diff --git a/backend/services/src/project/project.service.ts b/backend/services/src/project/project.service.ts index 2b3824c1..f3bc83fe 100644 --- a/backend/services/src/project/project.service.ts +++ b/backend/services/src/project/project.service.ts @@ -98,7 +98,7 @@ export class ProjectService { project.programme = programme; project.path = programme.path && programme.path.trim() !== '' ? `${programme.path}.${programme.programmeId}` : `_.${programme.programmeId}`; project.sector = programme.sector; - project.type = programme.type; + // project.type = programme.type; this.addEventLogEntry(eventLog, LogEventType.PROJECT_LINKED, EntityType.PROGRAMME, programme.programmeId, user.id, project.projectId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_PROGRAMME, EntityType.PROJECT, project.projectId, user.id, programme.programmeId); } @@ -371,7 +371,7 @@ export class ProjectService { projectUpdate.path = currentProject.path; projectUpdate.programme = currentProject.programme; projectUpdate.sector = currentProject.sector; - projectUpdate.type = currentProject.type; + // projectUpdate.type = currentProject.type; projectUpdate.activities = currentProject.activities; projectUpdate.expectedTimeFrame = projectUpdateDto.endYear - projectUpdateDto.startYear; diff --git a/backend/services/src/support/support.service.ts b/backend/services/src/support/support.service.ts index a1afb604..dc2f9d90 100644 --- a/backend/services/src/support/support.service.ts +++ b/backend/services/src/support/support.service.ts @@ -72,7 +72,7 @@ export class SupportService { support.requiredAmountDomestic = this.helperService.roundToTwoDecimals(support.requiredAmount / support.exchangeRate); support.receivedAmountDomestic = this.helperService.roundToTwoDecimals(support.receivedAmount / support.exchangeRate); support.sector = activity.sector; - support.type = activity.type; + // support.type = activity.type; support.activity = activity; this.addEventLogEntry(eventLog, LogEventType.SUPPORT_CREATED, EntityType.SUPPORT, support.supportId, user.id, supportDto); @@ -295,7 +295,7 @@ export class SupportService { currentSupport.activity = activity; currentSupport.sector = activity.sector; - currentSupport.type = activity.type; + // currentSupport.type = activity.type; if (activity.validated) { activity.validated = false; diff --git a/backend/services/src/util/linkUnlink.service.ts b/backend/services/src/util/linkUnlink.service.ts index 11030a25..f41e7450 100644 --- a/backend/services/src/util/linkUnlink.service.ts +++ b/backend/services/src/util/linkUnlink.service.ts @@ -54,7 +54,7 @@ export class LinkUnlinkService { programme.action = action; programme.path = action.actionId; programme.sector = action.sector; - programme.type = action.type; + // programme.type = action.type; programmeId = programme.programmeId; @@ -84,7 +84,7 @@ export class LinkUnlinkService { // update each activity's path that are directly linked to the programme for (const activity of programme.activities) { activity.sector = action.sector; - activity.type = action.type; + // activity.type = action.type; activity.path = this.addActionToActivityPath(activity.path, action.actionId) // unvalidate the activity linked to programme @@ -104,7 +104,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { for (const support of activity.support) { support.sector = action.sector; - support.type = action.type; + // support.type = action.type; // unvalidate the activity linked to programme if (support.validated) { @@ -124,7 +124,7 @@ export class LinkUnlinkService { for (const project of programme.projects) { // update project's path project.sector = action.sector; - project.type = action.type; + // project.type = action.type; project.path = this.addActionToProjectPath(project.path, action.actionId); // unvalidate the linked projects @@ -148,7 +148,7 @@ export class LinkUnlinkService { for (const activity of project.activities) { activity.sector = action.sector; - activity.type = action.type; + // activity.type = action.type; activity.path = this.addActionToActivityPath(activity.path, action.actionId); // unvalidate the activity linked to project @@ -169,7 +169,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { for (const support of activity.support) { support.sector = action.sector; - support.type = action.type; + // support.type = action.type; // unvalidate the activity linked to programme if (support.validated) { @@ -229,7 +229,7 @@ export class LinkUnlinkService { programme.action = null; programme.path = ""; programme.sector = null; - programme.type = null; + // programme.type = null; logs.push(this.buildLogEntity(LogEventType.UNLINKED_FROM_ACTION, EntityType.PROGRAMME, programme.programmeId, user.id, payload)) @@ -267,7 +267,7 @@ export class LinkUnlinkService { const parts = activity.path.split("."); activity.path = ["_", parts[1], parts[2]].join("."); activity.sector = null; - activity.type = null; + // activity.type = null; // unvalidate the activity linked to programme if (activity.validated) { @@ -286,7 +286,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { for (const support of activity.support) { support.sector = action.sector; - support.type = action.type; + // support.type = action.type; // unvalidate the activity linked to programme if (support.validated) { @@ -311,7 +311,7 @@ export class LinkUnlinkService { const parts = project.path.split("."); project.path = ["_", parts[1]].join("."); project.sector = null; - project.type = null; + // project.type = null; // unvalidate the activity linked to programme if (project.validated) { @@ -333,7 +333,7 @@ export class LinkUnlinkService { const parts = activity.path.split("."); activity.path = ["_", parts[1], parts[2]].join("."); activity.sector = null; - activity.type = null; + // activity.type = null; // unvalidate the activity linked to project if (activity.validated) { @@ -352,7 +352,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { for (const support of activity.support) { support.sector = action.sector; - support.type = action.type; + // support.type = action.type; // unvalidate the activity linked to programme if (support.validated) { @@ -401,7 +401,7 @@ export class LinkUnlinkService { project.programme = programme; project.path = this.addProgrammeToProjectPath(project.path, programme.programmeId, programme.path); project.sector = programme.sector; - project.type = programme.type; + // project.type = programme.type; // unvalidate project if (project.validated) { @@ -428,7 +428,7 @@ export class LinkUnlinkService { activity.support.forEach((support) => { support.sector = programme.sector; - support.type = programme.type; + // support.type = programme.type; // unvalidate support if (support.validated) { support.validated = false; @@ -445,7 +445,7 @@ export class LinkUnlinkService { } activity.path = this.addProgrammeToActivityPath(activity.path, programme.programmeId, programme.path); activity.sector = programme.sector; - activity.type = programme.type; + // activity.type = programme.type; // unvalidate activity if (activity.validated) { @@ -545,7 +545,7 @@ export class LinkUnlinkService { project.programme = null; project.path = `_._`; project.sector = null; - project.type = null; + // project.type = null; // unvalidate project if (project.validated) { @@ -572,7 +572,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { activity.support.forEach((support) => { support.sector = null; - support.type = null; + // support.type = null; // unvalidate project if (support.validated) { @@ -590,7 +590,7 @@ export class LinkUnlinkService { } activity.path = `_._.${project.projectId}` activity.sector = null; - activity.type = null; + // activity.type = null; // unvalidate project if (activity.validated) { @@ -673,7 +673,7 @@ export class LinkUnlinkService { logEventType = LogEventType.LINKED_TO_ACTION; entityType = EntityType.ACTION; activity.sector = parentEntity?.sector; - activity.type = parentEntity?.type; + // activity.type = parentEntity?.type; rootNodeType = EntityType.ACTION; rootId = linkActivitiesDto.parentId; @@ -685,7 +685,7 @@ export class LinkUnlinkService { logEventType = LogEventType.LINKED_TO_PROGRAMME; entityType = EntityType.PROGRAMME; activity.sector = parentEntity?.sector; - activity.type = parentEntity?.type; + // activity.type = parentEntity?.type; rootNodeType = (parentEntity.path) ? EntityType.ACTION : EntityType.PROGRAMME; rootId = (parentEntity.path) ? parentEntity.path : linkActivitiesDto.parentId; @@ -696,7 +696,7 @@ export class LinkUnlinkService { logEventType = LogEventType.LINKED_TO_PROJECT; entityType = EntityType.PROJECT; activity.sector = parentEntity?.sector; - activity.type = parentEntity?.type; + // activity.type = parentEntity?.type; const parts = parentEntity.path?.split("."); if (parts && parts.length > 0) { @@ -738,7 +738,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { activity.support.forEach((support) => { support.sector = linkedActivity.sector; - support.type = linkedActivity.type; + // support.type = linkedActivity.type; if (support.validated) { support.validated = false; @@ -841,7 +841,7 @@ export class LinkUnlinkService { activity.parentType = null; activity.path = '_._._'; activity.sector = null; - activity.type = null; + // activity.type = null; activity.validated = false; const unlinkedActivity = await em.save(activity); @@ -851,7 +851,7 @@ export class LinkUnlinkService { if (activity.support && activity.support.length > 0) { activity.support.forEach((support) => { support.sector = null; - support.type = null; + // support.type = null; support.validated = false; supports.push(support); }); From c1b22b94a8dbedc975c980e50cda983fe85c9cde Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 09:59:51 +0530 Subject: [PATCH 36/66] Parent change restriction added to PRG, PRJ and ACTIVITY --- web/src/Pages/Activities/ActivityForm/activityForm.tsx | 8 ++++---- web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx | 4 ++-- web/src/Pages/Projects/ProjectForm/projectForm.tsx | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 1c07125b..aa454020 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -1221,13 +1221,13 @@ const ActivityForm: React.FC = ({ method }) => { {t('parentTypeTitle')}} name="parentType" - rules={[validation.required]} + rules={method !== 'create' ? undefined : [validation.required]} > diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index feba0383..0485bc5c 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -803,13 +803,13 @@ const ProgrammeForm: React.FC = ({ method }) => { {t('selectActionHeader')}} name="actionId" - rules={[validation.required]} + rules={method !== 'create' ? undefined : [validation.required]} > { setProjectConnectedProgramme(value); From 5259144b4450cd638bd0bdf1b0cb86f0644a3c02 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:58:19 +0530 Subject: [PATCH 37/66] Label Change locale update --- web/src/locales/i18n/configuration/en.json | 6 ++-- web/src/locales/i18n/configuration/fr.json | 6 ++-- web/src/locales/i18n/configuration/sin.json | 6 ++-- web/src/locales/i18n/emission/en.json | 12 ++++---- web/src/locales/i18n/emission/fr.json | 14 ++++----- web/src/locales/i18n/emission/sin.json | 12 ++++---- web/src/locales/i18n/faq/en.json | 8 +++--- web/src/locales/i18n/faq/fr.json | 22 +++++++------- web/src/locales/i18n/faq/sin.json | 32 ++++++++++----------- 9 files changed, 59 insertions(+), 59 deletions(-) diff --git a/web/src/locales/i18n/configuration/en.json b/web/src/locales/i18n/configuration/en.json index 2d55d03e..6c9b0592 100644 --- a/web/src/locales/i18n/configuration/en.json +++ b/web/src/locales/i18n/configuration/en.json @@ -8,9 +8,9 @@ "growthRate": "Growth Rate (%)", "baselineYear": "Baseline Year", - "co2": "Baseline CO2", - "ch4": "Baseline CH4", - "n2o": "Baseline N2O", + "co2": "Baseline ktCO2", + "ch4": "Baseline ktCH4", + "n2o": "Baseline ktN2O", "gwp_co2": "CO2", "gwp_ch4": "CH4", diff --git a/web/src/locales/i18n/configuration/fr.json b/web/src/locales/i18n/configuration/fr.json index 13087626..9e0fd974 100644 --- a/web/src/locales/i18n/configuration/fr.json +++ b/web/src/locales/i18n/configuration/fr.json @@ -8,9 +8,9 @@ "growthRate": "Taux de Croissance (%)", "baselineYear": "Année de Référence", - "co2": "Référence CO2", - "ch4": "Référence CH4", - "n2o": "Référence N2O", + "co2": "Référence ktCO2", + "ch4": "Référence ktCH4", + "n2o": "Référence ktN2O", "gwp_co2": "CO2", "gwp_ch4": "CH4", diff --git a/web/src/locales/i18n/configuration/sin.json b/web/src/locales/i18n/configuration/sin.json index 486335f2..4d7020b0 100644 --- a/web/src/locales/i18n/configuration/sin.json +++ b/web/src/locales/i18n/configuration/sin.json @@ -8,9 +8,9 @@ "growthRate": "වර්ධන අනුපාතය (%)", "baselineYear": "මූලික වසර", - "co2": "මූලික CO2", - "ch4": "මූලික CH4", - "n2o": "මූලික N2O", + "co2": "මූලික ktCO2", + "ch4": "මූලික ktCH4", + "n2o": "මූලික ktN2O", "gwp_co2": "CO2", "gwp_ch4": "CH4", diff --git a/web/src/locales/i18n/emission/en.json b/web/src/locales/i18n/emission/en.json index e30ed50a..797c86b6 100644 --- a/web/src/locales/i18n/emission/en.json +++ b/web/src/locales/i18n/emission/en.json @@ -5,13 +5,13 @@ "totalRowHeader" : "Total National Emissions and Removals", - "eqWithHeader" : "Total CO2 equivalent emissions with land use, land-use change and forestry", - "eqWithoutHeader" : "Total CO2 equivalent emissions without land use, land-use change and forestry", + "eqWithHeader" : "Total ktCO2 equivalent emissions with land", + "eqWithoutHeader" : "Total ktCO2 equivalent emissions without land", - "co2" : "tCO2", - "ch4" : "tCH4", - "n2o" : "tN2O", - "co2eq" : "tCO2-eq", + "co2" : "ktCO2", + "ch4" : "ktCH4", + "n2o" : "ktN2O", + "co2eq" : "ktCO2-eq", "1_title": "Energy", "2_title": "Industrial Processes & Product Use", diff --git a/web/src/locales/i18n/emission/fr.json b/web/src/locales/i18n/emission/fr.json index 93e45802..2977eeeb 100644 --- a/web/src/locales/i18n/emission/fr.json +++ b/web/src/locales/i18n/emission/fr.json @@ -5,13 +5,13 @@ "totalRowHeader": "Total des Émissions et Captures Nationales", - "eqWithHeader": "Émissions totales équivalentes en CO2 avec utilisation des terres, changement d'affectation des terres et foresterie", - "eqWithoutHeader": "Émissions totales équivalentes en CO2 sans utilisation des terres, changement d'affectation des terres et foresterie", - - "co2": "tCO2", - "ch4": "tCH4", - "n2o": "tN2O", - "co2eq": "tCO2-éq", + "eqWithHeader": "Émissions totales en ktCO2 équivalent avec terres", + "eqWithoutHeader": "Émissions totales en ktCO2 équivalent sans terres", + + "co2": "ktCO2", + "ch4": "ktCH4", + "n2o": "ktN2O", + "co2eq": "ktCO2-éq", "1_title": "Énergie", "2_title": "Processus Industriels et Utilisation des Produits", diff --git a/web/src/locales/i18n/emission/sin.json b/web/src/locales/i18n/emission/sin.json index 40560674..1d354d34 100644 --- a/web/src/locales/i18n/emission/sin.json +++ b/web/src/locales/i18n/emission/sin.json @@ -5,13 +5,13 @@ "totalRowHeader": "ජාතික විමෝචන හා හරස් කිරීම් සමස්ත", - "eqWithHeader": "ඉඩම් භාවිතය, ඉඩම් භාවිතයේ වෙනස් කිරීම හා වනාන්තර සමග CO2 සමතලා විමෝචන", - "eqWithoutHeader": "ඉඩම් භාවිතය, ඉඩම් භාවිතයේ වෙනස් කිරීම හා වනාන්තර රහිත CO2 සමතලා විමෝචන", + "eqWithHeader": "භූමිය සමඟ මුළු ktCO2 සමාන විමෝචන", + "eqWithoutHeader": "භූමිය තොරව මුළු ktCO2 සමාන විමෝචන", - "co2": "tCO2", - "ch4": "tCH4", - "n2o": "tN2O", - "co2eq": "tCO2-eq", + "co2": "ktCO2", + "ch4": "ktCH4", + "n2o": "ktN2O", + "co2eq": "ktCO2-eq", "1_title": "බලශක්ති", "2_title": "කාර්මික ක්‍රියාවලිය හා නිෂ්පාදන භාවිතය", diff --git a/web/src/locales/i18n/faq/en.json b/web/src/locales/i18n/faq/en.json index 3ff5dd18..bd1925ae 100644 --- a/web/src/locales/i18n/faq/en.json +++ b/web/src/locales/i18n/faq/en.json @@ -9,9 +9,9 @@ "answer2": "Answer 2", "answer3": "Answer 3", "answer4": "Answer 4", - "docsAndTemplates": "Documents and Templates", + "docsAndTemplates": "Other Documents and Templates", "trainingVideos": "Training Videos", - "ghgResourcesSection": "GHG Inventory Resources", - "ghgSavedSuccessfully": "GHG Resources Updated Successfully", - "templateSavedSuccessfully": "Documents and Templates Updated Successfully" + "ghgResourcesSection": "Digital Transparency System Resources", + "ghgSavedSuccessfully": "Digital Transparency System Resources Updated Successfully", + "templateSavedSuccessfully": "Other Documents and Templates Updated Successfully" } \ No newline at end of file diff --git a/web/src/locales/i18n/faq/fr.json b/web/src/locales/i18n/faq/fr.json index cb19a120..f1c56b0d 100644 --- a/web/src/locales/i18n/faq/fr.json +++ b/web/src/locales/i18n/faq/fr.json @@ -1,17 +1,17 @@ { "faqMenu": "Menu FAQ", - "freqAskedQ": "Questions fréquemment posées", - "question1": "Qu'est-ce que le Système de transparence des CDN et quel est son but ?", - "question2": "Pourquoi en tant que consommateur devrais-je utiliser le Système de transparence des CDN ?", - "question3": "Pourquoi mon organisation devrait-elle utiliser le Système de transparence des CDN ?", - "question4": "Comment enregistrer mon organisation sur le Système de transparence des CDN ?", + "freqAskedQ": "Questions Fréquemment Posées", + "question1": "Qu'est-ce que le Système de Transparence NDC et quel est son objectif?", + "question2": "Pourquoi devrais-je, en tant que consommateur, utiliser le Système de Transparence NDC?", + "question3": "Pourquoi mon organisation devrait-elle utiliser le Système de Transparence NDC?", + "question4": "Comment enregistrer mon organisation sur le Système de Transparence NDC?", "answer1": "Réponse 1", "answer2": "Réponse 2", "answer3": "Réponse 3", "answer4": "Réponse 4", - "docsAndTemplates": "Documents et modèles", - "trainingVideos": "Vidéos de formation", - "ghgResourcesSection": "Ressources de l'inventaire des GES", - "ghgSavedSuccessfully": "Ressources des GES mises à jour avec succès", - "templateSavedSuccessfully": "Documents et modèles mis à jour avec succès" -} \ No newline at end of file + "docsAndTemplates": "Autres Documents et Modèles", + "trainingVideos": "Vidéos de Formation", + "ghgResourcesSection": "Ressources du Système de Transparence Numérique", + "ghgSavedSuccessfully": "Ressources du Système de Transparence Numérique mises à jour avec succès", + "templateSavedSuccessfully": "Autres Documents et Modèles mis à jour avec succès" +} diff --git a/web/src/locales/i18n/faq/sin.json b/web/src/locales/i18n/faq/sin.json index 390ec5df..bd13001d 100644 --- a/web/src/locales/i18n/faq/sin.json +++ b/web/src/locales/i18n/faq/sin.json @@ -1,17 +1,17 @@ { - "faqMenu": "FAQ මෙනුව", - "freqAskedQ": "සාමාන්‍යයෙන් විචල්‍ය කල ප‍්‍රශ්න", - "question1": "NDC ස්ථාපිත පද්ධතිය කුලක්කාර භාවිත කරයි පහත දැක්වෙන්නේ ඇය හා ඇයි?", - "question2": "හැම විදියේම මා සහායි NDC ප්‍රගති පද්ධතිය භාවිතා කළ යුතුද?", - "question3": "මමේ සංවිධානයෙන් නිර්දේශය කිරීමේ තරගය බලන්නේ කෙසේද?", - "question4": "මම ඇය කිරීමට මගේ සංවිධානය ලියාපදිංචි කිරීමේදී බලය යුතුද?", - "answer1": "විස්තර 1", - "answer2": "විස්තර 2", - "answer3": "විස්තර 3", - "answer4": "විස්තර 4", - "docsAndTemplates": "ලියාපදිංචි දායක සහ ආකෘති", - "trainingVideos": "ප්‍රාදේශීය වීඩියෝ", - "ghgResourcesSection": "ජීඑච්ජී ඉන්වෙන්ටරි සම්පත්", - "ghgSavedSuccessfully": "ජීඑච්ජී සම්පත් සාර්ථකව යාවත්කාලීන කර ඇත", - "templateSavedSuccessfully": "ලේඛන සහ සැකිලි සාර්ථකව යාවත්කාලීන කර ඇත" -} \ No newline at end of file + "faqMenu": "නිතර අසන ප්‍රශ්න මෙනුව", + "freqAskedQ": "නිතර අසන ප්‍රශ්න", + "question1": "NDC පාරද්‍රශ්‍යතාව පද්ධතිය යනු කුමක්ද සහ එහි අරමුණ කුමක්ද?", + "question2": "පාරිභෝගිකයා ලෙස මම NDC පාරද්‍රශ්‍යතාව පද්ධතිය භාවිතා කළ යුත්තේ ඇයි?", + "question3": "මගේ සංවිධානය NDC පාරද්‍රශ්‍යතාව පද්ධතිය භාවිතා කළ යුත්තේ ඇයි?", + "question4": "මගේ සංවිධානය NDC පාරද්‍රශ්‍යතාවය සඳහා ලියාපදිංචි කරන්නේ කෙසේද?", + "answer1": "පිළිතුර 1", + "answer2": "පිළිතුර 2", + "answer3": "පිළිතුර 3", + "answer4": "පිළිතුර 4", + "docsAndTemplates": "වෙනත් ලේඛන සහ සැකිලි", + "trainingVideos": "පුහුණු වීඩියෝ", + "ghgResourcesSection": "ඩිජිටල් පාරද්‍රශ්‍යතාව පද්ධති සම්පත්", + "ghgSavedSuccessfully": "ඩිජිටල් පාරද්‍රශ්‍යතාව පද්ධති සම්පත් සාර්ථකව යාවත්කාලීන කරන ලදී", + "templateSavedSuccessfully": "වෙනත් ලේඛන සහ සැකිලි සාර්ථකව යාවත්කාලීන කරන ලදී" +} From 180060f65b0f37ca507d041e5c7d2097ee4c5d0f Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:08:26 +0530 Subject: [PATCH 38/66] Tooltip added for large numbers in emission form --- web/src/Components/NumberChip/numberChip.tsx | 17 +++++++++++------ web/src/Utils/utilServices.tsx | 11 +++++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/web/src/Components/NumberChip/numberChip.tsx b/web/src/Components/NumberChip/numberChip.tsx index 6b60dd6a..c8e27c05 100644 --- a/web/src/Components/NumberChip/numberChip.tsx +++ b/web/src/Components/NumberChip/numberChip.tsx @@ -1,5 +1,5 @@ import './numberChip.scss'; -import { Tag } from 'antd'; +import { Tag, Tooltip } from 'antd'; import { EmissionUnits } from '../../Enums/emission.enum'; import { convertToMillions } from '../../Utils/utilServices'; @@ -9,13 +9,18 @@ interface Props { } const NumberChip: React.FC = ({ value, valueType }) => { + // Converting to Million or Billion format to prevent overflow + const { processedNumber, isConverted } = convertToMillions(value); + return (
- {valueType ? ( - {convertToMillions(value)} - ) : ( - {convertToMillions(value)} - )} + + {valueType ? ( + {processedNumber} + ) : ( + {processedNumber} + )} +
); }; diff --git a/web/src/Utils/utilServices.tsx b/web/src/Utils/utilServices.tsx index 2936fe86..55c98497 100644 --- a/web/src/Utils/utilServices.tsx +++ b/web/src/Utils/utilServices.tsx @@ -120,14 +120,17 @@ export const getRounded = (num: number | string): number => { export const convertToMillions = (value: number) => { const roundedNumber = getRounded(value); - let numberInMills = roundedNumber.toString(); + let processedNumber = roundedNumber.toString(); + let isConverted = false; if (roundedNumber > 1000000000) { - numberInMills = `${customRound(roundedNumber / 1000000000)} billion`; + processedNumber = `${customRound(roundedNumber / 1000000000)} billion`; + isConverted = true; } else if (roundedNumber > 1000000) { - numberInMills = `${customRound(roundedNumber / 1000000)} million`; + processedNumber = `${customRound(roundedNumber / 1000000)} million`; + isConverted = true; } - return numberInMills; + return { processedNumber, isConverted }; }; export const getCollapseIcon = (isActive: boolean, clicked?: any) => { From 1dfb2d2e8383e59793b3e91da19e5ea47d15edb8 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:10:18 +0530 Subject: [PATCH 39/66] Removed million transform --- web/src/Components/NumberChip/numberChip.tsx | 4 ++-- web/src/Utils/utilServices.tsx | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/web/src/Components/NumberChip/numberChip.tsx b/web/src/Components/NumberChip/numberChip.tsx index c8e27c05..d2eba7b9 100644 --- a/web/src/Components/NumberChip/numberChip.tsx +++ b/web/src/Components/NumberChip/numberChip.tsx @@ -1,7 +1,7 @@ import './numberChip.scss'; import { Tag, Tooltip } from 'antd'; import { EmissionUnits } from '../../Enums/emission.enum'; -import { convertToMillions } from '../../Utils/utilServices'; +import { convertToBillions } from '../../Utils/utilServices'; interface Props { value: number; @@ -10,7 +10,7 @@ interface Props { const NumberChip: React.FC = ({ value, valueType }) => { // Converting to Million or Billion format to prevent overflow - const { processedNumber, isConverted } = convertToMillions(value); + const { processedNumber, isConverted } = convertToBillions(value); return (
diff --git a/web/src/Utils/utilServices.tsx b/web/src/Utils/utilServices.tsx index 55c98497..66acd5e7 100644 --- a/web/src/Utils/utilServices.tsx +++ b/web/src/Utils/utilServices.tsx @@ -118,16 +118,13 @@ export const getRounded = (num: number | string): number => { } }; -export const convertToMillions = (value: number) => { +export const convertToBillions = (value: number) => { const roundedNumber = getRounded(value); let processedNumber = roundedNumber.toString(); let isConverted = false; if (roundedNumber > 1000000000) { processedNumber = `${customRound(roundedNumber / 1000000000)} billion`; isConverted = true; - } else if (roundedNumber > 1000000) { - processedNumber = `${customRound(roundedNumber / 1000000)} million`; - isConverted = true; } return { processedNumber, isConverted }; From 267d4216a5e653cd9b46c9562f8e8b078b1ab053 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:04:00 +0530 Subject: [PATCH 40/66] Emission Form migrations fixed --- backend/services/src/dtos/emission.dto.ts | 10 --- .../services/src/emission/emission.service.ts | 4 +- .../services/src/entities/emission.entity.ts | 6 -- web/src/Components/Inventory/emissionForm.tsx | 88 +++++++++++-------- web/src/Definitions/emissionDefinitions.tsx | 2 - web/src/Pages/Emissions/emissions.tsx | 30 +++++++ web/src/Utils/payloadCreators.tsx | 7 +- 7 files changed, 85 insertions(+), 62 deletions(-) diff --git a/backend/services/src/dtos/emission.dto.ts b/backend/services/src/dtos/emission.dto.ts index 2f288cc9..da05fa9e 100644 --- a/backend/services/src/dtos/emission.dto.ts +++ b/backend/services/src/dtos/emission.dto.ts @@ -42,16 +42,6 @@ export class EmissionDto { @IsNotEmpty() @Type(() => EmissionOther) other: EmissionOther; - - @ApiProperty() - @IsNotEmpty() - @Type(() => EmissionProperties) - totalCo2WithoutLand: EmissionProperties; - - @ApiProperty() - @IsNotEmpty() - @Type(() => EmissionProperties) - totalCo2WithLand: EmissionProperties; } export class EmissionValidateDto { diff --git a/backend/services/src/emission/emission.service.ts b/backend/services/src/emission/emission.service.ts index 74a57b88..6b9b9190 100644 --- a/backend/services/src/emission/emission.service.ts +++ b/backend/services/src/emission/emission.service.ts @@ -31,7 +31,7 @@ export class GhgEmissionsService { const emission: EmissionEntity = this.toEmission(emissionDto); this.verifyEmissionValues(emission); - let savedEmission; + let savedEmission: any; const result = await this.getEmissionByYear(emission.year, user); if (result && result.length > 0) { @@ -55,8 +55,6 @@ export class GhgEmissionsService { agricultureForestryOtherLandUse: emission.agricultureForestryOtherLandUse, waste: emission.waste, other: emission.other, - totalCo2WithoutLand: emission.totalCo2WithoutLand, - totalCo2WithLand: emission.totalCo2WithLand, state: emission.state, }); return updatedData; diff --git a/backend/services/src/entities/emission.entity.ts b/backend/services/src/entities/emission.entity.ts index 88bfd5ee..083e4bd4 100644 --- a/backend/services/src/entities/emission.entity.ts +++ b/backend/services/src/entities/emission.entity.ts @@ -35,12 +35,6 @@ export class EmissionEntity { @Column('jsonb', { array: false, nullable: true }) other?: EmissionOther; - @Column('jsonb', { array: false, nullable: true }) - totalCo2WithoutLand?: EmissionProperties; - - @Column('jsonb', { array: false, nullable: true }) - totalCo2WithLand?: EmissionProperties; - @Column({ type: 'enum', enum: GHGRecordState, diff --git a/web/src/Components/Inventory/emissionForm.tsx b/web/src/Components/Inventory/emissionForm.tsx index cbefd4c0..5b11d2c3 100644 --- a/web/src/Components/Inventory/emissionForm.tsx +++ b/web/src/Components/Inventory/emissionForm.tsx @@ -16,7 +16,6 @@ import { import NumberChip from '../NumberChip/numberChip'; import { AgricultureSection, - EmissionData, EmissionTotals, EnergySection, IndustrySection, @@ -25,7 +24,6 @@ import { SubSectionsDefinition, WasteSection, agricultureSectionInit, - emissionInitData, emissionSections, emissionTotals, energySectionInit, @@ -33,7 +31,6 @@ import { otherSectionInit, processAgrEmissionData, processEnergyEmissionData, - processIndividualEmissionData, processIndustryEmissionData, processOtherEmissionData, processWasteEmissionData, @@ -53,6 +50,10 @@ interface Props { year: string | null; finalized: boolean; availableYears: number[]; + gwpSetting: { + [EmissionUnits.CH4]: number; + [EmissionUnits.N2O]: number; + }; setActiveYear: React.Dispatch>; getAvailableEmissionReports: () => void; } @@ -64,6 +65,7 @@ export const EmissionForm: React.FC = ({ year, finalized, availableYears, + gwpSetting, setActiveYear, getAvailableEmissionReports, }) => { @@ -94,8 +96,6 @@ export const EmissionForm: React.FC = ({ const [agrSection, setAgrSection] = useState({ ...agricultureSectionInit }); const [wasteSection, setWasteSection] = useState({ ...wasteSectionInit }); const [otherSection, setOtherSection] = useState({ ...otherSectionInit }); - const [eqWithout, setEqWithout] = useState({ ...emissionInitData }); - const [eqWith, setEqWith] = useState({ ...emissionInitData }); // Total State @@ -192,8 +192,6 @@ export const EmissionForm: React.FC = ({ setAgrSection(processAgrEmissionData(response.data[0].agricultureForestryOtherLandUse)); setWasteSection(processWasteEmissionData(response.data[0].waste)); setOtherSection(processOtherEmissionData(response.data[0].other)); - setEqWith(processIndividualEmissionData(response.data[0].totalCo2WithLand)); - setEqWithout(processIndividualEmissionData(response.data[0].totalCo2WithoutLand)); } } catch (error) { console.error('Error fetching timeline data:', error); @@ -210,8 +208,6 @@ export const EmissionForm: React.FC = ({ setAgrSection({ ...agricultureSectionInit }); setWasteSection({ ...wasteSectionInit }); setOtherSection({ ...otherSectionInit }); - setEqWithout({ ...emissionInitData }); - setEqWith({ ...emissionInitData }); setEmissionTotal({ ...emissionTotals }); setEmissionYear(undefined); @@ -242,18 +238,6 @@ export const EmissionForm: React.FC = ({ } const newValue = enteredValue ? parseToTwoDecimals(enteredValue) : 0; switch (section) { - case 'eqWithout': - setEqWithout((prevState) => ({ - ...prevState, - [unit]: newValue, - })); - return; - case 'eqWith': - setEqWith((prevState) => ({ - ...prevState, - [unit]: newValue, - })); - return; case '1': const energy = levelTwo as EnergyLevels; let secondLevel; @@ -390,7 +374,49 @@ export const EmissionForm: React.FC = ({ (section) => (overallSum += getSectionUnitSum(section, unit) ?? 0) ); - return (overallSum ?? 0) + (eqWith[unit] ?? 0) + (eqWithout[unit] ?? 0); + return overallSum ?? 0; + }; + + // GWP Multiplier + + const convertToEquivalentEmission = (rawValue: number, unit: EmissionUnits) => { + switch (unit) { + case EmissionUnits.CH4: + return gwpSetting[EmissionUnits.CH4] * rawValue; + case EmissionUnits.N2O: + return gwpSetting[EmissionUnits.N2O] * rawValue; + default: + return 1 * rawValue; + } + }; + + // Get EQV Sum with Land + + const getOverallEquivalentWithLands = (unit: EmissionUnits) => { + let overallSum = 0; + + Object.values(SectionLevels).map( + (section) => (overallSum += getSectionUnitSum(section, unit) ?? 0) + ); + + const convertedSum = convertToEquivalentEmission(overallSum, unit); + + return convertedSum ?? 0; + }; + + // Get EQV Sum without Land + + const getOverallEquivalentWithoutLands = (unit: EmissionUnits) => { + let overallSum = 0; + + Object.values(SectionLevels).map( + (section) => (overallSum += getSectionUnitSum(section, unit) ?? 0) + ); + + const overallSumWithoutLand = overallSum - (agrSection[AgrLevels.ThreeB][unit] ?? 0); + const convertedSum = convertToEquivalentEmission(overallSumWithoutLand, unit); + + return convertedSum ?? 0; }; // Handle Submit @@ -404,9 +430,7 @@ export const EmissionForm: React.FC = ({ industrySection, agrSection, wasteSection, - otherSection, - eqWith, - eqWithout + otherSection ); const response: any = await post('national/emissions/add', emissionCreatePayload); @@ -651,11 +675,8 @@ export const EmissionForm: React.FC = ({ {Object.values(EmissionUnits).map((unit) => ( - setIndividualEntry(value ?? undefined, 'eqWithout', null, null, unit) - } + disabled={true} + value={getOverallEquivalentWithoutLands(unit)} decimalSeparator="." controls={false} className="input-emission" @@ -670,11 +691,8 @@ export const EmissionForm: React.FC = ({ {Object.values(EmissionUnits).map((unit) => ( - setIndividualEntry(value ?? undefined, 'eqWith', null, null, unit) - } + disabled={true} + value={getOverallEquivalentWithLands(unit)} decimalSeparator="." controls={false} className="input-emission" diff --git a/web/src/Definitions/emissionDefinitions.tsx b/web/src/Definitions/emissionDefinitions.tsx index 64a5231d..29e22f20 100644 --- a/web/src/Definitions/emissionDefinitions.tsx +++ b/web/src/Definitions/emissionDefinitions.tsx @@ -388,6 +388,4 @@ export type EmissionPayload = { indirectN2oEmissions: any; other: any; }; - totalCo2WithoutLand: any; - totalCo2WithLand: any; }; diff --git a/web/src/Pages/Emissions/emissions.tsx b/web/src/Pages/Emissions/emissions.tsx index 7ae1f332..a2abf6a7 100644 --- a/web/src/Pages/Emissions/emissions.tsx +++ b/web/src/Pages/Emissions/emissions.tsx @@ -8,6 +8,7 @@ import { useEffect, useState } from 'react'; import { LockOutlined, PlusCircleOutlined } from '@ant-design/icons'; import { EmissionTabItem } from '../../Definitions/emissionDefinitions'; import { useUserContext } from '../../Context/UserInformationContext/userInformationContext'; +import { EmissionUnits } from '../../Enums/emission.enum'; const GhgEmissions = () => { // Page Context @@ -28,6 +29,16 @@ const GhgEmissions = () => { const [activeYear, setActiveYear] = useState(); + // GWP Settings + + const [gwpSetting, setGwpSetting] = useState<{ + [EmissionUnits.CH4]: number; + [EmissionUnits.N2O]: number; + }>({ + [EmissionUnits.CH4]: 1, + [EmissionUnits.N2O]: 1, + }); + // Getter of all available emission report years and their state const getAvailableEmissionReports = async () => { @@ -46,9 +57,26 @@ const GhgEmissions = () => { } }; + const fetchGwpSettings = async () => { + let response: any; + try { + response = await get(`national/settings/GWP`); + + if (response.status === 200 || response.status === 201) { + setGwpSetting({ + [EmissionUnits.CH4]: response.data.gwp_ch4, + [EmissionUnits.N2O]: response.data.gwp_n2o, + }); + } + } catch (error) { + console.log('Error fetching GWP values:', error); + } + }; + // Init Function at first render useEffect(() => { + fetchGwpSettings(); getAvailableEmissionReports(); }, []); @@ -71,6 +99,7 @@ const GhgEmissions = () => { year={null} finalized={false} availableYears={availableYears} + gwpSetting={gwpSetting} setActiveYear={setActiveYear} getAvailableEmissionReports={getAvailableEmissionReports} /> @@ -94,6 +123,7 @@ const GhgEmissions = () => { index={index + 1} year={report.year} availableYears={availableYears} + gwpSetting={gwpSetting} setActiveYear={setActiveYear} finalized={report.state === 'FINALIZED' ? true : false} getAvailableEmissionReports={getAvailableEmissionReports} diff --git a/web/src/Utils/payloadCreators.tsx b/web/src/Utils/payloadCreators.tsx index 898a9c70..ae107035 100644 --- a/web/src/Utils/payloadCreators.tsx +++ b/web/src/Utils/payloadCreators.tsx @@ -1,6 +1,5 @@ import { AgricultureSection, - EmissionData, EmissionPayload, EnergySection, IndustrySection, @@ -27,9 +26,7 @@ export const getEmissionCreatePayload = ( industryData: IndustrySection, agrData: AgricultureSection, wasteData: WasteSection, - otherData: OtherSection, - eqWith: EmissionData, - eqWithout: EmissionData + otherData: OtherSection ) => { const tempPayload: EmissionPayload = { year: year, @@ -79,8 +76,6 @@ export const getEmissionCreatePayload = ( indirectN2oEmissions: otherData[OtherLevels.FiveA], other: otherData[OtherLevels.FiveB], }, - totalCo2WithoutLand: eqWithout, - totalCo2WithLand: eqWith, }; return tempPayload; From 1178d41297d131ad9bf886b7301c0629379abc07 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:08:14 +0530 Subject: [PATCH 41/66] Estimated Investment Need added to Action Form --- web/src/Pages/Actions/ActionForm/actionForm.tsx | 2 +- web/src/locales/i18n/formTable/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index 020fc7b4..c86e01ee 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -563,7 +563,7 @@ const actionForm: React.FC = ({ method }) => { type: prg.action?.type, status: prg.programmeStatus, subSectorsAffected: prg.affectedSubSector ?? [], - estimatedInvestment: prg.investment, + estimatedInvestment: prg.migratedData[0]?.estimatedAmount ?? 0, ghgsAffected: prg.migratedData[0]?.ghgsAffected ?? [], types: prg.migratedData[0]?.types ?? [], natImplementer: prg.natImplementor ?? [], diff --git a/web/src/locales/i18n/formTable/en.json b/web/src/locales/i18n/formTable/en.json index 0645e730..42945bab 100644 --- a/web/src/locales/i18n/formTable/en.json +++ b/web/src/locales/i18n/formTable/en.json @@ -5,7 +5,7 @@ "programmeType": "Type", "programmeStatus": "Programme Status", "subSectorAffected": "Sub-Sector Affected", - "investmentNeeds": " Estimated investment needs (USD)", + "investmentNeeds": " Estimated Investment Needs (USD)", "projectId": "Project ID", "projectName": "Project Name", From 22348e9f3735205552eac7877f1a7774948256a0 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:07:06 +0530 Subject: [PATCH 42/66] International Implementor removed from Project level --- backend/services/src/dtos/project.dto.ts | 29 ------------------- .../services/src/dtos/projectUpdate.dto.ts | 28 ------------------ .../src/entities/programme.view.entity.ts | 12 ++++---- .../services/src/entities/project.entity.ts | 9 ------ .../src/entities/project.view.entity.ts | 5 ++++ .../src/entities/report.five.view.entity.ts | 12 -------- backend/services/src/enums/action.enum.ts | 2 +- .../src/programme/programme.service.ts | 23 ++++++--------- web/src/Enums/action.enum.ts | 2 +- .../Projects/ProjectForm/projectForm.tsx | 15 +++------- .../Projects/ProjectList/projectList.tsx | 3 +- 11 files changed, 29 insertions(+), 111 deletions(-) diff --git a/backend/services/src/dtos/project.dto.ts b/backend/services/src/dtos/project.dto.ts index efc7ae1f..73ce7d21 100644 --- a/backend/services/src/dtos/project.dto.ts +++ b/backend/services/src/dtos/project.dto.ts @@ -52,35 +52,6 @@ export class ProjectDto { @ApiProperty() endYear: number; - // @IsArray() - // @ArrayMinSize(1) - // @MaxLength(100, { each: true }) - // @IsNotEmpty({ each: true }) - // @IsEnum(Recipient, { - // each: true, - // message: 'Invalid Recipient Entity. Supported following entities:' + Object.values(Recipient) - // }) - // @ApiProperty({ - // type: [String], - // enum: Object.values(Recipient), - // }) - // recipientEntities: Recipient[]; - - @ValidateIf((c) => c.internationalImplementingEntities) - @IsArray() - @ArrayMinSize(1) - @MaxLength(100, { each: true }) - @IsNotEmpty({ each: true }) - @IsEnum(IntImplementor, { - each: true, - message: 'Invalid International Implementing Entity. Supported following entities:' + Object.values(IntImplementor) - }) - @ApiPropertyOptional({ - type: [String], - enum: Object.values(IntImplementor), - }) - internationalImplementingEntities: IntImplementor[]; - @IsOptional() @ApiPropertyOptional( { diff --git a/backend/services/src/dtos/projectUpdate.dto.ts b/backend/services/src/dtos/projectUpdate.dto.ts index 61b0f065..6e7f21e3 100644 --- a/backend/services/src/dtos/projectUpdate.dto.ts +++ b/backend/services/src/dtos/projectUpdate.dto.ts @@ -57,34 +57,6 @@ export class ProjectUpdateDto { @ApiProperty() endYear: number; - // @IsArray() - // @ArrayMinSize(1) - // @MaxLength(100, { each: true }) - // @IsNotEmpty({ each: true }) - // @IsEnum(Recipient, { - // each: true, - // message: 'Invalid Recipient Entity. Supported following entities:' + Object.values(Recipient) - // }) - // @ApiProperty({ - // type: [String], - // enum: Object.values(Recipient), - // }) - // recipientEntities: Recipient[]; - - @ValidateIf((c) => c.internationalImplementingEntities) - @IsArray() - @MaxLength(100, { each: true }) - @IsNotEmpty({ each: true }) - @IsEnum(IntImplementor, { - each: true, - message: 'Invalid International Implementing Entity. Supported following entities:' + Object.values(IntImplementor) - }) - @ApiProperty({ - type: [String], - enum: Object.values(IntImplementor), - }) - internationalImplementingEntities: IntImplementor[]; - @IsOptional() @ApiPropertyOptional() @IsString() diff --git a/backend/services/src/entities/programme.view.entity.ts b/backend/services/src/entities/programme.view.entity.ts index 2bbea930..3a12d796 100644 --- a/backend/services/src/entities/programme.view.entity.ts +++ b/backend/services/src/entities/programme.view.entity.ts @@ -5,12 +5,12 @@ WITH fullprj AS ( SELECT prj."projectId" AS id, prj."programmeId", - prj."internationalImplementingEntities", p_v_e."recipientEntities", + p_v_e."internationalImplementingEntities", COALESCE(SUM(p_v_e."achievedGHGReduction"), 0) AS "achievedGHGReduction", COALESCE(SUM(p_v_e."expectedGHGReduction"), 0) AS "expectedGHGReduction", COALESCE(SUM(p_v_e."estimatedAmount"), 0) AS "estimatedAmount", - COALESCE(SUM(p_v_e."receivedAmount"), 0) AS "receivedAmount", + COALESCE(SUM(p_v_e."receivedAmount"), 0) AS "receivedAmount", CUSTOM_ARRAY_AGG(p_v_e."ghgsAffected") FILTER (WHERE p_v_e."ghgsAffected" IS NOT NULL) AS "ghgsAffected" FROM project prj @@ -21,13 +21,14 @@ WITH fullprj AS ( "expectedGHGReduction", "ghgsAffected", "recipientEntities", + "internationalImplementingEntities", "estimatedAmount", "receivedAmount" FROM project_view_entity ) p_v_e ON prj."projectId" = p_v_e.id GROUP BY - prj."projectId", prj."programmeId", prj."internationalImplementingEntities", p_v_e."recipientEntities" + prj."projectId", prj."programmeId", p_v_e."recipientEntities", p_v_e."internationalImplementingEntities" ), act AS ( SELECT @@ -37,7 +38,8 @@ act AS ( COALESCE(SUM(sup."requiredAmount"), 0) AS "requiredAmount", COALESCE(SUM(sup."receivedAmount"), 0) AS "receivedAmount", ARRAY_AGG(a."ghgsAffected") FILTER (WHERE a."ghgsAffected" IS NOT NULL)::character varying[] AS "ghgsAffected", - CUSTOM_ARRAY_AGG(a."recipientEntities") FILTER (WHERE a."recipientEntities" IS NOT NULL)::character varying[] AS "recipientEntities" + CUSTOM_ARRAY_AGG(a."recipientEntities") FILTER (WHERE a."recipientEntities" IS NOT NULL)::character varying[] AS "recipientEntities", + CUSTOM_ARRAY_AGG(a."internationalImplementingEntity") FILTER (WHERE a."internationalImplementingEntity" IS NOT NULL)::character varying[] AS "internationalImplementingEntities" FROM activity a LEFT JOIN ( @@ -57,8 +59,8 @@ act AS ( ) SELECT p."programmeId" AS id, - CUSTOM_ARRAY_AGG(fullprj."internationalImplementingEntities") FILTER (WHERE fullprj."internationalImplementingEntities" IS NOT NULL) AS "internationalImplementingEntities", CUSTOM_ARRAY_AGG(DISTINCT COALESCE(fullprj."recipientEntities", '{}') || COALESCE(act."recipientEntities", '{}')) FILTER (WHERE (fullprj."recipientEntities" IS NOT NULL OR act."recipientEntities" IS NOT NULL)) AS "recipientEntities", + CUSTOM_ARRAY_AGG(DISTINCT COALESCE(fullprj."internationalImplementingEntities", '{}') || COALESCE(act."internationalImplementingEntities", '{}')) FILTER (WHERE (fullprj."internationalImplementingEntities" IS NOT NULL OR act."internationalImplementingEntities" IS NOT NULL)) AS "internationalImplementingEntities", COALESCE(SUM(fullprj."achievedGHGReduction"), 0) + COALESCE(act."achievedGHGReduction", 0) AS "achievedGHGReduction", COALESCE(SUM(fullprj."expectedGHGReduction"), 0) + COALESCE(act."expectedGHGReduction", 0) AS "expectedGHGReduction", CUSTOM_ARRAY_AGG(DISTINCT COALESCE(fullprj."ghgsAffected", '{}') || COALESCE(act."ghgsAffected", '{}')) FILTER (WHERE (fullprj."ghgsAffected" IS NOT NULL OR act."ghgsAffected" IS NOT NULL)) AS "ghgsAffected", diff --git a/backend/services/src/entities/project.entity.ts b/backend/services/src/entities/project.entity.ts index 4f8b8864..0ab358ff 100644 --- a/backend/services/src/entities/project.entity.ts +++ b/backend/services/src/entities/project.entity.ts @@ -32,12 +32,6 @@ export class ProjectEntity { @Column({ nullable: true }) expectedTimeFrame: number; - // @Column("varchar", { array: true, nullable: false }) - // recipientEntities: Recipient[]; - - @Column("varchar", { array: true, nullable: true }) - internationalImplementingEntities: IntImplementor[]; - @Column({ type: 'jsonb', nullable: true }) documents: any; @@ -47,9 +41,6 @@ export class ProjectEntity { @Column({ type: "enum", enum: Sector, nullable: true }) sector: Sector; - // @Column({ type: "enum", enum: ActionType,nullable: false }) - // type: string; - @Column({ type: "ltree" }) path: string; diff --git a/backend/services/src/entities/project.view.entity.ts b/backend/services/src/entities/project.view.entity.ts index 8fa73f0b..d21c41f4 100644 --- a/backend/services/src/entities/project.view.entity.ts +++ b/backend/services/src/entities/project.view.entity.ts @@ -7,6 +7,7 @@ SELECT ARRAY_AGG(DISTINCT fullact."ghgsAffected") FILTER (WHERE fullact."ghgsAffected" IS NOT NULL) AS "ghgsAffected", ARRAY_AGG(DISTINCT fullact."meansOfImplementation") FILTER (WHERE fullact."meansOfImplementation" IS NOT NULL) AS "meansOfImplementation", CUSTOM_ARRAY_AGG(fullact."recipientEntities") FILTER (WHERE fullact."recipientEntities" IS NOT NULL) AS "recipientEntities", + CUSTOM_ARRAY_AGG(fullact."internationalImplementingEntities") FILTER (WHERE fullact."internationalImplementingEntities" IS NOT NULL) AS "internationalImplementingEntities", SUM(fullact."requiredAmount") AS "estimatedAmount", SUM(fullact."receivedAmount") AS "receivedAmount", SUM(fullact."requiredAmountDomestic") AS "estimatedAmountDomestic", @@ -25,6 +26,7 @@ LEFT JOIN ( act."achievedGHGReduction" AS "achievedGHGReduction", act."expectedGHGReduction" AS "expectedGHGReduction", act."recipientEntities" AS "recipientEntities", + act."internationalImplementingEntity" AS "internationalImplementingEntities", sup."requiredAmount", sup."receivedAmount", sup."requiredAmountDomestic", @@ -67,6 +69,9 @@ export class ProjectViewEntity { @ViewColumn() recipientEntities: string[]; + @ViewColumn() + internationalImplementingEntities: string[]; + @ViewColumn() technologyTypes: string[] diff --git a/backend/services/src/entities/report.five.view.entity.ts b/backend/services/src/entities/report.five.view.entity.ts index eff2ddd4..6c248b1f 100644 --- a/backend/services/src/entities/report.five.view.entity.ts +++ b/backend/services/src/entities/report.five.view.entity.ts @@ -57,21 +57,9 @@ export class ReportFiveViewEntity { @ViewColumn() actionId: string; - // @ViewColumn() - // programmeId: string; - - // @ViewColumn() - // projectId: string; - @ViewColumn() titleOfAction: string; - // @ViewColumn() - // titleOfProgramme: string; - - // @ViewColumn() - // titleOfProject: string; - @ViewColumn() description: string; diff --git a/backend/services/src/enums/action.enum.ts b/backend/services/src/enums/action.enum.ts index fe8b543e..986ee97f 100644 --- a/backend/services/src/enums/action.enum.ts +++ b/backend/services/src/enums/action.enum.ts @@ -20,7 +20,7 @@ export enum NatAnchor { export enum ActionType { MITIGATION = "Mitigation", - ADAPTION = "Adaption", + ADAPTION = "Adaptation", CROSSCUT = "Cross-cutting", TRANSPARENCY = "Transparency", OTHER = "Other", diff --git a/backend/services/src/programme/programme.service.ts b/backend/services/src/programme/programme.service.ts index 6791f7ae..0c5570e6 100644 --- a/backend/services/src/programme/programme.service.ts +++ b/backend/services/src/programme/programme.service.ts @@ -986,22 +986,18 @@ export class ProgrammeService { const recipientEntitySet: Set = new Set(); const interNationalImplementorSet: Set = new Set(); - if (programme.projects && programme.projects.length > 0) { - for (const project of programme.projects) { - if (project.internationalImplementingEntities) { - project.internationalImplementingEntities.forEach(internationalImplementer => { - interNationalImplementorSet.add(internationalImplementer); - }); - }; - } - } - if (migratedData && migratedData.length > 0) { for (const data of migratedData) { if (data.recipientEntities) { - data.recipientEntities.forEach((recipientEntity) => { - recipientEntitySet.add(recipientEntity); - }); + data.recipientEntities.forEach((recipientEntity) => { + recipientEntitySet.add(recipientEntity); + }); + } + + if (data.internationalImplementingEntities) { + data.internationalImplementingEntities.forEach((IntImplementor) => { + interNationalImplementorSet.add(IntImplementor); + }); } } } @@ -1023,7 +1019,6 @@ export class ProgrammeService { programmeViewDto.objectives = programme.objective; programmeViewDto.instrumentType = programme.action?.instrumentType; programmeViewDto.sector = programme.sector; - // programmeViewDto.type = programme.type; programmeViewDto.affectedSubSector = programme.affectedSubSector; programmeViewDto.programmeStatus = programme.programmeStatus; programmeViewDto.recipientEntity = recipientEntity; diff --git a/web/src/Enums/action.enum.ts b/web/src/Enums/action.enum.ts index e078a3f9..52553464 100644 --- a/web/src/Enums/action.enum.ts +++ b/web/src/Enums/action.enum.ts @@ -11,7 +11,7 @@ export enum Action { export enum ActionType { MITIGATION = 'Mitigation', - ADAPTION = 'Adaption', + ADAPTION = 'Adaptation', CROSSCUT = 'Cross-cutting', TRANSPARENCY = 'Transparency', OTHER = 'Other', diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.tsx b/web/src/Pages/Projects/ProjectForm/projectForm.tsx index 9299530c..d059bed3 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.tsx +++ b/web/src/Pages/Projects/ProjectForm/projectForm.tsx @@ -8,7 +8,7 @@ import UploadFileGrid from '../../../Components/Upload/uploadFiles'; import { useConnection } from '../../../Context/ConnectionContext/connectionContext'; import './projectForm.scss'; import { ProjectStatus } from '../../../Enums/project.enum'; -import { IntImplementor, KPIAction, Recipient } from '../../../Enums/shared.enum'; +import { KPIAction, Recipient } from '../../../Enums/shared.enum'; import EntityIdCard from '../../../Components/EntityIdCard/entityIdCard'; import { CreatedKpiData, NewKpiData } from '../../../Definitions/kpiDefinitions'; import { ProgrammeSelectData } from '../../../Definitions/programmeDefinitions'; @@ -502,7 +502,7 @@ const ProjectForm: React.FC = ({ method }) => { expectedTimeFrame: entityData.expectedTimeFrame, recipientEntities: entityData.recipientEntities, internationalImplementingEntities: - entityData.internationalImplementingEntities ?? undefined, + entityData.migratedData?.internationalImplementingEntities ?? [], comment: entityData.comment ?? undefined, }); @@ -1183,15 +1183,8 @@ const ProjectForm: React.FC = ({ method }) => { style={{ fontSize: inputFontSize }} mode="multiple" allowClear - disabled={isView} - showSearch - > - {Object.values(IntImplementor).map((instrument) => ( - - ))} - + disabled={true} + > diff --git a/web/src/Pages/Projects/ProjectList/projectList.tsx b/web/src/Pages/Projects/ProjectList/projectList.tsx index 9fb55241..87614dac 100644 --- a/web/src/Pages/Projects/ProjectList/projectList.tsx +++ b/web/src/Pages/Projects/ProjectList/projectList.tsx @@ -139,7 +139,8 @@ const projectList = () => { title: unstructuredData[i].title, projectStatus: unstructuredData[i].projectStatus, recipientEntity: unstructuredData[i].migratedData[0]?.recipientEntities ?? [], - intImplementingEntity: unstructuredData[i].internationalImplementingEntities ?? [], + intImplementingEntity: + unstructuredData[i].migratedData[0]?.internationalImplementingEntities ?? [], validationStatus: unstructuredData[i].validated ? 'validated' : 'pending', natImplementingEntity: unstructuredData[i].programme?.natImplementor ?? [], estimatedInvestment: Math.round( From 8c13f56c1f4d3ab22a854fd3d74f3b3458f8cf99 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:00:51 +0530 Subject: [PATCH 43/66] KPI rendering issue fixed --- web/src/Pages/Actions/ActionForm/actionForm.tsx | 4 ++-- web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx | 4 ++-- web/src/Pages/Projects/ProjectForm/projectForm.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index c86e01ee..5d0978be 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -352,13 +352,13 @@ const actionForm: React.FC = ({ method }) => { const createKPI = () => { const newItem: NewKpiData = { - index: kpiCounter + 1, + index: kpiCounter, name: '', unit: '', achieved: undefined, expected: 0, }; - setKpiCounter(kpiCounter + 1); + setKpiCounter((prevCount) => prevCount + 1); setNewKpiList((prevList) => [...prevList, newItem]); setHandleKPI(true); setIsSaveButtonDisabled(false); diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 0485bc5c..56eb6d27 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -352,13 +352,13 @@ const ProgrammeForm: React.FC = ({ method }) => { const createKPI = () => { const newItem: NewKpiData = { - index: kpiCounter + 1, + index: kpiCounter, name: '', unit: '', achieved: undefined, expected: 0, }; - setKpiCounter(kpiCounter + 1); + setKpiCounter((prevCount) => prevCount + 1); setNewKpiList((prevList) => [...prevList, newItem]); setHandleKPI(true); setIsSaveButtonDisabled(false); diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.tsx b/web/src/Pages/Projects/ProjectForm/projectForm.tsx index d059bed3..ccd78913 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.tsx +++ b/web/src/Pages/Projects/ProjectForm/projectForm.tsx @@ -342,13 +342,13 @@ const ProjectForm: React.FC = ({ method }) => { const createKPI = () => { const newItem: NewKpiData = { - index: kpiCounter + 1, + index: kpiCounter, name: '', unit: '', achieved: undefined, expected: 0, }; - setKpiCounter(kpiCounter + 1); + setKpiCounter((prevCount) => prevCount + 1); setNewKpiList((prevList) => [...prevList, newItem]); setHandleKPI(true); setIsSaveButtonDisabled(false); From fb66f9da0d426bb8668434c8368f43890e742c4a Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:33:05 +0530 Subject: [PATCH 44/66] Gas Flow reset added to parent update --- .../Activities/ActivityForm/activityForm.tsx | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index aa454020..fc65ca07 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -158,29 +158,49 @@ const ActivityForm: React.FC = ({ method }) => { const mtgRange = 30; + const resetGasFlowOptions = () => { + setIsGasFlow(false); + setSelectedGhg(undefined); + setExpectedTimeline([]); + setActualTimeline([]); + + form.setFieldsValue({ + measure: undefined, + ghgsAffected: undefined, + achievedGHGReduction: undefined, + expectedGHGReduction: undefined, + }); + }; + const handleParentIdSelect = (id: string) => { - setConnectedParentId(id); - setShouldFetchParentKpi(true); + try { + resetGasFlowOptions(); + } finally { + setConnectedParentId(id); + setShouldFetchParentKpi(true); - if (id === undefined && method === 'create') { - setMtgStartYear(0); + if (id === undefined && method === 'create') { + setMtgStartYear(0); + } } }; const handleParentTypeSelect = (value: string) => { - setParentType(value); - setConnectedParentId(undefined); - - if (method === 'create') { - setMtgStartYear(0); - } + try { + resetGasFlowOptions(); + } finally { + setParentType(value); + setConnectedParentId(undefined); - setIsGasFlow(false); + if (method === 'create') { + setMtgStartYear(0); + } - form.setFieldsValue({ - parentId: '', - parentDescription: '', - }); + form.setFieldsValue({ + parentId: '', + parentDescription: '', + }); + } }; const fetchGwpSettings = async () => { From a1de7d4f153dd10237cca4e46815624b5ddadb35 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:46:08 +0530 Subject: [PATCH 45/66] Type of technology data type fix --- backend/services/src/entities/project.view.entity.ts | 2 +- web/src/Pages/Actions/ActionForm/actionForm.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/services/src/entities/project.view.entity.ts b/backend/services/src/entities/project.view.entity.ts index d21c41f4..286a210b 100644 --- a/backend/services/src/entities/project.view.entity.ts +++ b/backend/services/src/entities/project.view.entity.ts @@ -20,7 +20,7 @@ LEFT JOIN ( SELECT act."activityId", act."parentId" AS "projectId", - act."technologyType" AS "techTypes", + act."technologyType"::character varying AS "techTypes", act."ghgsAffected"::character varying AS "ghgsAffected", act."meansOfImplementation" AS "meansOfImplementation", act."achievedGHGReduction" AS "achievedGHGReduction", diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index 5d0978be..800e358a 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -487,7 +487,7 @@ const actionForm: React.FC = ({ method }) => { // Populating Migrated Fields (Will be overwritten when attachments change) form.setFieldsValue({ - ghgsAffected: entityData.migratedData?.ghgsAffected, + ghgsAffected: entityData.migratedData?.ghgsAffected ?? [], natImplementor: entityData.migratedData?.natImplementors ?? [], estimatedInvestment: entityData.migratedData?.financeNeeded ?? 0, achievedReduct: entityData.migratedData?.achievedGHGReduction, From 8288ae384614294dcf204e620a2cfa53696b1694 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:55:50 +0530 Subject: [PATCH 46/66] First render completion check added --- .../Pages/Actions/ActionForm/actionForm.tsx | 34 ++-- .../Activities/ActivityForm/activityForm.tsx | 23 ++- .../ProgrammeForm/programmeForm.tsx | 37 ++-- .../Projects/ProjectForm/projectForm.tsx | 35 ++-- .../Pages/Support/SupportForm/supportForm.tsx | 187 +++++++++--------- 5 files changed, 167 insertions(+), 149 deletions(-) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index 800e358a..b8ad70a3 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -95,6 +95,10 @@ const actionForm: React.FC = ({ method }) => { const [isGasFlow, setIsGasFlow] = useState(false); + // First Render Check + + const [isFirstRenderDone, setIsFirstRenderDone] = useState(false); + // Spinner For Form Submit const [waitingForBE, setWaitingForBE] = useState(false); @@ -250,17 +254,13 @@ const actionForm: React.FC = ({ method }) => { duration: 3, style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, }); - - setWaitingForBE(false); - navigate('/actions'); } } catch (error: any) { displayErrorMessage(error); - await new Promise((resolve) => { setTimeout(resolve, 500); }); - + } finally { setWaitingForBE(false); navigate('/actions'); } @@ -674,21 +674,25 @@ const actionForm: React.FC = ({ method }) => { } }; - // Dynamic Update - - useEffect(() => { - fetchActionData(); - fetchCreatedKPIData(); - fetchConnectedProgrammeData(); - fetchConnectedActivityData(); - }, []); - // Fetching Activity data and calculating migrated fields when attachment changes useEffect(() => { fetchSupportData(); }, [activityData]); + // Init Job + + useEffect(() => { + Promise.all([ + fetchActionData(), + fetchCreatedKPIData(), + fetchConnectedProgrammeData(), + fetchConnectedActivityData(), + ]).then(() => { + setIsFirstRenderDone(true); + }); + }, []); + return (
= ({ method }) => {
{t(formTitle)}
- {!waitingForBE ? ( + {!waitingForBE && isFirstRenderDone ? (
= ({ method }) => { const [isGasFlow, setIsGasFlow] = useState(false); + // First Render Check + + const [isFirstRenderDone, setIsFirstRenderDone] = useState(false); + // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); @@ -772,11 +776,10 @@ const ActivityForm: React.FC = ({ method }) => { duration: 3, style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, }); - setWaitingForBE(false); - navigate('/activities'); } } catch (error: any) { displayErrorMessage(error); + } finally { setWaitingForBE(false); navigate('/activities'); } @@ -1170,13 +1173,17 @@ const ActivityForm: React.FC = ({ method }) => { } }, [actualTimeline]); - // Init Run + // Init JOb useEffect(() => { - fetchActivityData(); - fetchSupportData(); - fetchCreatedKPIData(); - fetchGwpSettings(); + Promise.all([ + fetchActivityData(), + fetchSupportData(), + fetchCreatedKPIData(), + fetchGwpSettings(), + ]).then(() => { + setIsFirstRenderDone(true); + }); }, []); return ( @@ -1199,7 +1206,7 @@ const ActivityForm: React.FC = ({ method }) => {
{t(formTitle)}
- {!waitingForBE ? ( + {!waitingForBE && isFirstRenderDone ? (
= ({ method }) => { const [isGasFlow, setIsGasFlow] = useState(false); + // First Render Check + + const [isFirstRenderDone, setIsFirstRenderDone] = useState(false); + // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); @@ -250,9 +254,6 @@ const ProgrammeForm: React.FC = ({ method }) => { duration: 3, style: { textAlign: 'right', marginRight: 15, marginTop: 10 }, }); - - setWaitingForBE(false); - navigate('/programmes'); } } catch (error: any) { displayErrorMessage(error); @@ -260,7 +261,7 @@ const ProgrammeForm: React.FC = ({ method }) => { await new Promise((resolve) => { setTimeout(resolve, 500); }); - + } finally { setWaitingForBE(false); navigate('/programmes'); } @@ -743,24 +744,26 @@ const ProgrammeForm: React.FC = ({ method }) => { } }; - // Dynamic Updates - - // Init For Entity - - useEffect(() => { - fetchNonValidatedActions(); - fetchProgramData(); - fetchAttachedKPIData(); - fetchConnectedProjectData(); - fetchConnectedActivityData(); - }, []); - // Fetching Support data, After Activity Data Loads useEffect(() => { fetchSupportData(); }, [activityData]); + // Init Job + + useEffect(() => { + Promise.all([ + fetchNonValidatedActions(), + fetchProgramData(), + fetchAttachedKPIData(), + fetchConnectedProjectData(), + fetchConnectedActivityData(), + ]).then(() => { + setIsFirstRenderDone(true); + }); + }, []); + return (
= ({ method }) => {
{t(formTitle)}
- {!waitingForBE ? ( + {!waitingForBE && isFirstRenderDone ? (
= ({ method }) => { const [isGasFlow, setIsGasFlow] = useState(false); + // First Render Check + + const [isFirstRenderDone, setIsFirstRenderDone] = useState(false); + // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); @@ -249,9 +253,6 @@ const ProjectForm: React.FC = ({ method }) => { await new Promise((resolve) => { setTimeout(resolve, 500); }); - - setWaitingForBE(false); - navigate('/projects'); } } catch (error: any) { displayErrorMessage(error); @@ -259,7 +260,7 @@ const ProjectForm: React.FC = ({ method }) => { await new Promise((resolve) => { setTimeout(resolve, 500); }); - + } finally { setWaitingForBE(false); navigate('/projects'); } @@ -766,17 +767,6 @@ const ProjectForm: React.FC = ({ method }) => { } }; - // Dynamic Updates - - // Init for Entity - - useEffect(() => { - fetchNonValidatedProgrammes(); - fetchProjectData(); - fetchCreatedKPIData(); - fetchConnectedActivityData(); - }, []); - // Fetching Action data for parent change useEffect(() => { @@ -809,6 +799,19 @@ const ProjectForm: React.FC = ({ method }) => { } }, [startYear, endYear]); + // Init Job + + useEffect(() => { + Promise.all([ + fetchNonValidatedProgrammes(), + fetchProjectData(), + fetchCreatedKPIData(), + fetchConnectedActivityData(), + ]).then(() => { + setIsFirstRenderDone(true); + }); + }, []); + return (
= ({ method }) => {
{t(formTitle)}
- {!waitingForBE ? ( + {!waitingForBE && isFirstRenderDone ? (
= ({ method }) => { const validation = getValidationRules(method); - // First Rendering Check + // First Render Check - const [firstRenderingCompleted, setFirstRenderingCompleted] = useState(false); + const [isFirstRenderDone, setIsFirstRenderDone] = useState(false); // Entity Validation Status @@ -114,104 +114,86 @@ const SupportForm: React.FC = ({ method }) => { } }; - useEffect(() => { - // Fetching All Activities which can be the parent - const fetchNonValidatedActivities = async () => { + // DB Queries + + const fetchNonValidatedActivities = async () => { + try { + const payload = { + sort: { + key: 'activityId', + order: 'ASC', + }, + }; + const response: any = await post('national/activities/query', payload); + const tempActivityData: ParentData[] = []; + response.data.forEach((activity: any) => { + tempActivityData.push({ + id: activity.activityId, + title: activity.title, + }); + }); + setParentList(tempActivityData); + } catch (error: any) { + displayErrorMessage(error); + } + }; + + const fetchSupportData = async () => { + if (method !== 'create' && entId) { + let response: any; try { const payload = { - sort: { - key: 'activityId', - order: 'ASC', - }, + filterAnd: [ + { + key: 'supportId', + operation: '=', + value: entId, + }, + ], }; - const response: any = await post('national/activities/query', payload); - const tempActivityData: ParentData[] = []; - response.data.forEach((activity: any) => { - tempActivityData.push({ - id: activity.activityId, - title: activity.title, + response = await post('national/supports/query', payload); + + if (response.response.data.total === 1) { + const entityData: any = response.data[0]; + // Populating Action owned data fields + form.setFieldsValue({ + activityId: entityData.activity?.activityId ?? undefined, + direction: entityData.direction, + financeNature: entityData.financeNature, + internationalSupportChannel: entityData.internationalSupportChannel, + internationalFinancialInstrument: + entityData.internationalFinancialInstrument ?? undefined, + nationalFinancialInstrument: entityData.nationalFinancialInstrument ?? undefined, + financingStatus: entityData.financingStatus, + internationalSource: entityData.internationalSource ?? undefined, + nationalSource: entityData.nationalSource ?? undefined, + requiredAmount: entityData.requiredAmount, + receivedAmount: entityData.receivedAmount, + exchangeRate: entityData.exchangeRate, }); - }); - setParentList(tempActivityData); - } catch (error: any) { - displayErrorMessage(error); - } - }; - fetchNonValidatedActivities(); - - // Initially Loading the underlying support data when not in create mode - - const fetchData = async () => { - if (method !== 'create' && entId) { - let response: any; - try { - const payload = { - filterAnd: [ - { - key: 'supportId', - operation: '=', - value: entId, - }, - ], - }; - response = await post('national/supports/query', payload); - - if (response.response.data.total === 1) { - const entityData: any = response.data[0]; - // Populating Action owned data fields - form.setFieldsValue({ - activityId: entityData.activity?.activityId ?? undefined, - direction: entityData.direction, - financeNature: entityData.financeNature, - internationalSupportChannel: entityData.internationalSupportChannel, - internationalFinancialInstrument: - entityData.internationalFinancialInstrument ?? undefined, - nationalFinancialInstrument: entityData.nationalFinancialInstrument ?? undefined, - financingStatus: entityData.financingStatus, - internationalSource: entityData.internationalSource ?? undefined, - nationalSource: entityData.nationalSource ?? undefined, - requiredAmount: entityData.requiredAmount, - receivedAmount: entityData.receivedAmount, - exchangeRate: entityData.exchangeRate, - }); - - setAmountNeeded(entityData.requiredAmount ?? undefined); - setAmountReceived(entityData.receivedAmount ?? undefined); - setExchangeRate(entityData.exchangeRate ?? undefined); - - // Setting the field rendering conditions - - renderNatureBasedFields(entityData.financeNature); - - if (entityData.direction === 'Received') { - setIsReceived(true); - } - - // Setting validation status - - setIsValidated(entityData.validated ?? false); + + setAmountNeeded(entityData.requiredAmount ?? undefined); + setAmountReceived(entityData.receivedAmount ?? undefined); + setExchangeRate(entityData.exchangeRate ?? undefined); + + // Setting the field rendering conditions + + renderNatureBasedFields(entityData.financeNature); + + if (entityData.direction === 'Received') { + setIsReceived(true); } - } catch { - navigate('/support'); + + // Setting validation status + + setIsValidated(entityData.validated ?? false); } - setIsSaveButtonDisabled(true); + } catch { + navigate('/support'); } - }; - fetchData(); - - if (!firstRenderingCompleted) { - setFirstRenderingCompleted(true); + setIsSaveButtonDisabled(true); } - }, []); - - useEffect(() => { - const updatedNeeded = (amountNeeded ?? 0) / (exchangeRate ?? 0); - const updatedReceived = (amountReceived ?? 0) / (exchangeRate ?? 0); - form.setFieldsValue({ - neededLocal: updatedNeeded > 0 ? parseFloat(updatedNeeded.toFixed(2)) : null, - receivedLocal: updatedReceived > 0 ? parseFloat(updatedReceived.toFixed(2)) : null, - }); - }, [exchangeRate, amountNeeded, amountReceived]); + }; // Form Submit @@ -344,6 +326,25 @@ const SupportForm: React.FC = ({ method }) => { setIsSaveButtonDisabled(false); }; + // Dynamic Updates + + useEffect(() => { + const updatedNeeded = (amountNeeded ?? 0) / (exchangeRate ?? 0); + const updatedReceived = (amountReceived ?? 0) / (exchangeRate ?? 0); + form.setFieldsValue({ + neededLocal: updatedNeeded > 0 ? parseFloat(updatedNeeded.toFixed(2)) : null, + receivedLocal: updatedReceived > 0 ? parseFloat(updatedReceived.toFixed(2)) : null, + }); + }, [exchangeRate, amountNeeded, amountReceived]); + + // Init Job + + useEffect(() => { + Promise.all([fetchNonValidatedActivities(), fetchSupportData()]).then(() => { + setIsFirstRenderDone(true); + }); + }, []); + return (
= ({ method }) => {
{t(formTitle)}
- {!waitingForBE && firstRenderingCompleted ? ( + {!waitingForBE && isFirstRenderDone ? (
Date: Mon, 28 Oct 2024 17:46:12 +0530 Subject: [PATCH 47/66] Dashboard loading refined --- web/src/Pages/Dashboard/dashboard.tsx | 200 +++++++++++++------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/web/src/Pages/Dashboard/dashboard.tsx b/web/src/Pages/Dashboard/dashboard.tsx index d1852348..3416b629 100644 --- a/web/src/Pages/Dashboard/dashboard.tsx +++ b/web/src/Pages/Dashboard/dashboard.tsx @@ -1,4 +1,4 @@ -import { Col, Grid, Row, Select, Spin, Tag } from 'antd'; +import { Col, Grid, Row, Select, Tag } from 'antd'; import './dashboard.scss'; import { InfoCircleOutlined } from '@ant-design/icons'; import ChartInformation from '../../Components/Popups/chartInformation'; @@ -24,7 +24,6 @@ const Dashboard = () => { const { get, post, statServerUrl } = useConnection(); const [loading, setLoading] = useState(true); - const [chartLoading, setChartLoading] = useState(true); // Table Data State @@ -248,18 +247,33 @@ const Dashboard = () => { }); } catch (error: any) { displayErrorMessage(error); - } finally { - setChartLoading(false); } }; + // Action List Table Columns + + const columns = getActionTableColumns(t); + + // Handling Table Pagination and Sorting Changes + + // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars + const handleTableChange = (pagination: any, filters: any, sorter: any) => { + // Setting Pagination + setCurrentPage(pagination.current); + setPageSize(pagination.pageSize); + }; + // Data Fetching for GHG MTG Selected Year useEffect(() => { getIndividualMitigationChartData(); }, [mtgYear]); - // Data Fetching at the Initial Loading + useEffect(() => { + getAllData(); + }, [currentPage, pageSize]); + + // Init Job useEffect(() => { getClimateActionChartData(); @@ -269,41 +283,19 @@ const Dashboard = () => { getRecentMitigationChartData(); }, []); - useEffect(() => { - getAllData(); - }, [currentPage, pageSize]); - - // Action List Table Columns - - const columns = getActionTableColumns(t); - - // Handling Table Pagination and Sorting Changes - - // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars - const handleTableChange = (pagination: any, filters: any, sorter: any) => { - // Setting Pagination - setCurrentPage(pagination.current); - setPageSize(pagination.pageSize); - }; - return (
- {chartLoading && ( -
- -
- )} - {!chartLoading && ( -
- - - {actionChart && ( - -
+
+ + + +
+ {actionChart && ( + <>
{actionChart.chartTitle} @@ -321,12 +313,14 @@ const Dashboard = () => {
-
- - )} - {projectChart && ( - -
+ + )} +
+ + +
+ {projectChart && ( + <>
{projectChart.chartTitle} @@ -344,12 +338,14 @@ const Dashboard = () => {
-
- - )} - {supportChart && ( - -
+ + )} +
+ + +
+ {supportChart && ( + <>
{supportChart.chartTitle} @@ -367,12 +363,14 @@ const Dashboard = () => {
-
- - )} - {financeChart && ( - -
+ + )} +
+ + +
+ {financeChart && ( + <>
{financeChart.chartTitle} @@ -390,12 +388,14 @@ const Dashboard = () => {
-
- - )} - {mitigationIndividualChart && ( - -
+ + )} +
+ + +
+ {mitigationIndividualChart && ( + <>
{mitigationIndividualChart.chartTitle} @@ -430,12 +430,14 @@ const Dashboard = () => {
-
- - )} - {mitigationRecentChart && ( - -
+ + )} +
+ + +
+ {mitigationRecentChart && ( + <>
{mitigationRecentChart.chartTitle} @@ -456,38 +458,36 @@ const Dashboard = () => {
-
- - )} -
-
- )} - {!loading && ( -
- - - - - -
- )} + + )} +
+ +
+
+
+ + + + + +
); }; From 2aeb5afb935389137c9f17902af81f712b20c9e1 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:28:50 +0530 Subject: [PATCH 48/66] GWP Update for Migrated EXP and ACH Values --- .../services/src/activity/activity.service.ts | 5 ++- .../services/src/casl/casl-ability.factory.ts | 1 - .../src/util/configurationSettings.service.ts | 33 ++++++++++--------- .../Pages/Configurations/configurations.tsx | 8 ++--- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/backend/services/src/activity/activity.service.ts b/backend/services/src/activity/activity.service.ts index 53fb169f..20e31a8a 100644 --- a/backend/services/src/activity/activity.service.ts +++ b/backend/services/src/activity/activity.service.ts @@ -1524,7 +1524,7 @@ export class ActivityService { ) } - const activ = await this.entityManager + await this.entityManager .transaction(async (em) => { await em .createQueryBuilder() @@ -1549,6 +1549,9 @@ export class ActivityService { em.save(supportsList); } + // Refreshing M-View for ACH and EXP update + await this.helperService.refreshMaterializedViews(em); + await em.save(logs); return activity; }) diff --git a/backend/services/src/casl/casl-ability.factory.ts b/backend/services/src/casl/casl-ability.factory.ts index db6a701c..c08c61bf 100644 --- a/backend/services/src/casl/casl-ability.factory.ts +++ b/backend/services/src/casl/casl-ability.factory.ts @@ -31,7 +31,6 @@ export const createAppAbility = createMongoAbility as CreateAbility; @Injectable() export class CaslAbilityFactory { createForUser(user: User) { - console.log("createForUser", user); const { can, cannot, build } = new AbilityBuilder(createAppAbility); if (user) { if (user.role == Role.Root) { diff --git a/backend/services/src/util/configurationSettings.service.ts b/backend/services/src/util/configurationSettings.service.ts index 606c287d..c2291b3c 100644 --- a/backend/services/src/util/configurationSettings.service.ts +++ b/backend/services/src/util/configurationSettings.service.ts @@ -1,4 +1,4 @@ -import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common"; +import { HttpException, HttpStatus, Injectable } from "@nestjs/common"; import { InjectEntityManager, InjectRepository } from "@nestjs/typeorm"; import { EntityManager, Repository } from "typeorm"; import { BasicResponseDto } from "../dtos/basic.response.dto"; @@ -11,7 +11,6 @@ import { GHGRecordState } from "../enums/ghg.state.enum"; import { ExtendedProjectionType, ProjectionLeafSection } from "../enums/projection.enum"; import { User } from "../entities/user.entity"; import { GHGInventoryManipulate } from "../enums/user.enum"; -import { ActivityEntity } from "../entities/activity.entity"; import { GHGS } from "../enums/shared.enum"; @Injectable() @@ -21,7 +20,6 @@ export class ConfigurationSettingsService { @InjectRepository(ConfigurationSettingsEntity) private configSettingsRepo: Repository, @InjectRepository(ProjectionEntity) private projectionRepo: Repository, - @InjectRepository(ActivityEntity) private activityRepo: Repository, private helperService: HelperService ) { } @@ -89,7 +87,6 @@ export class ConfigurationSettingsService { if (savedSetting){ if ([ConfigurationSettingsType.PROJECTIONS_WITH_MEASURES, ConfigurationSettingsType.PROJECTIONS_WITH_ADDITIONAL_MEASURES, ConfigurationSettingsType.PROJECTIONS_WITHOUT_MEASURES].includes(type)){ - const projectionType = type === ConfigurationSettingsType.PROJECTIONS_WITH_MEASURES ? ExtendedProjectionType.BASELINE_WITH_MEASURES : ( type === ConfigurationSettingsType.PROJECTIONS_WITH_ADDITIONAL_MEASURES @@ -97,16 +94,16 @@ export class ConfigurationSettingsService { : ExtendedProjectionType.BASELINE_WITHOUT_MEASURES); await this.updateBaselineProjection(settingValue as ProjectionData, projectionType) + } else if (type === ConfigurationSettingsType.GWP){ + await this.updateActivityGhgState(setting.settingValue, oldGwp); + await this.helperService.refreshMaterializedViews(em); } } }) .catch((err: any) => { throw err; }); - - if(type === ConfigurationSettingsType.GWP){ - await this.updateMitigationTimeline(setting.settingValue, oldGwp); - } + // Return success message return new BasicResponseDto( HttpStatus.OK, @@ -187,16 +184,20 @@ export class ConfigurationSettingsService { } - private async updateMitigationTimeline(newGwpValue: any, oldGwpValue: any) { - - const currentYear = new Date().getFullYear(); + private async updateActivityGhgState(newGwpValue: any, oldGwpValue: any) { - if (oldGwpValue === undefined || newGwpValue.gwp_ch4 !== oldGwpValue.gwp_ch4) { - await this.entityManager.query(`SELECT update_mitigation_timeline(${newGwpValue.gwp_ch4}::INTEGER, '${GHGS.CH}', ${currentYear}::INTEGER);`); - } + try { + const currentYear = new Date().getFullYear(); - if (oldGwpValue === undefined || newGwpValue.gwp_n2o !== oldGwpValue.gwp_n2o) { - await this.entityManager.query(`SELECT update_mitigation_timeline(${newGwpValue.gwp_n2o}::INTEGER, '${GHGS.NO}', ${currentYear}::INTEGER);`); + if (oldGwpValue === undefined || newGwpValue.gwp_ch4 !== oldGwpValue.gwp_ch4) { + await this.entityManager.query(`SELECT update_mitigation_timeline(${newGwpValue.gwp_ch4}::INTEGER, '${GHGS.CH}', ${currentYear}::INTEGER);`); + } + + if (oldGwpValue === undefined || newGwpValue.gwp_n2o !== oldGwpValue.gwp_n2o) { + await this.entityManager.query(`SELECT update_mitigation_timeline(${newGwpValue.gwp_n2o}::INTEGER, '${GHGS.NO}', ${currentYear}::INTEGER);`); + } + } catch (error: any) { + console.log('Error occurred when updating activity ghg state ', error); } } } diff --git a/web/src/Pages/Configurations/configurations.tsx b/web/src/Pages/Configurations/configurations.tsx index a221e4a0..40b9220f 100644 --- a/web/src/Pages/Configurations/configurations.tsx +++ b/web/src/Pages/Configurations/configurations.tsx @@ -110,11 +110,9 @@ const GhgConfigurations = () => { > { - if (e.key === '-' || e.key === 'e' || e.key === '+') { + if (e.key === '.' || e.key === 'e' || e.key === '+') { e.preventDefault(); } }} @@ -130,11 +128,9 @@ const GhgConfigurations = () => { > { - if (e.key === '-' || e.key === 'e' || e.key === '+') { + if (e.key === '.' || e.key === 'e' || e.key === '+') { e.preventDefault(); } }} From bda8b545e4820ea0aa6a4c9851a6176ddee1dfa4 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:29:16 +0530 Subject: [PATCH 49/66] Action save in PRG Create fixed --- backend/services/src/programme/programme.service.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/services/src/programme/programme.service.ts b/backend/services/src/programme/programme.service.ts index 0c5570e6..5011f378 100644 --- a/backend/services/src/programme/programme.service.ts +++ b/backend/services/src/programme/programme.service.ts @@ -128,7 +128,6 @@ export class ProgrammeService { programme.action = action; programme.path = programmeDto.actionId; programme.sector = action.sector; - // programme.type = action.type; this.addEventLogEntry(eventLog, LogEventType.PROGRAMME_LINKED, EntityType.ACTION, action.actionId, user.id, programme.programmeId); this.addEventLogEntry(eventLog, LogEventType.LINKED_TO_ACTION, EntityType.PROGRAMME, programme.programmeId, user.id, action.actionId); @@ -171,12 +170,11 @@ export class ProgrammeService { programme.programmeId ); await em.save(saveUnvalidatedLog); - await em.save(action) + await em.update(ActionEntity, action.actionId, { validated: false }); await this.linkUnlinkService.updateAllValidatedChildrenStatusByActionId(action.actionId, em); } - - if (programmeDto.kpis) { + if (programmeDto.kpis) { await em.save(kpiList); } } From 600e61280f6fcf804cbad7c74eae20eb4ac6c822 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:31:19 +0530 Subject: [PATCH 50/66] Action Query order fixed --- backend/services/src/action/action.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/services/src/action/action.service.ts b/backend/services/src/action/action.service.ts index fe7546eb..298b4f43 100644 --- a/backend/services/src/action/action.service.ts +++ b/backend/services/src/action/action.service.ts @@ -186,7 +186,10 @@ export class ActionService { async findAllActionsForAttaching() { const allActions = await this.actionRepo.find({ - select: ["actionId", "title", "instrumentType", "sector", "type"] + select: ["actionId", "title", "instrumentType", "sector", "type"], + order: { + actionId: "ASC" + } }); if (allActions.length === 0){ From 6ae32d87898997d0d7be48189ebc84a420be7eea Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:24:43 +0530 Subject: [PATCH 51/66] Color fix in parent dropdowns --- web/src/Pages/Activities/ActivityForm/activityForm.tsx | 4 +++- web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 1e31180c..cb549e8f 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -1314,7 +1314,9 @@ const ActivityForm: React.FC = ({ method }) => { value={parent.id} disabled={parent.hasChildProgrammes} > - + {parent.hasChildProgrammes ? `${parent.id} : Attached to Programmes` : parent.id} diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 0f1e25f7..1ef1f437 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -831,7 +831,9 @@ const ProgrammeForm: React.FC = ({ method }) => { value={action.id} disabled={action.hasChildActivities} > - + {action.hasChildActivities ? `${action.id} : Attached to Activities` : action.id} From b0223d8814d6eed9fe5417fa879eee11a3f123ff Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:24:52 +0530 Subject: [PATCH 52/66] Bar chart refined --- web/src/Components/Charts/BarChart/barChart.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/web/src/Components/Charts/BarChart/barChart.tsx b/web/src/Components/Charts/BarChart/barChart.tsx index 844c4b91..b2ea6aa7 100644 --- a/web/src/Components/Charts/BarChart/barChart.tsx +++ b/web/src/Components/Charts/BarChart/barChart.tsx @@ -36,6 +36,9 @@ const BarChart: React.FC = ({ chart, t, chartHeight }) => { options={{ chart: { id: `Chart_${chart.chartId}`, + toolbar: { + show: false, + }, }, tooltip: { enabled: false, @@ -65,6 +68,16 @@ const BarChart: React.FC = ({ chart, t, chartHeight }) => { legend: { show: false, }, + annotations: { + yaxis: [ + { + y: 0, + borderColor: '#000', + borderWidth: 1, + strokeDashArray: 0, + }, + ], + }, }} series={[{ name: 'mtg_data', data: chart.values }]} height={chartHeight} From 444aaeb8299c77220cb5c727008cf4ff5a79ee56 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:42:58 +0530 Subject: [PATCH 53/66] Form reference moved up from the conditional block --- .../Pages/Actions/ActionForm/actionForm.tsx | 26 +++++++++---------- .../Activities/ActivityForm/activityForm.tsx | 26 +++++++++---------- .../ProgrammeForm/programmeForm.tsx | 26 +++++++++---------- .../Projects/ProjectForm/projectForm.tsx | 26 +++++++++---------- .../Pages/Support/SupportForm/supportForm.tsx | 26 +++++++++---------- 5 files changed, 65 insertions(+), 65 deletions(-) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index b8ad70a3..7027d197 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -713,14 +713,14 @@ const actionForm: React.FC = ({ method }) => {
{t(formTitle)}
- {!waitingForBE && isFirstRenderDone ? ( -
- + + {!waitingForBE && isFirstRenderDone ? ( +
{t('generalInfoTitle')}
{method !== 'create' && entId && ( @@ -1286,11 +1286,11 @@ const actionForm: React.FC = ({ method }) => { )} - -
- ) : ( - - )} +
+ ) : ( + + )} +
); }; diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index cb549e8f..2392bc9e 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -1206,14 +1206,14 @@ const ActivityForm: React.FC = ({ method }) => {
{t(formTitle)}
- {!waitingForBE && isFirstRenderDone ? ( -
-
+ + {!waitingForBE && isFirstRenderDone ? ( +
{t('generalInfoTitle')}
{method !== 'create' && entId && ( @@ -1987,11 +1987,11 @@ const ActivityForm: React.FC = ({ method }) => { )} - -
- ) : ( - - )} +
+ ) : ( + + )} +
); }; diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 1ef1f437..37b33678 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -784,14 +784,14 @@ const ProgrammeForm: React.FC = ({ method }) => {
{t(formTitle)}
- {!waitingForBE && isFirstRenderDone ? ( -
-
+ + {!waitingForBE && isFirstRenderDone ? ( +
{t('generalInfoTitle')}
{method !== 'create' && entId && ( @@ -1437,11 +1437,11 @@ const ProgrammeForm: React.FC = ({ method }) => { )} - -
- ) : ( - - )} +
+ ) : ( + + )} +
); }; diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.tsx b/web/src/Pages/Projects/ProjectForm/projectForm.tsx index 515bbc1b..860457d4 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.tsx +++ b/web/src/Pages/Projects/ProjectForm/projectForm.tsx @@ -832,14 +832,14 @@ const ProjectForm: React.FC = ({ method }) => {
{t(formTitle)}
- {!waitingForBE && isFirstRenderDone ? ( -
-
+ + {!waitingForBE && isFirstRenderDone ? ( +
{t('generalInfoTitle')}
{method !== 'create' && entId && ( @@ -1575,11 +1575,11 @@ const ProjectForm: React.FC = ({ method }) => { )} - -
- ) : ( - - )} +
+ ) : ( + + )} +
); }; diff --git a/web/src/Pages/Support/SupportForm/supportForm.tsx b/web/src/Pages/Support/SupportForm/supportForm.tsx index e7ddbe33..e392c381 100644 --- a/web/src/Pages/Support/SupportForm/supportForm.tsx +++ b/web/src/Pages/Support/SupportForm/supportForm.tsx @@ -365,14 +365,14 @@ const SupportForm: React.FC = ({ method }) => {
{t(formTitle)}
- {!waitingForBE && isFirstRenderDone ? ( -
-
+ + {!waitingForBE && isFirstRenderDone ? ( +
{t('generalInfoTitle')}
{method !== 'create' && entId && ( @@ -805,11 +805,11 @@ const SupportForm: React.FC = ({ method }) => { )} - -
- ) : ( - - )} +
+ ) : ( + + )} +
); }; From 7277f25e91621b6899fc15536a5a3fb8d121c587 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:51:03 +0530 Subject: [PATCH 54/66] GWP read dependency updated --- web/src/Pages/Emissions/emissions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/Pages/Emissions/emissions.tsx b/web/src/Pages/Emissions/emissions.tsx index a2abf6a7..b8b458e4 100644 --- a/web/src/Pages/Emissions/emissions.tsx +++ b/web/src/Pages/Emissions/emissions.tsx @@ -135,7 +135,7 @@ const GhgEmissions = () => { tempTabItems.sort((a, b) => parseFloat(a.key) - parseFloat(b.key)); setTabItems(tempTabItems); - }, [availableReports, availableYears]); + }, [availableReports, availableYears, gwpSetting]); return (
From 4bcdd88c2198dfc53bf868b95f16ba91a86f9d5f Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:23:27 +0530 Subject: [PATCH 55/66] GWP Validation fix added --- .../services/src/activity/activity.service.ts | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/backend/services/src/activity/activity.service.ts b/backend/services/src/activity/activity.service.ts index 20e31a8a..6c305c65 100644 --- a/backend/services/src/activity/activity.service.ts +++ b/backend/services/src/activity/activity.service.ts @@ -129,25 +129,24 @@ export class ActivityService { if (activityDto.mitigationTimeline) { const gwpSettingsRecord = await this.configSettingsRepo.findOneBy({ id: ConfigurationSettingsType.GWP }); + + const gwpSetting = { + [GHGS.NO]: gwpSettingsRecord?.settingValue?.gwp_n20 ?? 1, + [GHGS.CH]: gwpSettingsRecord?.settingValue?.gwp_ch4 ?? 1, + } + let validUnit: GHGS = GHGS.CO; let gwpValue: number = 1; - if (gwpSettingsRecord) { - const gwpSettings = gwpSettingsRecord.settingValue; - switch (activityDto.ghgsAffected) { - case GHGS.NO: - validUnit = gwpSettings.gwp_n2o > 1 ? GHGS.NO : GHGS.CO; - gwpValue = gwpSettings.gwp_n2o > 1 ? gwpSettings.gwp_n2o : 1; - break; - case GHGS.CH: - validUnit = gwpSettings.gwp_ch4 > 1 ? GHGS.CH : GHGS.CO; - gwpValue = gwpSettings.gwp_ch4 > 1 ? gwpSettings.gwp_ch4 : 1; - break; - default: - validUnit = GHGS.CO; - gwpValue = 1; - break; - } + switch (activityDto.ghgsAffected) { + case GHGS.NO: + validUnit = GHGS.NO; + gwpValue = gwpSetting[GHGS.NO]; + break; + case GHGS.CH: + validUnit = GHGS.CH; + gwpValue = gwpSetting[GHGS.CH]; + break; } if (!activityDto.mitigationTimeline.startYear) { From 23455a2260a33e578623c483c4731c2d3665052c Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:30:20 +0530 Subject: [PATCH 56/66] Bar chart color refined --- web/src/Components/Charts/BarChart/barChart.tsx | 4 ++-- web/src/Components/Charts/PieChart/pieChart.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/Components/Charts/BarChart/barChart.tsx b/web/src/Components/Charts/BarChart/barChart.tsx index b2ea6aa7..ac70b268 100644 --- a/web/src/Components/Charts/BarChart/barChart.tsx +++ b/web/src/Components/Charts/BarChart/barChart.tsx @@ -20,7 +20,7 @@ const BarChart: React.FC = ({ chart, t, chartHeight }) => { useEffect(() => { const tempChartColorMapping: string[] = [1, 2, 5, 6].includes(chart.chartId) ? chartColorMappings.sectors - : chart.chartId === 5 + : chart.chartId === 3 ? chartColorMappings.support : chartColorMappings.finance; @@ -47,7 +47,6 @@ const BarChart: React.FC = ({ chart, t, chartHeight }) => { bar: { distributed: true, colors: { - backgroundBarColors: chartColorMapping, backgroundBarOpacity: 0, }, dataLabels: { @@ -55,6 +54,7 @@ const BarChart: React.FC = ({ chart, t, chartHeight }) => { }, }, }, + colors: chartColorMapping, dataLabels: { enabled: true, offsetY: -25, diff --git a/web/src/Components/Charts/PieChart/pieChart.tsx b/web/src/Components/Charts/PieChart/pieChart.tsx index a3e6e1c7..c9fa0d89 100644 --- a/web/src/Components/Charts/PieChart/pieChart.tsx +++ b/web/src/Components/Charts/PieChart/pieChart.tsx @@ -34,7 +34,7 @@ const PieChart: React.FC = ({ chart, t, chartWidth }) => { useEffect(() => { const tempChartColorMapping: string[] = [1, 2, 5, 6].includes(chart.chartId) ? chartColorMappings.sectors - : chart.chartId === 5 + : chart.chartId === 3 ? chartColorMappings.support : chartColorMappings.finance; From b12cbb206ddfdf9fc492a4ff0498269036cedd08 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 10:10:39 +0530 Subject: [PATCH 57/66] MTG Timeline Validation Fix --- .../services/src/activity/activity.service.ts | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/backend/services/src/activity/activity.service.ts b/backend/services/src/activity/activity.service.ts index 6c305c65..52f0dce6 100644 --- a/backend/services/src/activity/activity.service.ts +++ b/backend/services/src/activity/activity.service.ts @@ -131,8 +131,8 @@ export class ActivityService { const gwpSettingsRecord = await this.configSettingsRepo.findOneBy({ id: ConfigurationSettingsType.GWP }); const gwpSetting = { - [GHGS.NO]: gwpSettingsRecord?.settingValue?.gwp_n20 ?? 1, - [GHGS.CH]: gwpSettingsRecord?.settingValue?.gwp_ch4 ?? 1, + [GHGS.NO]: parseFloat(gwpSettingsRecord?.settingValue?.gwp_n2o) ?? 1, + [GHGS.CH]: parseFloat(gwpSettingsRecord?.settingValue?.gwp_ch4) ?? 1, } let validUnit: GHGS = GHGS.CO; @@ -1473,22 +1473,23 @@ export class ActivityService { } const currentMitigationTimeline = activity.mitigationTimeline; + const gwpSettingsRecord = await this.configSettingsRepo.findOneBy({ id: ConfigurationSettingsType.GWP }); + + const gwpSetting = { + [GHGS.NO]: parseFloat(gwpSettingsRecord?.settingValue?.gwp_n2o) ?? 1, + [GHGS.CH]: parseFloat(gwpSettingsRecord?.settingValue?.gwp_ch4) ?? 1, + } + let gwpValue: number = 1; - if (gwpSettingsRecord) { - const gwpSettings = gwpSettingsRecord.settingValue; - switch (currentMitigationTimeline.unit) { - case GHGS.NO: - gwpValue = gwpSettings.gwp_n2o > 1 ? gwpSettings.gwp_n2o : 1; - break; - case GHGS.CH: - gwpValue = gwpSettings.gwp_ch4 > 1 ? gwpSettings.gwp_ch4 : 1; - break; - default: - gwpValue = 1; - break; - } + switch (currentMitigationTimeline.unit) { + case GHGS.NO: + gwpValue = gwpSetting[GHGS.NO]; + break; + case GHGS.CH: + gwpValue = gwpSetting[GHGS.CH]; + break; } this.payloadValidator.validateMitigationTimelinePayload(mitigationTimelineDto, gwpValue, currentMitigationTimeline.startYear); From 29f241cfd3d8c89c4c7901e12d5d4727dd0206ea Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:26:08 +0530 Subject: [PATCH 58/66] Docker Compose init refinement --- docker-compose-image.yml | 38 ++++++++++++++------------------------ docker-compose.yml | 22 ---------------------- web/.env-cmdrc | 8 -------- 3 files changed, 14 insertions(+), 54 deletions(-) delete mode 100644 web/.env-cmdrc diff --git a/docker-compose-image.yml b/docker-compose-image.yml index 4913692c..244404c3 100644 --- a/docker-compose-image.yml +++ b/docker-compose-image.yml @@ -16,16 +16,24 @@ services: POSTGRES_USER: root PGPORT: 5433 PSQL_USERNAME: root - # POSTGRES_HOST_AUTH_METHOD: trust PGDATA: /data/postgres volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql - data:/data/postgres + migration: + image: 302213478610.dkr.ecr.us-east-1.amazonaws.com/transparency-services:CARBON-329 + depends_on: + - dbmrv + - national + command: ['yarn', 'migration:run'] + environment: + DB_HOST: dbmrv + DB_PORT: "5433" + DB_USER: root + DB_PASSWORD: "" + DB_NAME: "carbondev" national: image: 302213478610.dkr.ecr.us-east-1.amazonaws.com/transparency-services:CARBON-329 - # build: - # context: . - # dockerfile: ./backend/services/Dockerfile ports: - "9000:3000" depends_on: @@ -34,6 +42,8 @@ services: DB_HOST: dbmrv DB_USER: root DB_PASSWORD: "" + DB_PORT: "5433" + DB_NAME: "carbondev" RUN_MODULE: national-api rootEmail: systemCountryCode: "NG" @@ -45,8 +55,6 @@ services: HOST: "http://localhost:80" DOMAIN_MAP: "true" EXPIRES_IN: "7200" - DB_PORT: "5433" - DB_NAME: "carbondev" NODE_ENV: 'dev' FILE_SERVICE: local DISABLE_LOW_PRIORITY_EMAIL: "true" @@ -61,9 +69,6 @@ services: - ./users.csv:/app/backend/services/users.csv - ./organisations.csv:/app/backend/services/organisations.csv stats: - # build: - # context: . - # dockerfile: ./backend/services/Dockerfile image: 302213478610.dkr.ecr.us-east-1.amazonaws.com/transparency-services:CARBON-329 ports: - "9100:3100" @@ -83,9 +88,6 @@ services: FILE_SERVICE: local async-operations-handler: image: 302213478610.dkr.ecr.us-east-1.amazonaws.com/transparency-services:CARBON-329 - # build: - # context: . - # dockerfile: ./backend/services/Dockerfile depends_on: - dbmrv - national @@ -93,7 +95,6 @@ services: DB_HOST: dbmrv DB_USER: root DB_PASSWORD: "" - # ,data-importer RUN_MODULE: async-operations-handler,data-importer LOCATION_SERVICE: https://mrv-common-dev.s3.amazonaws.com/flag.png CERTIFIER_IMAGE : "https://mrv-common-dev.s3.amazonaws.com/flag.png" @@ -118,17 +119,6 @@ services: BACKEND_HOST: http://localhost:9000 web: image: 302213478610.dkr.ecr.us-east-1.amazonaws.com/transparency-web:CARBON-329 - # build: - # context: . - # dockerfile: ./web/Dockerfile - # args: - # PORT: 3030 - # REACT_APP_BACKEND: http://localhost:3000 - # REACT_APP_STAT_URL: http://localhost:3100 - # REACT_APP_GOVERNMENT_MINISTRY: "Ministry Of Environment" - # REACT_APP_COUNTRY_NAME: "Antarctic Region" - # REACT_APP_COUNTRY_FLAG_URL: "https://mrv-common-dev.s3.amazonaws.com/flag.png" - # COUNTRY_CODE: "NG" ports: - "9030:3030" depends_on: diff --git a/docker-compose.yml b/docker-compose.yml index 7ec6f494..6d1b9685 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,6 @@ services: POSTGRES_USER: root PGPORT: 5433 PSQL_USERNAME: root - # POSTGRES_HOST_AUTH_METHOD: trust PGDATA: /data/postgres volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql @@ -105,7 +104,6 @@ services: DB_HOST: dbmrv DB_USER: root DB_PASSWORD: "" - # ,data-importer RUN_MODULE: async-operations-handler,data-importer LOCATION_SERVICE: https://mrv-common-dev.s3.amazonaws.com/flag.png CERTIFIER_IMAGE : "https://mrv-common-dev.s3.amazonaws.com/flag.png" @@ -128,21 +126,6 @@ services: SYSTEM_TYPE: CARBON_TRANSPARENCY_SYSTEM SYNC_ENABLE: true BACKEND_HOST: http://localhost:9000 - # async-operations-handler: - # build: - # context: . - # dockerfile: ./backend/services/Dockerfile - # depends_on: - # - db - # - national - # environment: - # DB_HOST: db - # DB_USER: root - # DB_PASSWORD: "" - # RUN_MODULE: async-operations-handler - # SMTP_ENDPOINT: email-smtp.us-east-1.amazonaws.com - # SMTP_PASSWORD: "" - # ASYNC_OPERATIONS_TYPE: Database web: build: context: . @@ -152,11 +135,6 @@ services: REACT_APP_BACKEND: http://localhost:9000 REACT_APP_STAT_URL: http://localhost:9100 REACT_APP_COUNTRY_NAME: "Antarctic Region" - REACT_APP_GOVERNMENT_MINISTRY: "Ministry Of Environment" - REACT_APP_COUNTRY_FLAG_URL: "https://mrv-common-dev.s3.amazonaws.com/flag.png" - COUNTRY_CODE: "NG" - REACT_APP_MAP_TYPE: "None" - REACT_APP_ENABLE_REGISTRATION: true ports: - "9030:3030" depends_on: diff --git a/web/.env-cmdrc b/web/.env-cmdrc deleted file mode 100644 index 799423ed..00000000 --- a/web/.env-cmdrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "development": { - "REACT_APP_BACKEND": "https://api.carbreg.org", - "REACT_APP_MAP_TYPE": "Mapbox", - "REACT_APP_MAXIMUM_FILE_SIZE": 5000000, - "REACT_APP_GOVERNMENT_MINISTRY": "Ministry Of Environment" - } -} From ac86f6c4dd1f3b0bf39243924f429fbcdc46ca8d Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:31:34 +0530 Subject: [PATCH 59/66] Support Validation Issue fix --- .../services/src/support/support.service.ts | 93 +++++++++++++++---- .../services/src/util/linkUnlink.service.ts | 50 +++++++--- 2 files changed, 111 insertions(+), 32 deletions(-) diff --git a/backend/services/src/support/support.service.ts b/backend/services/src/support/support.service.ts index dc2f9d90..bf683766 100644 --- a/backend/services/src/support/support.service.ts +++ b/backend/services/src/support/support.service.ts @@ -21,6 +21,7 @@ import { ActivityEntity } from "../entities/activity.entity"; import { DeleteDto } from "../dtos/delete.dto"; import { Role } from "../casl/role.enum"; import { LinkUnlinkService } from "../util/linkUnlink.service"; +import { ActionEntity } from "src/entities/action.entity"; @Injectable() export class SupportService { @@ -69,6 +70,11 @@ export class SupportService { parentProject = await this.activityService.isProjectValid(activity.parentId, user); } + let parentAction: ActionEntity; + if (activity.parentType == EntityType.ACTION) { + parentAction = await this.activityService.isActionValid(activity.parentId, user); + } + support.requiredAmountDomestic = this.helperService.roundToTwoDecimals(support.requiredAmount / support.exchangeRate); support.receivedAmountDomestic = this.helperService.roundToTwoDecimals(support.receivedAmount / support.exchangeRate); support.sector = activity.sector; @@ -83,7 +89,8 @@ export class SupportService { .transaction(async (em) => { const savedSupport = await em.save(support); if (savedSupport) { - let unvalidateTree = false; + let unvalidateActionTree = false; + let unvalidateProjectTree = false; if (activity.validated) { activity.validated = false; @@ -95,7 +102,7 @@ export class SupportService { 0, support.supportId ); - await em.save(activity); + await em.update(ActivityEntity, activity.activityId, { validated: false }); if (activitySupports && activitySupports.length > 0) { const supportsList = [] @@ -118,18 +125,35 @@ export class SupportService { 0, activity.activityId ); - unvalidateTree = true; - await em.save(parentProject); + unvalidateProjectTree = true; + await em.update(ProjectEntity, parentProject.projectId, { validated: false }); + } else if (parentAction && parentAction.validated) { + parentAction.validated = false; + this.addEventLogEntry( + eventLog, + LogEventType.ACTION_UNVERIFIED_DUE_LINKED_ENTITY_UPDATE, + EntityType.ACTION, + parentAction.actionId, + 0, + activity.activityId + ); + unvalidateActionTree = true; + await em.update(ActionEntity, parentAction.actionId, { validated: false }); } } await em.save(eventLog); - if (unvalidateTree) { + if (unvalidateProjectTree) { await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByProject(parentProject, em, true, [activity.activityId]); - } else { - await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [support.supportId]); } + + if (unvalidateActionTree) { + await this.linkUnlinkService.updateAllValidatedChildrenStatusByActionId(parentAction.actionId, em); + } + + await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [support.supportId]); + } return savedSupport; }) @@ -229,7 +253,9 @@ export class SupportService { const activityList: ActivityEntity[] = []; const projectList: ProjectEntity[] = []; + const actionList: ActionEntity[] = []; + const updatedActionIds = []; const updatedProjectIds = []; const updatedActivityIds = []; @@ -262,6 +288,21 @@ export class SupportService { projectList.push(currentParentProject); updatedProjectIds.push(currentParentProject.projectId); } + } else if (currentActivity.parentType == EntityType.ACTION) { + const currentParentAction = await this.activityService.isActionValid(currentSupport.activity.parentId, user); + if (currentParentAction.validated) { + currentParentAction.validated = false; + this.addEventLogEntry( + eventLog, + LogEventType.ACTION_UNVERIFIED_DUE_LINKED_ENTITY_UPDATE, + EntityType.ACTION, + currentParentAction.actionId, + 0, + currentSupport.supportId + ); + actionList.push(currentParentAction); + updatedActionIds.push(currentParentAction.actionId); + } } } @@ -325,6 +366,21 @@ export class SupportService { projectList.push(newParentProject); updatedProjectIds.push(newParentProject.projectId); } + } else if (activity.parentType == EntityType.ACTION) { + const newParentAction = await this.activityService.isActionValid(activity.parentId, user); + if (newParentAction.validated) { + newParentAction.validated = false; + this.addEventLogEntry( + eventLog, + LogEventType.ACTION_UNVERIFIED_DUE_LINKED_ENTITY_UPDATE, + EntityType.ACTION, + newParentAction.actionId, + 0, + activity.activityId + ); + actionList.push(newParentAction); + updatedActionIds.push(newParentAction.actionId); + } } } @@ -354,27 +410,24 @@ export class SupportService { await em.save(activityList) } - if (projectList && projectList.length > 0) { - await em.save(projectList) - } - await em.save(eventLog); if (projectList && projectList.length > 0) { + await em.save(projectList) for (const project of projectList) { await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByProject(project, em, true, updatedActivityIds); } - for (const activity of activityList) { - await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [currentSupport.supportId]); + } else if (actionList && actionList.length > 0) { + await em.save(actionList) + for (const action of actionList) { + await this.linkUnlinkService.updateAllValidatedChildrenStatusByActionId(action.actionId, em); } - } else { - for (const activity of activityList) { - await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [currentSupport.supportId]); - } - - } - + } + for (const activity of activityList) { + await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [currentSupport.supportId]); + } + } return savedSupport; }) diff --git a/backend/services/src/util/linkUnlink.service.ts b/backend/services/src/util/linkUnlink.service.ts index f41e7450..1da1ce32 100644 --- a/backend/services/src/util/linkUnlink.service.ts +++ b/backend/services/src/util/linkUnlink.service.ts @@ -1025,7 +1025,7 @@ export class LinkUnlinkService { .transaction(async (em) => { const logs = []; - const programmes = [] + const programmeIdsToUnvalidate = [] for (const programme of programmeChildren) { // unvalidate programme if (programme.validated) { @@ -1038,12 +1038,18 @@ export class LinkUnlinkService { actionId) ) } - programmes.push(programme) + programmeIdsToUnvalidate.push(programme.programmeId) } - await em.save(programmes); + if (programmeIdsToUnvalidate.length > 0) { + await em.createQueryBuilder() + .update(ProgrammeEntity) + .set({ validated: false }) + .whereInIds(programmeIdsToUnvalidate) + .execute(); + } - const projects = [] + const projectIdsToUnvalidate = [] for (const project of projectChildren) { // unvalidate project if (project.validated) { @@ -1057,12 +1063,18 @@ export class LinkUnlinkService { ) } - projects.push(project) + projectIdsToUnvalidate.push(project) } - await em.save(projects); + if (projectIdsToUnvalidate.length > 0) { + await em.createQueryBuilder() + .update(ProjectEntity) + .set({ validated: false }) + .whereInIds(projectIdsToUnvalidate) + .execute(); + } - const activities = [] + const activityIdsToUnvalidate = [] for (const activity of activityChildren) { // unvalidate activity if (activity.validated) { @@ -1075,20 +1087,34 @@ export class LinkUnlinkService { actionId) ) } - activities.push(activity) + activityIdsToUnvalidate.push(activity) if (activity.support && activity.support.length > 0) { - const supports = [] + const supportIdsToUnvalidate = [] for (const support of activity.support) { support.validated = false; - supports.push(support) + supportIdsToUnvalidate.push(support.supportId); + } + + if (supportIdsToUnvalidate.length > 0) { + await em.createQueryBuilder() + .update(SupportEntity) + .set({ validated: false }) + .whereInIds(supportIdsToUnvalidate) + .execute(); } - await em.save(supports); } } - await em.save(activities); + if (activityIdsToUnvalidate.length > 0) { + await em.createQueryBuilder() + .update(ActivityEntity) + .set({ validated: false }) + .whereInIds(activityIdsToUnvalidate) + .execute(); + } + await em.save(logs); }); From 562cd4cf762efb444f958f84f2d9910e516d0a09 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:36:17 +0530 Subject: [PATCH 60/66] Id issue refined --- backend/services/src/util/linkUnlink.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/services/src/util/linkUnlink.service.ts b/backend/services/src/util/linkUnlink.service.ts index 1da1ce32..b37c67d2 100644 --- a/backend/services/src/util/linkUnlink.service.ts +++ b/backend/services/src/util/linkUnlink.service.ts @@ -1038,7 +1038,7 @@ export class LinkUnlinkService { actionId) ) } - programmeIdsToUnvalidate.push(programme.programmeId) + programmeIdsToUnvalidate.push(programme.programmeId); } if (programmeIdsToUnvalidate.length > 0) { @@ -1063,7 +1063,7 @@ export class LinkUnlinkService { ) } - projectIdsToUnvalidate.push(project) + projectIdsToUnvalidate.push(project.projectId); } if (projectIdsToUnvalidate.length > 0) { @@ -1087,7 +1087,7 @@ export class LinkUnlinkService { actionId) ) } - activityIdsToUnvalidate.push(activity) + activityIdsToUnvalidate.push(activity.activityId); if (activity.support && activity.support.length > 0) { const supportIdsToUnvalidate = [] From e1e51269ed623fd22bffcc70977e00b87196f048 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:48:46 +0530 Subject: [PATCH 61/66] Unvalidate loop refined in the Support create --- backend/services/src/support/support.service.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/backend/services/src/support/support.service.ts b/backend/services/src/support/support.service.ts index bf683766..548c5746 100644 --- a/backend/services/src/support/support.service.ts +++ b/backend/services/src/support/support.service.ts @@ -146,14 +146,12 @@ export class SupportService { if (unvalidateProjectTree) { await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByProject(parentProject, em, true, [activity.activityId]); - } - - if (unvalidateActionTree) { + } else if (unvalidateActionTree) { await this.linkUnlinkService.updateAllValidatedChildrenStatusByActionId(parentAction.actionId, em); + } else { + await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [support.supportId]); } - - await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [support.supportId]); - + } return savedSupport; }) From 8bd744c12ab6c3427685fe3343201cdbcf4ccd1a Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:22:31 +0530 Subject: [PATCH 62/66] Program invalidation added for support create and edit --- .../services/src/support/support.service.ts | 66 +++++++++++++++++-- .../services/src/util/linkUnlink.service.ts | 38 ++++++++--- 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/backend/services/src/support/support.service.ts b/backend/services/src/support/support.service.ts index 548c5746..3bf881dc 100644 --- a/backend/services/src/support/support.service.ts +++ b/backend/services/src/support/support.service.ts @@ -22,6 +22,7 @@ import { DeleteDto } from "../dtos/delete.dto"; import { Role } from "../casl/role.enum"; import { LinkUnlinkService } from "../util/linkUnlink.service"; import { ActionEntity } from "src/entities/action.entity"; +import { ProgrammeEntity } from "src/entities/programme.entity"; @Injectable() export class SupportService { @@ -70,6 +71,11 @@ export class SupportService { parentProject = await this.activityService.isProjectValid(activity.parentId, user); } + let parentProgramme: ProgrammeEntity; + if (activity.parentType == EntityType.PROGRAMME) { + parentProgramme = await this.activityService.isProgrammeValid(activity.parentId, user); + } + let parentAction: ActionEntity; if (activity.parentType == EntityType.ACTION) { parentAction = await this.activityService.isActionValid(activity.parentId, user); @@ -90,6 +96,7 @@ export class SupportService { const savedSupport = await em.save(support); if (savedSupport) { let unvalidateActionTree = false; + let unvalidateProgrammeTree = false; let unvalidateProjectTree = false; if (activity.validated) { @@ -127,6 +134,18 @@ export class SupportService { ); unvalidateProjectTree = true; await em.update(ProjectEntity, parentProject.projectId, { validated: false }); + } else if (parentProgramme && parentProgramme.validated) { + parentProgramme.validated = false; + this.addEventLogEntry( + eventLog, + LogEventType.PROGRAMME_UNVERIFIED_DUE_LINKED_ENTITY_UPDATE, + EntityType.PROGRAMME, + parentProgramme.programmeId, + 0, + activity.activityId + ); + unvalidateProgrammeTree = true; + await em.update(ProgrammeEntity, parentProgramme.programmeId, { validated: false }); } else if (parentAction && parentAction.validated) { parentAction.validated = false; this.addEventLogEntry( @@ -146,6 +165,8 @@ export class SupportService { if (unvalidateProjectTree) { await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByProject(parentProject, em, true, [activity.activityId]); + } else if (unvalidateProgrammeTree) { + await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByProgrammeId(parentProgramme, em, false); } else if (unvalidateActionTree) { await this.linkUnlinkService.updateAllValidatedChildrenStatusByActionId(parentAction.actionId, em); } else { @@ -251,9 +272,11 @@ export class SupportService { const activityList: ActivityEntity[] = []; const projectList: ProjectEntity[] = []; + const programmeList: ProgrammeEntity[] = []; const actionList: ActionEntity[] = []; const updatedActionIds = []; + const updatedProgrammeIds = []; const updatedProjectIds = []; const updatedActivityIds = []; @@ -286,6 +309,21 @@ export class SupportService { projectList.push(currentParentProject); updatedProjectIds.push(currentParentProject.projectId); } + } else if (currentActivity.parentType == EntityType.PROGRAMME) { + const currentParentProgramme = await this.activityService.isProgrammeValid(currentSupport.activity.parentId, user); + if (currentParentProgramme.validated) { + currentParentProgramme.validated = false; + this.addEventLogEntry( + eventLog, + LogEventType.PROGRAMME_UNVERIFIED_DUE_LINKED_ENTITY_UPDATE, + EntityType.PROGRAMME, + currentParentProgramme.programmeId, + 0, + currentSupport.supportId + ); + programmeList.push(currentParentProgramme); + updatedProgrammeIds.push(currentParentProgramme.programmeId); + } } else if (currentActivity.parentType == EntityType.ACTION) { const currentParentAction = await this.activityService.isActionValid(currentSupport.activity.parentId, user); if (currentParentAction.validated) { @@ -364,6 +402,21 @@ export class SupportService { projectList.push(newParentProject); updatedProjectIds.push(newParentProject.projectId); } + } else if (activity.parentType == EntityType.PROGRAMME) { + const newParentProgramme = await this.activityService.isProgrammeValid(activity.parentId, user); + if (newParentProgramme.validated) { + newParentProgramme.validated = false; + this.addEventLogEntry( + eventLog, + LogEventType.PROGRAMME_UNVERIFIED_DUE_LINKED_ENTITY_UPDATE, + EntityType.PROGRAMME, + newParentProgramme.programmeId, + 0, + activity.activityId + ); + programmeList.push(newParentProgramme); + updatedProgrammeIds.push(newParentProgramme.programmeId); + } } else if (activity.parentType == EntityType.ACTION) { const newParentAction = await this.activityService.isActionValid(activity.parentId, user); if (newParentAction.validated) { @@ -415,15 +468,20 @@ export class SupportService { for (const project of projectList) { await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByProject(project, em, true, updatedActivityIds); } + } else if (programmeList && programmeList.length > 0) { + await em.save(programmeList) + for (const programme of programmeList) { + await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByProgrammeId(programme, em, false); + } } else if (actionList && actionList.length > 0) { await em.save(actionList) for (const action of actionList) { await this.linkUnlinkService.updateAllValidatedChildrenStatusByActionId(action.actionId, em); } - } - - for (const activity of activityList) { - await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [currentSupport.supportId]); + } else { + for (const activity of activityList) { + await this.linkUnlinkService.updateAllValidatedChildrenAndParentStatusByActivityId(activity.activityId, em, [currentSupport.supportId]); + } } } diff --git a/backend/services/src/util/linkUnlink.service.ts b/backend/services/src/util/linkUnlink.service.ts index b37c67d2..56e65da6 100644 --- a/backend/services/src/util/linkUnlink.service.ts +++ b/backend/services/src/util/linkUnlink.service.ts @@ -1163,12 +1163,12 @@ export class LinkUnlinkService { 0, programme.programmeId) ); - await em.save(action) + await em.update(ActionEntity, action.actionId, { validated: false }); await this.updateAllValidatedChildrenStatusByActionId(action.actionId, em, [programme.programmeId], excludeProjectIds, excludeActivityIds); } else { - const projects = [] + const projectIdsToUnvalidate = [] for (const project of projectChildren) { project.validated = false; logs.push(this.buildLogEntity( @@ -1178,12 +1178,18 @@ export class LinkUnlinkService { 0, programme.programmeId) ); - projects.push(project) + projectIdsToUnvalidate.push(project.projectId); } - await em.save(projects); + if (projectIdsToUnvalidate.length > 0) { + await em.createQueryBuilder() + .update(ProjectEntity) + .set({ validated: false }) + .whereInIds(projectIdsToUnvalidate) + .execute(); + } - const activities = [] + const activityIdsToUnvalidate = [] for (const activity of activityChildren) { activity.validated = false; @@ -1194,10 +1200,10 @@ export class LinkUnlinkService { 0, programme.programmeId) ); - activities.push(activity) + activityIdsToUnvalidate.push(activity.activityId); if (activity.support && activity.support.length > 0) { - const supports = [] + const supportIdsToUnvalidate = [] for (const support of activity.support) { support.validated = false; @@ -1208,14 +1214,26 @@ export class LinkUnlinkService { 0, programme.programmeId) ); - supports.push(support) + supportIdsToUnvalidate.push(support.supportId); } - await em.save(supports); + if (supportIdsToUnvalidate.length > 0) { + await em.createQueryBuilder() + .update(SupportEntity) + .set({ validated: false }) + .whereInIds(supportIdsToUnvalidate) + .execute(); + } } } - await em.save(activities); + if (activityIdsToUnvalidate.length > 0) { + await em.createQueryBuilder() + .update(ActivityEntity) + .set({ validated: false }) + .whereInIds(activityIdsToUnvalidate) + .execute(); + } } From c03c511b1f4ec6f3a45d2fd799366ca710526160 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:06:02 +0530 Subject: [PATCH 63/66] Activity validation error message refined --- backend/services/src/activity/activity.service.ts | 2 +- backend/services/src/i18n/en/activity.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/services/src/activity/activity.service.ts b/backend/services/src/activity/activity.service.ts index 52f0dce6..00470bfe 100644 --- a/backend/services/src/activity/activity.service.ts +++ b/backend/services/src/activity/activity.service.ts @@ -1334,7 +1334,7 @@ export class ActivityService { throw new HttpException( this.helperService.formatReqMessagesString( "activity.parentNotValidated", - [] + [activity.parentId] ), HttpStatus.FORBIDDEN ); diff --git a/backend/services/src/i18n/en/activity.json b/backend/services/src/i18n/en/activity.json index 38c4b674..8a3b4314 100644 --- a/backend/services/src/i18n/en/activity.json +++ b/backend/services/src/i18n/en/activity.json @@ -29,6 +29,6 @@ "permissionDeniedForSector": "User does not have the permission for the sector", "deleteActivitySuccess": "Activity deleted successfully", "activityDeletionFailed": "Activity delete failed due to {}", - "parentNotValidated": "Parent entity should be validated.", + "parentNotValidated": "Parent entity with id {} should be validated.", "programmesLinkedToAction": "Cannot attach activity to the action with id : {} as it has programmes attached" } \ No newline at end of file From 92332785d15943c2e9fc2dd441cd431de9900cf0 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:22:25 +0530 Subject: [PATCH 64/66] Parent Type added to the validation warning --- backend/services/src/activity/activity.service.ts | 2 +- backend/services/src/i18n/en/activity.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/services/src/activity/activity.service.ts b/backend/services/src/activity/activity.service.ts index 00470bfe..aa9c31ce 100644 --- a/backend/services/src/activity/activity.service.ts +++ b/backend/services/src/activity/activity.service.ts @@ -1334,7 +1334,7 @@ export class ActivityService { throw new HttpException( this.helperService.formatReqMessagesString( "activity.parentNotValidated", - [activity.parentId] + [activity.parentType, activity.parentId] ), HttpStatus.FORBIDDEN ); diff --git a/backend/services/src/i18n/en/activity.json b/backend/services/src/i18n/en/activity.json index 8a3b4314..0f4f74dc 100644 --- a/backend/services/src/i18n/en/activity.json +++ b/backend/services/src/i18n/en/activity.json @@ -29,6 +29,6 @@ "permissionDeniedForSector": "User does not have the permission for the sector", "deleteActivitySuccess": "Activity deleted successfully", "activityDeletionFailed": "Activity delete failed due to {}", - "parentNotValidated": "Parent entity with id {} should be validated.", + "parentNotValidated": "Parent {} with id {} should be validated.", "programmesLinkedToAction": "Cannot attach activity to the action with id : {} as it has programmes attached" } \ No newline at end of file From 24a63901e03f286f1468eee6dca8fd57040cd4d3 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:44:39 +0530 Subject: [PATCH 65/66] Validation Button Spinner Loader added --- web/src/Pages/Actions/ActionForm/actionForm.tsx | 6 ++++++ web/src/Pages/Activities/ActivityForm/activityForm.tsx | 6 ++++++ web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx | 6 ++++++ web/src/Pages/Projects/ProjectForm/projectForm.tsx | 6 ++++++ web/src/Pages/Support/SupportForm/supportForm.tsx | 6 ++++++ 5 files changed, 30 insertions(+) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index 7027d197..2a11f7b8 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -102,6 +102,7 @@ const actionForm: React.FC = ({ method }) => { // Spinner For Form Submit const [waitingForBE, setWaitingForBE] = useState(false); + const [waitingForValidation, setWaitingForValidation] = useState(false); // Programme state @@ -270,6 +271,8 @@ const actionForm: React.FC = ({ method }) => { const validateEntity = async () => { try { + setWaitingForValidation(true); + if (entId) { const payload = { entityId: entId, @@ -297,6 +300,8 @@ const actionForm: React.FC = ({ method }) => { } else { displayErrorMessage(error, `${entId} Validation Failed`); } + } finally { + setIsValidationAllowed(false); } }; @@ -1232,6 +1237,7 @@ const actionForm: React.FC = ({ method }) => { onClick={() => { validateEntity(); }} + loading={waitingForValidation} disabled={!isValidationAllowed} > {isValidated ? t('entityAction:unvalidate') : t('entityAction:validate')} diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index 2392bc9e..de944d9f 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -117,6 +117,7 @@ const ActivityForm: React.FC = ({ method }) => { // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); + const [waitingForValidation, setWaitingForValidation] = useState(false); // Methodology Doc state @@ -252,6 +253,8 @@ const ActivityForm: React.FC = ({ method }) => { const validateEntity = async () => { try { + setWaitingForValidation(true); + if (entId) { const payload = { entityId: entId, @@ -279,6 +282,8 @@ const ActivityForm: React.FC = ({ method }) => { } else { displayErrorMessage(error, `${entId} Validation Failed`); } + } finally { + setIsValidationAllowed(false); } }; @@ -1933,6 +1938,7 @@ const ActivityForm: React.FC = ({ method }) => { onClick={() => { validateEntity(); }} + loading={waitingForValidation} disabled={!isValidationAllowed} > {isValidated ? t('entityAction:unvalidate') : t('entityAction:validate')} diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 37b33678..23371e53 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -101,6 +101,7 @@ const ProgrammeForm: React.FC = ({ method }) => { // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); + const [waitingForValidation, setWaitingForValidation] = useState(false); // Project Attachment state @@ -271,6 +272,8 @@ const ProgrammeForm: React.FC = ({ method }) => { const validateEntity = async () => { try { + setWaitingForValidation(true); + if (entId) { const payload = { entityId: entId, @@ -298,6 +301,8 @@ const ProgrammeForm: React.FC = ({ method }) => { } else { displayErrorMessage(error, `${entId} Validation Failed`); } + } finally { + setIsValidationAllowed(false); } }; @@ -1383,6 +1388,7 @@ const ProgrammeForm: React.FC = ({ method }) => { onClick={() => { validateEntity(); }} + loading={waitingForValidation} disabled={!isValidationAllowed} > {isValidated ? t('entityAction:unvalidate') : t('entityAction:validate')} diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.tsx b/web/src/Pages/Projects/ProjectForm/projectForm.tsx index 860457d4..7d97318e 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.tsx +++ b/web/src/Pages/Projects/ProjectForm/projectForm.tsx @@ -102,6 +102,7 @@ const ProjectForm: React.FC = ({ method }) => { // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); + const [waitingForValidation, setWaitingForValidation] = useState(false); // Activity State @@ -270,6 +271,8 @@ const ProjectForm: React.FC = ({ method }) => { const validateEntity = async () => { try { + setWaitingForValidation(true); + if (entId) { const payload = { entityId: entId, @@ -297,6 +300,8 @@ const ProjectForm: React.FC = ({ method }) => { } else { displayErrorMessage(error, `${entId} Validation Failed`); } + } finally { + setIsValidationAllowed(false); } }; @@ -1521,6 +1526,7 @@ const ProjectForm: React.FC = ({ method }) => { onClick={() => { validateEntity(); }} + loading={waitingForValidation} disabled={!isValidationAllowed} > {isValidated ? t('entityAction:unvalidate') : t('entityAction:validate')} diff --git a/web/src/Pages/Support/SupportForm/supportForm.tsx b/web/src/Pages/Support/SupportForm/supportForm.tsx index e392c381..f9bde420 100644 --- a/web/src/Pages/Support/SupportForm/supportForm.tsx +++ b/web/src/Pages/Support/SupportForm/supportForm.tsx @@ -76,6 +76,7 @@ const SupportForm: React.FC = ({ method }) => { // Spinner When Form Submit Occurs const [waitingForBE, setWaitingForBE] = useState(false); + const [waitingForValidation, setWaitingForValidation] = useState(false); // Popup Definition @@ -237,6 +238,8 @@ const SupportForm: React.FC = ({ method }) => { const validateEntity = async () => { try { + setWaitingForValidation(true); + if (entId) { const payload = { entityId: entId, @@ -264,6 +267,8 @@ const SupportForm: React.FC = ({ method }) => { } else { displayErrorMessage(error, `${entId} Validation Failed`); } + } finally { + setIsValidationAllowed(false); } }; @@ -751,6 +756,7 @@ const SupportForm: React.FC = ({ method }) => { onClick={() => { validateEntity(); }} + loading={waitingForValidation} disabled={!isValidationAllowed} > {isValidated ? t('entityAction:unvalidate') : t('entityAction:validate')} From 74a11acd78249e85eab456c39584256e25a5dd50 Mon Sep 17 00:00:00 2001 From: MeelanB <155341696+MeelanB@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:31:17 +0530 Subject: [PATCH 66/66] Validation Spinner exiting issue fixed --- web/src/Pages/Actions/ActionForm/actionForm.tsx | 2 +- web/src/Pages/Activities/ActivityForm/activityForm.tsx | 2 +- web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx | 2 +- web/src/Pages/Projects/ProjectForm/projectForm.tsx | 2 +- web/src/Pages/Support/SupportForm/supportForm.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/src/Pages/Actions/ActionForm/actionForm.tsx b/web/src/Pages/Actions/ActionForm/actionForm.tsx index 2a11f7b8..6fa170ca 100644 --- a/web/src/Pages/Actions/ActionForm/actionForm.tsx +++ b/web/src/Pages/Actions/ActionForm/actionForm.tsx @@ -301,7 +301,7 @@ const actionForm: React.FC = ({ method }) => { displayErrorMessage(error, `${entId} Validation Failed`); } } finally { - setIsValidationAllowed(false); + setWaitingForValidation(false); } }; diff --git a/web/src/Pages/Activities/ActivityForm/activityForm.tsx b/web/src/Pages/Activities/ActivityForm/activityForm.tsx index de944d9f..bdae31d9 100644 --- a/web/src/Pages/Activities/ActivityForm/activityForm.tsx +++ b/web/src/Pages/Activities/ActivityForm/activityForm.tsx @@ -283,7 +283,7 @@ const ActivityForm: React.FC = ({ method }) => { displayErrorMessage(error, `${entId} Validation Failed`); } } finally { - setIsValidationAllowed(false); + setWaitingForValidation(false); } }; diff --git a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx index 23371e53..7deb99f9 100644 --- a/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx +++ b/web/src/Pages/Programmes/ProgrammeForm/programmeForm.tsx @@ -302,7 +302,7 @@ const ProgrammeForm: React.FC = ({ method }) => { displayErrorMessage(error, `${entId} Validation Failed`); } } finally { - setIsValidationAllowed(false); + setWaitingForValidation(false); } }; diff --git a/web/src/Pages/Projects/ProjectForm/projectForm.tsx b/web/src/Pages/Projects/ProjectForm/projectForm.tsx index 7d97318e..aa3519f2 100644 --- a/web/src/Pages/Projects/ProjectForm/projectForm.tsx +++ b/web/src/Pages/Projects/ProjectForm/projectForm.tsx @@ -301,7 +301,7 @@ const ProjectForm: React.FC = ({ method }) => { displayErrorMessage(error, `${entId} Validation Failed`); } } finally { - setIsValidationAllowed(false); + setWaitingForValidation(false); } }; diff --git a/web/src/Pages/Support/SupportForm/supportForm.tsx b/web/src/Pages/Support/SupportForm/supportForm.tsx index f9bde420..00eb5a6f 100644 --- a/web/src/Pages/Support/SupportForm/supportForm.tsx +++ b/web/src/Pages/Support/SupportForm/supportForm.tsx @@ -268,7 +268,7 @@ const SupportForm: React.FC = ({ method }) => { displayErrorMessage(error, `${entId} Validation Failed`); } } finally { - setIsValidationAllowed(false); + setWaitingForValidation(false); } };