Skip to content

Commit 2ecfbc0

Browse files
Merge pull request #1321 from bluewave-labs/hp-may-8-fix-file-uploads
Fix undefined subcontrols
2 parents deacc14 + e2f62d4 commit 2ecfbc0

File tree

4 files changed

+65
-61
lines changed

4 files changed

+65
-61
lines changed

Clients/src/presentation/components/Modals/Controlpane/NewControlPane.tsx

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import {
1111
} from "@mui/material";
1212
import { ReactComponent as CloseIcon } from "../../../assets/icons/close.svg";
1313
import DropDowns from "../../Inputs/Dropdowns";
14-
import { useState, useContext, useEffect } from "react";
14+
import { useState, useContext } from "react";
1515
import AuditorFeedback from "../ComplianceFeedback/ComplianceFeedback";
16-
import { getEntityById, updateEntityById } from "../../../../application/repository/entity.repository";
16+
import { updateEntityById } from "../../../../application/repository/entity.repository";
1717
import { Subcontrol } from "../../../../domain/types/Subcontrol";
1818
import { Control } from "../../../../domain/types/Control";
1919
import { FileData } from "../../../../domain/types/File";
@@ -22,7 +22,6 @@ import VWToast from "../../../vw-v2-components/Toast";
2222
import SaveIcon from "@mui/icons-material/Save";
2323
import VWButton from "../../../vw-v2-components/Buttons";
2424
import { VerifyWiseContext } from "../../../../application/contexts/VerifyWise.context";
25-
import VWSkeleton from "../../../vw-v2-components/Skeletons";
2625

2726
const tabStyle = {
2827
textTransform: "none",
@@ -37,29 +36,25 @@ const tabStyle = {
3736
};
3837

3938
const NewControlPane = ({
40-
_data,
39+
data,
4140
isOpen,
4241
handleClose,
4342
controlCategoryId,
4443
OnSave,
4544
OnError,
4645
onComplianceUpdate,
4746
projectId,
48-
projectFrameworkId,
4947
}: {
50-
_data: Control;
48+
data: Control;
5149
isOpen: boolean;
5250
handleClose: () => void;
5351
controlCategoryId?: string;
5452
OnSave?: (state: Control) => void;
5553
OnError?: () => void;
5654
onComplianceUpdate?: () => void;
5755
projectId: number;
58-
projectFrameworkId: number;
5956
}) => {
6057
const theme = useTheme();
61-
const [data, setData] = useState<Control>(_data);
62-
const [loading, setLoading] = useState<boolean>(true);
6358
const [selectedTab, setSelectedTab] = useState<number>(0);
6459
const [activeSection, setActiveSection] = useState<string>("Overview");
6560
const [alert, setAlert] = useState<{
@@ -77,43 +72,14 @@ const NewControlPane = ({
7772
}>({});
7873
const context = useContext(VerifyWiseContext);
7974

80-
useEffect(() => {
81-
const fetchControls = async () => {
82-
setLoading(true);
83-
const response = await getEntityById({
84-
routeUrl: `eu-ai-act/controlById?controlId=${_data.id}&projectFrameworkId=${projectFrameworkId}`,
85-
});
86-
setData(response.data);
87-
setLoading(false);
88-
setState({
89-
order_no: response.data.order_no,
90-
id: response.data.id,
91-
title: response.data.title,
92-
description: response.data.description,
93-
status: response.data.status,
94-
approver: response.data.approver,
95-
risk_review: response.data.risk_review,
96-
owner: response.data.owner,
97-
reviewer: response.data.reviewer,
98-
implementation_details: response.data.implementation_details,
99-
due_date: response.data.due_date,
100-
control_category_id: response.data.control_category_id, // Added missing property
101-
102-
subControls: response.data.subControls,
103-
})
104-
};
105-
106-
fetchControls();
107-
}, [isOpen])
108-
10975
const sanitizeField = (value: string | undefined | null): string => {
11076
if (!value || value === "undefined") {
11177
return "";
11278
}
11379
return value;
11480
};
11581

116-
const initialSubControlState = (data.subControls || []).length > 0 && data
82+
const initialSubControlState = data
11783
.subControls!.slice()
11884
.sort((a, b) => a.order_no! - b.order_no!)
11985
.map((subControl: Subcontrol) => ({
@@ -366,16 +332,6 @@ const NewControlPane = ({
366332
handleClose();
367333
};
368334

369-
if (loading) {
370-
return (
371-
<Stack spacing={2}>
372-
<VWSkeleton variant="rectangular" width="100%" height={36} />
373-
<VWSkeleton variant="rectangular" width="100%" height={36} />
374-
<VWSkeleton variant="rectangular" width="100%" height={36} />
375-
</Stack>
376-
);
377-
}
378-
379335
return (
380336
<>
381337
{alert && (

Clients/src/presentation/pages/ComplianceTracker/1.0ComplianceTracker/ControlsTable.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const ControlsTable: React.FC<ControlsTableProps> = ({
5050
const { users } = dashboardValues;
5151
const currentProjectId = projectId;
5252
const [controls, setControls] = useState<Control[]>([]);
53+
const [selectedControl, setSelectedControl] = useState<Control | null>(null);
5354
const [loading, setLoading] = useState<boolean>(true);
5455
const [error, setError] = useState<unknown>(null);
5556
const [selectedRow, setSelectedRow] = useState<number | null>(null);
@@ -72,7 +73,11 @@ const ControlsTable: React.FC<ControlsTableProps> = ({
7273
setAlert(null);
7374
}, [currentProjectId]);
7475

75-
const handleRowClick = (id: number) => {
76+
const handleRowClick = async (id: number) => {
77+
const subControlsResponse = await getEntityById({
78+
routeUrl: `eu-ai-act/controlById?controlId=${id}&projectFrameworkId=${projectFrameworkId}`,
79+
});
80+
setSelectedControl(subControlsResponse.data);
7681
setSelectedRow(id);
7782
setModalOpen(true);
7883
};
@@ -125,7 +130,7 @@ const ControlsTable: React.FC<ControlsTableProps> = ({
125130
setLoading(true);
126131
try {
127132
const response = await getEntityById({
128-
routeUrl: `/eu-ai-act/controls/byControlCategoryId/${controlCategoryId}`,
133+
routeUrl: `/eu-ai-act/controls/byControlCategoryId/${controlCategoryId}?projectFrameworkId=${projectFrameworkId}`,
129134
});
130135
setControls(response);
131136
} catch (err) {
@@ -278,15 +283,14 @@ const ControlsTable: React.FC<ControlsTableProps> = ({
278283
</TableContainer>
279284
{modalOpen && selectedRow !== null && (
280285
<NewControlPane
281-
_data={controls.find((c) => c.id === selectedRow)!}
286+
data={selectedControl!}
282287
isOpen={modalOpen}
283288
handleClose={handleCloseModal}
284289
OnSave={handleSaveSuccess}
285290
OnError={handleSaveError}
286291
controlCategoryId={controlCategoryIndex?.toString()}
287292
onComplianceUpdate={onComplianceUpdate}
288293
projectId={currentProjectId}
289-
projectFrameworkId={projectFrameworkId}
290294
/>
291295
)}
292296
</>

Servers/controllers/eu.ctrl.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { getAllProjectsQuery, updateProjectUpdatedByIdQuery } from "../utils/pro
66
import { RequestWithFile, UploadedFile } from "../utils/question.utils";
77
import { STATUS_CODE } from "../utils/statusCode.utils";
88
import { QuestionStructEU } from "../models/EU/questionStructEU.model";
9-
import { countAnswersEUByProjectId, countSubControlsEUByProjectId, deleteAssessmentEUByProjectIdQuery, deleteComplianeEUByProjectIdQuery, getAllControlCategoriesQuery, getAllTopicsQuery, getAssessmentsEUByProjectIdQuery, getComplianceEUByProjectIdQuery, getControlByIdForProjectQuery, getControlStructByControlCategoryIdQuery, getTopicByIdForProjectQuery, updateControlEUByIdQuery, updateQuestionEUByIdQuery, updateSubcontrolEUByIdQuery } from "../utils/eu.utils";
9+
import { countAnswersEUByProjectId, countSubControlsEUByProjectId, deleteAssessmentEUByProjectIdQuery, deleteComplianeEUByProjectIdQuery, getAllControlCategoriesQuery, getAllTopicsQuery, getAssessmentsEUByProjectIdQuery, getComplianceEUByProjectIdQuery, getControlByIdForProjectQuery, getControlStructByControlCategoryIdForAProjectQuery, getControlStructByControlCategoryIdQuery, getTopicByIdForProjectQuery, updateControlEUByIdQuery, updateQuestionEUByIdQuery, updateSubcontrolEUByIdQuery } from "../utils/eu.utils";
1010
import { AnswerEU } from "../models/EU/answerEU.model";
1111
import { sequelize } from "../database/db";
1212
import { Project, ProjectModel } from "../models/project.model";
@@ -292,8 +292,8 @@ export async function getProjectAssessmentProgress(req: Request, res: Response)
292292
const { totalAssessments, answeredAssessments } = await countAnswersEUByProjectId(projectFrameworkId);
293293
return res.status(200).json(
294294
STATUS_CODE[200]({
295-
totalQuestions: totalAssessments,
296-
answeredQuestions: answeredAssessments,
295+
totalQuestions: parseInt(totalAssessments),
296+
answeredQuestions: parseInt(answeredAssessments),
297297
})
298298
);
299299
} catch (error) {
@@ -313,8 +313,8 @@ export async function getProjectComplianceProgress(req: Request, res: Response)
313313
const { totalSubcontrols, doneSubcontrols } = await countSubControlsEUByProjectId(projectFrameworkId);
314314
return res.status(200).json(
315315
STATUS_CODE[200]({
316-
allsubControls: totalSubcontrols,
317-
allDonesubControls: doneSubcontrols,
316+
allsubControls: parseInt(totalSubcontrols),
317+
allDonesubControls: parseInt(doneSubcontrols),
318318
})
319319
);
320320
} catch (error) {
@@ -412,7 +412,8 @@ export async function getControlsByControlCategoryId(
412412
): Promise<any> {
413413
try {
414414
const controlCategoryId = parseInt(req.params.id);
415-
const controls = await getControlStructByControlCategoryIdQuery(controlCategoryId);
415+
const projectFrameworkId = parseInt(req.query.projectFrameworkId as string);
416+
const controls = await getControlStructByControlCategoryIdForAProjectQuery(controlCategoryId, projectFrameworkId);
416417
return res.status(200).json(controls);
417418
} catch (error) {
418419
return res.status(500).json(STATUS_CODE[500]((error as Error).message));

Servers/utils/eu.utils.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,22 @@ export const getTopicByIdForProjectQuery = async (
122122
return topic;
123123
}
124124

125+
const getSubControlsCalculations = async (
126+
controlId: number,
127+
) => {
128+
const result = await sequelize.query(
129+
`SELECT COUNT(*) AS "numberOfSubcontrols", COUNT(CASE WHEN sc.status = 'Done' THEN 1 END) AS "numberOfDoneSubcontrols" FROM
130+
controls_eu c JOIN subcontrols_eu sc ON c.id = sc.control_id WHERE c.id = :control_id;`,
131+
{
132+
replacements: { control_id: controlId },
133+
}
134+
) as [{ numberOfSubcontrols: string; numberOfDoneSubcontrols: string }[], number];
135+
return result[0][0] as {
136+
numberOfSubcontrols: string;
137+
numberOfDoneSubcontrols: string;
138+
};
139+
}
140+
125141
export const getControlByIdForProjectQuery = async (
126142
controlStructId: number,
127143
projectFrameworkId: number
@@ -139,6 +155,9 @@ export const getControlByIdForProjectQuery = async (
139155
for (let subControl of subControls) {
140156
(control as any).subControls.push({ ...subControl });
141157
}
158+
const subControlsCalculations = await getSubControlsCalculations(control.id!);
159+
(control as any).numberOfSubcontrols = parseInt(subControlsCalculations.numberOfSubcontrols);
160+
(control as any).numberOfDoneSubcontrols = parseInt(subControlsCalculations.numberOfDoneSubcontrols);
142161
return control;
143162
}
144163

@@ -251,6 +270,27 @@ export const getControlStructByControlCategoryIdQuery = async (
251270
return controlsStruct;
252271
}
253272

273+
export const getControlStructByControlCategoryIdForAProjectQuery = async (
274+
controlCategoryId: number,
275+
projectFrameworkId: number,
276+
) => {
277+
const controlsStruct = await sequelize.query(
278+
`SELECT cs.*, c.owner FROM controls_struct_eu cs JOIN controls_eu c ON cs.id = c.control_meta_id
279+
WHERE cs.control_category_id = :control_category_id AND c.projects_frameworks_id = :projects_frameworks_id;`,
280+
{
281+
replacements: {
282+
control_category_id: controlCategoryId, projects_frameworks_id: projectFrameworkId
283+
}
284+
}
285+
) as [Partial<ControlStructEUModel & ControlEUModel>[], number];
286+
for (let control of controlsStruct[0]) {
287+
const subControlsCalculations = await getSubControlsCalculations(control.id!);
288+
(control as any).numberOfSubcontrols = parseInt(subControlsCalculations.numberOfSubcontrols);
289+
(control as any).numberOfDoneSubcontrols = parseInt(subControlsCalculations.numberOfDoneSubcontrols);
290+
}
291+
return controlsStruct[0];
292+
}
293+
254294
export const getControlByIdQuery = async (
255295
controlId: number,
256296
transaction: Transaction | null = null
@@ -571,6 +611,9 @@ export const updateControlEUByIdQuery = async (
571611
return true
572612
}
573613
}).map(f => `${f} = :${f}`).join(", ");
614+
if (!setClause) {
615+
return control as ControlEU;
616+
}
574617

575618
const query = `UPDATE controls_eu SET ${setClause} WHERE id = :id RETURNING *;`;
576619

@@ -593,7 +636,7 @@ export const updateSubcontrolEUByIdQuery = async (
593636
feedbackUploadedFiles: { id: string; fileName: string, project_id: number, uploaded_by: number, uploaded_time: Date }[] = [],
594637
deletedFiles: number[] = [],
595638
transaction: Transaction
596-
): Promise<SubcontrolEU | null> => {
639+
): Promise<SubcontrolEU> => {
597640
const files = await sequelize.query(
598641
`SELECT evidence_files, feedback_files FROM subcontrols_eu WHERE id = :id`,
599642
{
@@ -649,7 +692,7 @@ export const updateSubcontrolEUByIdQuery = async (
649692
}).join(", ");
650693

651694
if (setClause.length === 0) {
652-
return null;
695+
return subcontrol as SubcontrolEU;
653696
}
654697

655698
const query = `UPDATE subcontrols_eu SET ${setClause} WHERE id = :id RETURNING *;`;

0 commit comments

Comments
 (0)