diff --git a/app/components/Analyst/CBC/History/CbcHistoryCommunitiesTable.tsx b/app/components/Analyst/CBC/History/CbcHistoryCommunitiesTable.tsx new file mode 100644 index 000000000..713bd61f9 --- /dev/null +++ b/app/components/Analyst/CBC/History/CbcHistoryCommunitiesTable.tsx @@ -0,0 +1,80 @@ +import styled from 'styled-components'; + +interface Props { + heading: string; + communities: any[]; +} + +const StyledCommunitiesContainer = styled.div` + display: flex; + align-items: center; +`; + +const StyledLeftContainer = styled.div` + padding-right: 2%; + width: 250px; +`; + +const StyledTable = styled.table` + th { + border: none; + } + tbody > tr { + border-bottom: thin dashed; + border-color: ${(props) => props.theme.color.borderGrey}; + td { + width: 200px; + max-width: 200px; + border: none; + } + } +`; + +const StyledIdCell = styled.td` + width: 100px !important; + max-width: 100px; +`; + +const CbcHistoryCommunitiesTable: React.FC = ({ + heading, + communities, +}) => { + return ( + + {heading} +
+ + + + Economic Region + Regional District + Geographic Name + Type + ID + + + + {communities?.map((community, index) => ( + + {community.economic_region} + {community.regional_district} + {community.bc_geographic_name} + {community.geographic_type} + + {community.communities_source_data_id} + + + ))} + + +
+
+ ); +}; + +export default CbcHistoryCommunitiesTable; diff --git a/app/components/Analyst/CBC/History/CbcHistoryContent.tsx b/app/components/Analyst/CBC/History/CbcHistoryContent.tsx index bc97587b9..d4936dfef 100644 --- a/app/components/Analyst/CBC/History/CbcHistoryContent.tsx +++ b/app/components/Analyst/CBC/History/CbcHistoryContent.tsx @@ -2,6 +2,7 @@ import HistoryDetails from 'components/Analyst/History/HistoryDetails'; import cbcData from 'formSchema/uiSchema/history/cbcData'; import { DateTime } from 'luxon'; import styled from 'styled-components'; +import CbcHistoryCommunitiesTable from './CbcHistoryCommunitiesTable'; const StyledContent = styled.span` display: flex; @@ -72,10 +73,23 @@ const HistoryContent = ({ 'updated_at', 'change_reason', 'cbc_data_id', + 'locations', ]} diffSchema={cbcData} overrideParent="cbcData" /> + {json?.locations?.added?.length > 0 && ( + + )} + {json?.locations?.removed?.length > 0 && ( + + )} {op === 'UPDATE' && changeReason !== '' && ( )} diff --git a/app/components/Analyst/CBC/History/CbcHistoryTable.tsx b/app/components/Analyst/CBC/History/CbcHistoryTable.tsx index fb7b92184..9e8041d39 100644 --- a/app/components/Analyst/CBC/History/CbcHistoryTable.tsx +++ b/app/components/Analyst/CBC/History/CbcHistoryTable.tsx @@ -65,6 +65,10 @@ const CbcHistoryTable: React.FC = ({ query }) => { json={{ ...historyItem.record?.json_data, project_number: historyItem.record?.project_number, + locations: { + added: historyItem.record?.added_communities, + removed: historyItem.record?.deleted_communities, + }, }} prevJson={{ ...historyItem.oldRecord?.json_data, diff --git a/app/pages/analyst/cbc/[cbcId].tsx b/app/pages/analyst/cbc/[cbcId].tsx index e2f2a480e..050cfcc8f 100644 --- a/app/pages/analyst/cbc/[cbcId].tsx +++ b/app/pages/analyst/cbc/[cbcId].tsx @@ -9,7 +9,7 @@ import CbcForm from 'components/Analyst/CBC/CbcForm'; import { ChangeModal } from 'components/Analyst'; import styled from 'styled-components'; import ReviewTheme from 'components/Review/ReviewTheme'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useUpdateCbcDataAndInsertChangeRequest } from 'schema/mutations/cbc/updateCbcDataAndInsertChangeReason'; import review from 'formSchema/analyst/cbc/review'; import reviewUiSchema from 'formSchema/uiSchema/cbc/reviewUiSchema'; @@ -25,7 +25,6 @@ import { import customValidate, { CBC_WARN_COLOR } from 'utils/cbcCustomValidator'; import CbcRecordLock from 'components/Analyst/CBC/CbcRecordLock'; import useModal from 'lib/helpers/useModal'; -import { useUpdateCbcCommunityDataMutationMutation } from 'schema/mutations/cbc/updateCbcCommunityData'; const getCbcQuery = graphql` query CbcIdQuery($rowId: Int!) { @@ -248,42 +247,12 @@ const Cbc = ({ }, [query, isCbcAdmin, editFeatureEnabled, cbcCommunitiesData]); const [updateFormData] = useUpdateCbcDataAndInsertChangeRequest(); - const [updateCbcCommunitySourceData] = - useUpdateCbcCommunityDataMutationMutation(); const handleChangeRequestModal = () => { setChangeReason(null); changeModal.open(); }; - const handleUpdateCommunitySource = useCallback(() => { - updateCbcCommunitySourceData({ - variables: { - input: { - _projectId: rowId, - _communityIdsToAdd: addedCommunities, - _communityIdsToArchive: removedCommunities, - }, - }, - debounceKey: 'cbc_update_community_source_data', - onCompleted: (response) => { - setAddedCommunities([]); - setRemovedCommunities([]); - setResponseCommunityData( - response.editCbcProjectCommunities.cbcProjectCommunities.map( - (proj) => proj.communitiesSourceDataByCommunitiesSourceDataId - ) - ); - }, - }); - }, [ - addedCommunities, - removedCommunities, - updateCbcCommunitySourceData, - rowId, - setResponseCommunityData, - ]); - const handleSubmit = () => { const { geographicNames, @@ -317,12 +286,23 @@ const Cbc = ({ query?.cbcByRowId?.cbcDataByCbcId?.edges[0].node.rowId || null, }, }, + inputCbcProjectCommunities: { + _projectId: rowId, + _communityIdsToAdd: addedCommunities, + _communityIdsToArchive: removedCommunities, + }, }, debounceKey: 'cbc_update_form_data', - onCompleted: () => { + onCompleted: (response) => { setEditMode(false); changeModal.close(); - handleUpdateCommunitySource(); + setAddedCommunities([]); + setRemovedCommunities([]); + setResponseCommunityData( + response.editCbcProjectCommunities.cbcProjectCommunities.map( + (proj) => proj.communitiesSourceDataByCommunitiesSourceDataId + ) + ); setAllowEdit(isCbcAdmin && editFeatureEnabled); }, }); diff --git a/app/pages/analyst/cbc/[cbcId]/edit/[section].tsx b/app/pages/analyst/cbc/[cbcId]/edit/[section].tsx index 0ebcd7296..6e9906b02 100644 --- a/app/pages/analyst/cbc/[cbcId]/edit/[section].tsx +++ b/app/pages/analyst/cbc/[cbcId]/edit/[section].tsx @@ -23,7 +23,6 @@ import { getAllEconomicRegionNames, } from 'utils/schemaUtils'; import ArrayLocationFieldTemplate from 'lib/theme/fields/ArrayLocationDataField'; -import { useUpdateCbcCommunityDataMutationMutation } from 'schema/mutations/cbc/updateCbcCommunityData'; import customValidate, { CBC_WARN_COLOR } from 'utils/cbcCustomValidator'; const getCbcSectionQuery = graphql` @@ -152,9 +151,6 @@ const EditCbcSection = ({ return formPayload; }; - const [updateCbcCommunitySourceData] = - useUpdateCbcCommunityDataMutationMutation(); - const handleOnChange = (e) => { if (section === 'locations') { setFormData({ @@ -164,28 +160,6 @@ const EditCbcSection = ({ } else setFormData({ ...formData, [section]: e.formData }); }; - const handleUpdateCommunitySource = useCallback(() => { - updateCbcCommunitySourceData({ - variables: { - input: { - _projectId: rowId, - _communityIdsToAdd: addedCommunities, - _communityIdsToArchive: removedCommunities, - }, - }, - debounceKey: 'cbc_update_community_source_data', - onCompleted: () => { - setAddedCommunities([]); - setRemovedCommunities([]); - }, - }); - }, [ - addedCommunities, - removedCommunities, - updateCbcCommunitySourceData, - rowId, - ]); - const handleChangeRequestModal = (e) => { changeModal.open(); setFormData({ ...formData, [section]: e.formData }); @@ -248,10 +222,16 @@ const EditCbcSection = ({ cbcDataId: cbcDataRowId, }, }, + inputCbcProjectCommunities: { + _projectId: rowId, + _communityIdsToAdd: addedCommunities, + _communityIdsToArchive: removedCommunities, + }, }, debounceKey: 'cbc_update_section_data', onCompleted: () => { - handleUpdateCommunitySource(); + setAddedCommunities([]); + setRemovedCommunities([]); router.push(`/analyst/cbc/${rowId}`); }, }); diff --git a/app/schema/mutations/cbc/updateCbcDataAndInsertChangeReason.ts b/app/schema/mutations/cbc/updateCbcDataAndInsertChangeReason.ts index 9649126e7..67e27b04f 100644 --- a/app/schema/mutations/cbc/updateCbcDataAndInsertChangeReason.ts +++ b/app/schema/mutations/cbc/updateCbcDataAndInsertChangeReason.ts @@ -6,6 +6,7 @@ const mutation = graphql` mutation updateCbcDataAndInsertChangeReasonMutation( $inputCbcData: UpdateCbcDataByRowIdInput! $inputCbcChangeReason: CreateCbcDataChangeReasonInput! + $inputCbcProjectCommunities: EditCbcProjectCommunitiesInput! ) { updateCbcDataByRowId(input: $inputCbcData) { cbcData { @@ -24,6 +25,20 @@ const mutation = graphql` createdAt } } + editCbcProjectCommunities(input: $inputCbcProjectCommunities) { + cbcProjectCommunities { + communitiesSourceDataId + cbcId + communitiesSourceDataByCommunitiesSourceDataId { + geographicNameId + economicRegion + regionalDistrict + bcGeographicName + geographicType + rowId + } + } + } } `; diff --git a/app/tests/pages/analyst/cbc/[cbcId]/[section].test.tsx b/app/tests/pages/analyst/cbc/[cbcId]/[section].test.tsx index 703f05539..29ec95927 100644 --- a/app/tests/pages/analyst/cbc/[cbcId]/[section].test.tsx +++ b/app/tests/pages/analyst/cbc/[cbcId]/[section].test.tsx @@ -265,6 +265,11 @@ describe('EditCbcSection', () => { cbcDataId: 20, }, }, + inputCbcProjectCommunities: { + _projectId: 1, + _communityIdsToAdd: expect.anything(), + _communityIdsToArchive: expect.anything(), + }, } ); }); @@ -428,6 +433,11 @@ describe('EditCbcSection', () => { cbcDataId: 20, }, }, + inputCbcProjectCommunities: { + _projectId: 1, + _communityIdsToAdd: expect.anything(), + _communityIdsToArchive: [], + }, } ); @@ -440,16 +450,5 @@ describe('EditCbcSection', () => { }, }, }); - - pageTestingHelper.expectMutationToBeCalled( - 'updateCbcCommunityDataMutation', - { - input: { - _projectId: 1, - _communityIdsToAdd: expect.anything(), - _communityIdsToArchive: [], - }, - } - ); }); }); diff --git a/db/deploy/computed_columns/cbc_history.sql b/db/deploy/computed_columns/cbc_history.sql index 25e664674..639fb5e4d 100644 --- a/db/deploy/computed_columns/cbc_history.sql +++ b/db/deploy/computed_columns/cbc_history.sql @@ -4,16 +4,50 @@ begin; create or replace function ccbc_public.cbc_history(_cbc_project ccbc_public.cbc) returns setof ccbc_public.record_version as $$ - select id, record_id, old_record_id, op, ts, table_oid, table_schema, table_name, + select + r.id, r.record_id, r.old_record_id, r.op, r.ts, r.table_oid, r.table_schema, r.table_name, -- if the operation is an update, use the updated_by field in the record -- because created_by is listed as the person who initially created the record - case when op = 'UPDATE'::audit.operation then (record->>'updated_by')::int else created_by end as created_by, - created_at, record, old_record - from ccbc_public.record_version - where table_name = 'cbc_data' and record->>'cbc_id' = _cbc_project.id::text + case when r.op = 'UPDATE'::audit.operation then (r.record->>'updated_by')::int else r.created_by end as created_by, + r.created_at, + -- Add added_communities and deleted_communities to the record JSON field + r.record || jsonb_build_object( + 'added_communities', ( + select jsonb_agg( + community.record || ( + select row_to_json(cs)::jsonb + from ccbc_public.communities_source_data as cs + where cs.geographic_name_id = (community.record->>'communities_source_data_id')::int + ) + ) + from ccbc_public.record_version as community + where community.ts = r.ts + and community.created_by = created_by + and community.table_name = 'cbc_project_communities' + and community.op = 'INSERT' + ), + 'deleted_communities', ( + select jsonb_agg( + community.record || ( + select row_to_json(cs)::jsonb + from ccbc_public.communities_source_data as cs + where cs.geographic_name_id = (community.record->>'communities_source_data_id')::int + ) + ) + from ccbc_public.record_version as community + where community.ts = r.ts + and community.created_by = created_by + and community.table_name = 'cbc_project_communities' + and community.op = 'UPDATE' + ) + ) as record, + r.old_record + from ccbc_public.record_version as r + where table_name = 'cbc_data' and r.record->>'cbc_id' = _cbc_project.id::text -- order by record->>'updated_at' since created_at is filled with the initial record creation date -- could be replaced by id desc, should give the same result - order by record->>'updated_at' desc; + order by r.record->>'updated_at' desc; + $$ language sql stable; diff --git a/db/deploy/computed_columns/cbc_history@1.203.3.sql b/db/deploy/computed_columns/cbc_history@1.203.3.sql new file mode 100644 index 000000000..25e664674 --- /dev/null +++ b/db/deploy/computed_columns/cbc_history@1.203.3.sql @@ -0,0 +1,27 @@ +-- Deploy ccbc:computed_columns/cbc_history to pg + +begin; + +create or replace function ccbc_public.cbc_history(_cbc_project ccbc_public.cbc) returns setof ccbc_public.record_version as $$ + + select id, record_id, old_record_id, op, ts, table_oid, table_schema, table_name, + -- if the operation is an update, use the updated_by field in the record + -- because created_by is listed as the person who initially created the record + case when op = 'UPDATE'::audit.operation then (record->>'updated_by')::int else created_by end as created_by, + created_at, record, old_record + from ccbc_public.record_version + where table_name = 'cbc_data' and record->>'cbc_id' = _cbc_project.id::text + -- order by record->>'updated_at' since created_at is filled with the initial record creation date + -- could be replaced by id desc, should give the same result + order by record->>'updated_at' desc; + + +$$ language sql stable; + +grant execute on function ccbc_public.cbc_history to ccbc_admin; +grant execute on function ccbc_public.cbc_history to cbc_admin; +grant execute on function ccbc_public.cbc_history to ccbc_analyst; + +comment on function ccbc_public.cbc_history is 'Get the history of a cbc record'; + +commit; diff --git a/db/deploy/tables/cbc_project_communities_001_enable_tracking.sql b/db/deploy/tables/cbc_project_communities_001_enable_tracking.sql new file mode 100644 index 000000000..f65830c18 --- /dev/null +++ b/db/deploy/tables/cbc_project_communities_001_enable_tracking.sql @@ -0,0 +1,7 @@ +-- Deploy ccbc:tables/cbc_project_communities_001_enable_tracking to pg + +BEGIN; + +select audit.enable_tracking('ccbc_public.cbc_project_communities'::regclass); + +COMMIT; diff --git a/db/revert/computed_columns/cbc_history.sql b/db/revert/computed_columns/cbc_history.sql index 30477d0d7..25e664674 100644 --- a/db/revert/computed_columns/cbc_history.sql +++ b/db/revert/computed_columns/cbc_history.sql @@ -1,7 +1,27 @@ --- Revert ccbc:computed_columns/cbc_history from pg +-- Deploy ccbc:computed_columns/cbc_history to pg -BEGIN; +begin; --- XXX Add DDLs here. +create or replace function ccbc_public.cbc_history(_cbc_project ccbc_public.cbc) returns setof ccbc_public.record_version as $$ -COMMIT; + select id, record_id, old_record_id, op, ts, table_oid, table_schema, table_name, + -- if the operation is an update, use the updated_by field in the record + -- because created_by is listed as the person who initially created the record + case when op = 'UPDATE'::audit.operation then (record->>'updated_by')::int else created_by end as created_by, + created_at, record, old_record + from ccbc_public.record_version + where table_name = 'cbc_data' and record->>'cbc_id' = _cbc_project.id::text + -- order by record->>'updated_at' since created_at is filled with the initial record creation date + -- could be replaced by id desc, should give the same result + order by record->>'updated_at' desc; + + +$$ language sql stable; + +grant execute on function ccbc_public.cbc_history to ccbc_admin; +grant execute on function ccbc_public.cbc_history to cbc_admin; +grant execute on function ccbc_public.cbc_history to ccbc_analyst; + +comment on function ccbc_public.cbc_history is 'Get the history of a cbc record'; + +commit; diff --git a/db/revert/computed_columns/cbc_history@1.203.3.sql b/db/revert/computed_columns/cbc_history@1.203.3.sql new file mode 100644 index 000000000..30477d0d7 --- /dev/null +++ b/db/revert/computed_columns/cbc_history@1.203.3.sql @@ -0,0 +1,7 @@ +-- Revert ccbc:computed_columns/cbc_history from pg + +BEGIN; + +-- XXX Add DDLs here. + +COMMIT; diff --git a/db/revert/tables/cbc_project_communities_001_enable_tracking.sql b/db/revert/tables/cbc_project_communities_001_enable_tracking.sql new file mode 100644 index 000000000..98c064504 --- /dev/null +++ b/db/revert/tables/cbc_project_communities_001_enable_tracking.sql @@ -0,0 +1,7 @@ +-- Revert ccbc:tables/cbc_project_communities_001_enable_tracking from pg + +BEGIN; + +-- XXX Add DDLs here. + +COMMIT; diff --git a/db/sqitch.plan b/db/sqitch.plan index 133b5fd1b..9c3852693 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -728,3 +728,5 @@ grant_read_access_to_service_account 2024-10-04T18:45:20Z Anthony Bushara # grant missing roles access to application template 9 table @1.206.1 2024-11-07T22:41:03Z CCBC Service Account # release v1.206.1 @1.207.0 2024-11-13T17:34:42Z CCBC Service Account # release v1.207.0 +tables/cbc_project_communities_001_enable_tracking 2024-11-01T18:07:48Z ,,, # enable history tracking for cbc_project_communities table +computed_columns/cbc_history [computed_columns/cbc_history@1.203.3] 2024-11-13T22:48:20Z ,,, # combine communities data changes to cbc history