Skip to content

Commit 9ffad7b

Browse files
authored
requirement source image api (#592)
* requirement source image api * lint fix
1 parent 3d9e504 commit 9ffad7b

File tree

9 files changed

+90
-76
lines changed

9 files changed

+90
-76
lines changed

compliance-api/src/compliance_api/models/inspection/inspection_req_detail_image.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from sqlalchemy import Boolean, Column, ForeignKey, Index, Integer, String
44

5+
from compliance_api.models.inspection.inspection_req_source_detail import InspectionReqSourceDetail
56
from compliance_api.utils.constant import DELETE_DIC_PARAMS
67

78
from ..base_model import BaseModelVersioned
@@ -76,13 +77,17 @@ def update_image(cls, image_id, image_data, session=None):
7677
return image_detail
7778

7879
@classmethod
79-
def find_all_images_by_req_detail_id(cls, req_detail_id):
80-
"""Get all images by req_detail_id."""
80+
def find_all_req_detail_images_by_req(cls, req_id):
81+
"""Get all images by req_detail_ids."""
8182
return (
82-
cls.query.filter_by(
83-
req_detail_id=req_detail_id,
84-
is_active=True,
85-
is_deleted=False,
83+
cls.query.join(
84+
InspectionReqSourceDetail,
85+
cls.req_detail_id == InspectionReqSourceDetail.id
86+
)
87+
.filter(
88+
InspectionReqSourceDetail.requirement_id == req_id,
89+
cls.is_active.is_(True),
90+
cls.is_deleted.is_(False),
8691
)
8792
.order_by(cls.id)
8893
.all()

compliance-api/src/compliance_api/resources/inspection_requirement.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def get(inspection_id, requirement_id):
215215

216216
@cors_preflight("GET, OPTIONS")
217217
@API.route(
218-
"/<int:requirement_id>/requirement-detail/<int:detail_id>/images",
218+
"/<int:requirement_id>/requirement-source-images",
219219
methods=["OPTIONS", "GET"],
220220
)
221221
class InspectionReqSourceImages(Resource):
@@ -224,14 +224,14 @@ class InspectionReqSourceImages(Resource):
224224
@staticmethod
225225
@ApiHelper.swagger_decorators(
226226
API,
227-
endpoint_description="Get all images for a requirement source detail",
227+
endpoint_description="Get all images for a requirement source",
228228
)
229229
@auth.require
230230
@API.response(code=200, description="Success", model=[inspesction_req_image_schema])
231231
@API.response(404, "Not Found")
232-
def get(inspection_id, requirement_id, detail_id):
233-
"""Get all images by requirement detail id."""
232+
def get(inspection_id, requirement_id):
233+
"""Get all images by requirement id."""
234234
images = InspectionRequirementService.get_all_requirement_detail_images(
235-
inspection_id, requirement_id, detail_id
235+
inspection_id, requirement_id
236236
)
237237
return InspectionReqDetailImageSchema(many=True).dump(images), HTTPStatus.OK

compliance-api/src/compliance_api/services/inspection_requirement.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -304,24 +304,13 @@ def get_all_images_by_inspection(cls, inspection_id):
304304

305305
@classmethod
306306
def get_all_requirement_detail_images(
307-
cls, inspection_id, requirement_id, detail_id
307+
cls, inspection_id, requirement_id
308308
):
309309
"""Get all images for a requirement source detail."""
310310
ServiceUtils.inspection_exist_check(inspection_id)
311311
_requirement_check(requirement_id)
312-
# Verify the detail belongs to the requirement
313-
detail = InspectionReqSourceDetailModel.query.filter_by(
314-
id=detail_id,
315-
requirement_id=requirement_id,
316-
is_active=True,
317-
is_deleted=False,
318-
).first()
319-
if not detail:
320-
raise ResourceNotFoundError(
321-
f"Requirement detail with id {detail_id} not found for requirement {requirement_id}"
322-
)
323-
images = InspectionReqDetailImageModel.find_all_images_by_req_detail_id(
324-
detail_id
312+
images = InspectionReqDetailImageModel.find_all_req_detail_images_by_req(
313+
requirement_id
325314
)
326315
images = _set_signed_url(images)
327316
return images

compliance-web/src/components/App/Inspections/Profile/Requirements/RequirementDrawer.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
} from "./RequirementUtils";
3333
import * as yup from "yup";
3434
import { useRequirementStore } from "./requirementStore";
35+
import { useQueryClient } from "@tanstack/react-query";
3536

3637
type RequirementDrawerProps = {
3738
inspectionData: Inspection;
@@ -57,6 +58,7 @@ const RequirementDrawer: React.FC<RequirementDrawerProps> = ({
5758
index,
5859
isRegulatoryConsideration = false,
5960
}) => {
61+
const queryClient = useQueryClient();
6062
const { appHeaderHeight } = useMenuStore();
6163
const [inspectionRequirementData, setInspectionRequirementData] = useState<
6264
InspectionRequirementFormData | undefined
@@ -118,9 +120,16 @@ const RequirementDrawer: React.FC<RequirementDrawerProps> = ({
118120
}, [onSubmit, resetForm]);
119121

120122
const onUpdateSuccess = useCallback(() => {
123+
queryClient.invalidateQueries({
124+
queryKey: [
125+
"requirement-source-images",
126+
inspectionData.id,
127+
requirement?.id ?? 0,
128+
],
129+
});
121130
onSubmit("Changes saved successfully!", false);
122131
setIsImageChanged(false);
123-
}, [onSubmit, setIsImageChanged]);
132+
}, [onSubmit, setIsImageChanged, queryClient, inspectionData, requirement]);
124133

125134
const onDeleteSuccess = useCallback(() => {
126135
onSubmit("Requirement deleted successfully!", true);

compliance-web/src/components/App/Inspections/Profile/Requirements/RequirementFormRight.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { FC, useEffect, useMemo, useState } from "react";
2222
import { useRequirementStore } from "./requirementStore";
2323
import { CaseFile } from "@/models/CaseFile";
2424
import { MODAL_WIDTHS } from "@/utils/constants";
25+
import { useRequirementSourceImages } from "@/hooks/useInspectionRequirements";
2526

2627
interface RequirementFormRightProps {
2728
onDataChange: (data: RequirementSourceFormData[]) => void;
@@ -47,6 +48,10 @@ const RequirementFormRight: FC<RequirementFormRightProps> = ({
4748
RequirementSourceFormData[]
4849
>(requirementSourceFormDataList);
4950
const { data: appendixList } = useAppendicesData(inspectionId);
51+
const { data: requirementSourceImages } = useRequirementSourceImages(
52+
inspectionId,
53+
requirementId
54+
);
5055
const { setIsDataChanged } = useRequirementStore();
5156

5257
useEffect(() => {
@@ -172,8 +177,6 @@ const RequirementFormRight: FC<RequirementFormRightProps> = ({
172177
onSubmit={handleOnAddSubmit}
173178
caseFile={caseFile}
174179
appendixList={appendixList}
175-
inspectionId={inspectionId}
176-
requirementId={requirementId}
177180
/>
178181
),
179182
width: MODAL_WIDTHS.REQUIREMENT_SOURCE,
@@ -191,8 +194,11 @@ const RequirementFormRight: FC<RequirementFormRightProps> = ({
191194
caseFile={caseFile}
192195
requirementSourceFormData={data}
193196
appendixList={appendixList}
194-
inspectionId={inspectionId}
195-
requirementId={requirementId}
197+
requirementSourceImages={
198+
requirementSourceImages?.filter(
199+
(image) => image.req_detail_id === data.id
200+
) ?? []
201+
}
196202
isSectionModal={index > 0}
197203
/>
198204
),
@@ -246,8 +252,7 @@ const RequirementFormRight: FC<RequirementFormRightProps> = ({
246252
requirementSource={data.requirementSource}
247253
order={data.order}
248254
appendixList={appendixList}
249-
inspectionId={inspectionId}
250-
requirementId={requirementId}
255+
requirementSourceImages={requirementSourceImages ?? []}
251256
isSectionModal={true}
252257
/>
253258
),
@@ -374,6 +379,7 @@ const RequirementFormRight: FC<RequirementFormRightProps> = ({
374379
onDeleteRelatedDocumentSection={
375380
handleDeleteRequirementRelatedDocumentSection
376381
}
382+
requirementSourceImages={requirementSourceImages}
377383
isRequirementEditable={isRequirementEditable}
378384
/>
379385
))}

compliance-web/src/components/App/Inspections/Profile/Requirements/RequirementSource/RequirementSourceCard.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import ParagraphWithReadMore from "@/components/Shared/ParagraphWithReadMore";
2828
import { RequirementSourceEnum } from "@/utils/constants";
2929
import RequirementRelatedDocumentCard from "./RequirementRelatedDocumentCard";
3030
import { requirementSourceNumberType } from "../RequirementUtils";
31+
import { RequirementImage } from "@/models/Image";
3132

3233
type RequirementSourceCardProps = {
3334
data: RequirementSourceFormData[];
@@ -46,6 +47,7 @@ type RequirementSourceCardProps = {
4647
onDeleteRelatedDocumentSection: (
4748
data: RequirementRelatedDocumentSectionData
4849
) => void;
50+
requirementSourceImages?: RequirementImage[];
4951
isRequirementEditable?: boolean;
5052
};
5153

@@ -60,6 +62,7 @@ const RequirementSourceCard: FC<RequirementSourceCardProps> = memo(
6062
onAddRelatedDocumentSection,
6163
onDeleteRelatedDocumentSection,
6264
onEditRelatedDocumentSection,
65+
requirementSourceImages,
6366
isRequirementEditable = true,
6467
}) => {
6568
const [isExpanded, setIsExpanded] = useState(true);
@@ -272,15 +275,39 @@ const RequirementSourceCard: FC<RequirementSourceCardProps> = memo(
272275
Description:
273276
</Typography>
274277
<ParagraphWithReadMore
275-
key={item.description?.html}
278+
key={`req-scc-desc-${item.id}`}
276279
maxHeight={84}
277280
isFormatted={true}
278281
renderTypography={
279-
<Typography
280-
variant="subtitle2"
281-
component={"div"}
282+
<Box
282283
dangerouslySetInnerHTML={{
283-
__html: item.description?.html ?? "",
284+
__html: (() => {
285+
const baseHtml = item.description?.html ?? "";
286+
const relatedImages = requirementSourceImages?.filter(
287+
(image) => image.req_detail_id === item.id
288+
);
289+
if (!relatedImages || relatedImages.length === 0) {
290+
return baseHtml;
291+
}
292+
const imageHtml = relatedImages
293+
.map((image) => {
294+
const altText = image.original_file_name ?? "";
295+
const caption = image.caption ? `<p style="margin-top: 8px; font-size: 0.875rem; color: #666; font-style: italic;">${image.caption}</p>` : "";
296+
return `
297+
<div style="margin-top: 8px;">
298+
<img
299+
src="${image.url}"
300+
alt="${altText}"
301+
style="max-width: 100%; height: auto; display: block; border-radius: 4px;"
302+
/>
303+
${caption}
304+
</div>
305+
`;
306+
})
307+
.join("");
308+
309+
return baseHtml + imageHtml;
310+
})(),
284311
}}
285312
/>
286313
}

compliance-web/src/components/App/Inspections/Profile/Requirements/RequirementSource/RequirementSourceModal.tsx

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ import { CaseFile } from "@/models/CaseFile";
2121
import { useCaseFileByNumber } from "@/hooks/useCaseFiles";
2222
import { formatAuthorization } from "@/utils/appUtils";
2323
import { RequirementImage } from "@/models/Image";
24-
import ImagesRequirementSource from "../Images/ImagesRequirementSource";
25-
import { useRequirementDetailImages } from "@/hooks/useInspectionRequirements";
24+
import ImagesRequirementSource from "@/components/App/Inspections/Profile/Requirements/Images/ImagesRequirementSource";
2625

2726
type RequirementSourceModalProps = {
2827
onSubmit: (data: RequirementSourceFormData) => void;
@@ -31,8 +30,7 @@ type RequirementSourceModalProps = {
3130
requirementSource?: RequirementSource;
3231
order?: InspectionOrder;
3332
appendixList?: Appendix[];
34-
inspectionId: number;
35-
requirementId: number;
33+
requirementSourceImages?: RequirementImage[];
3634
isSectionModal?: boolean;
3735
};
3836

@@ -96,21 +94,13 @@ const RequirementSourceModal: React.FC<RequirementSourceModalProps> = ({
9694
requirementSource,
9795
order,
9896
appendixList,
99-
inspectionId,
100-
requirementId,
97+
requirementSourceImages,
10198
isSectionModal = false,
10299
}) => {
103100
const { data: requirementSourceList } = useRequirementSourcesData();
104101
const { data: orderList } = useInspectionOrdersProjectwiseData(caseFile.id);
105102
const { data: caseFileData } = useCaseFileByNumber(caseFile.case_file_number);
106-
107-
// Fetch requirement detail images in edit mode (when requirementSourceFormData has an id)
108-
const detailId = requirementSourceFormData?.id;
109-
const { data: fetchedImages } = useRequirementDetailImages(
110-
inspectionId,
111-
requirementId,
112-
detailId
113-
);
103+
114104
const defaultValues = useMemo<RequirementSourceFormData>(() => {
115105
return (
116106
requirementSourceFormData ?? {
@@ -237,14 +227,8 @@ const RequirementSourceModal: React.FC<RequirementSourceModalProps> = ({
237227
}, [selectedRequirementSource, setValue, caseFileData]);
238228

239229
const [uploadedImages, setUploadedImages] = useState<RequirementImage[]>(
240-
requirementSourceFormData?.images ?? []
230+
requirementSourceImages ?? []
241231
);
242-
// Update uploadedImages when fetched images are available (edit mode)
243-
useEffect(() => {
244-
if (fetchedImages && detailId && uploadedImages.length == 0) {
245-
setUploadedImages(fetchedImages);
246-
}
247-
}, [fetchedImages, detailId, uploadedImages]);
248232

249233
const onSubmitHandler = (data: RequirementSourceSchemaType) => {
250234
const formData = data as RequirementSourceFormData;
@@ -311,7 +295,7 @@ const RequirementSourceModal: React.FC<RequirementSourceModalProps> = ({
311295
/>
312296
)}
313297
{selectedRequirementSource && (
314-
<Stack direction={"row"} gap={2}>
298+
<>
315299
{!isSectionModal && (
316300
<ControlledTextField
317301
name="requirementSourceTitle"
@@ -345,7 +329,7 @@ const RequirementSourceModal: React.FC<RequirementSourceModalProps> = ({
345329
fullWidth
346330
/>
347331
)}
348-
</Stack>
332+
</>
349333
)}
350334
<ControlledAutoComplete
351335
name="appendix"

compliance-web/src/hooks/useInspectionRequirements.tsx

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,12 @@ const fetchInspectionRequirementImages = (
5454
});
5555
};
5656

57-
const fetchRequirementDetailImages = (
57+
const fetchRequirementSourceImages = (
5858
inspectionId: number,
59-
requirementId: number,
60-
detailId: number
59+
requirementId: number
6160
): Promise<RequirementImage[]> => {
6261
return request({
63-
url: `/inspections/${inspectionId}/requirements/${requirementId}/requirement-detail/${detailId}/images`,
62+
url: `/inspections/${inspectionId}/requirements/${requirementId}/requirement-source-images`,
6463
});
6564
};
6665

@@ -234,28 +233,22 @@ export const useInspectionRequirementImages = (inspectionId: number) => {
234233
});
235234
};
236235

237-
export const useRequirementDetailImages = (
236+
export const useRequirementSourceImages = (
238237
inspectionId: number,
239-
requirementId: number,
240-
detailId?: number
238+
requirementId: number
241239
) => {
242240
return useQuery({
243-
queryKey: [
244-
"requirement-detail-images",
245-
inspectionId,
246-
requirementId
247-
],
248-
queryFn: () =>
249-
fetchRequirementDetailImages(inspectionId, requirementId, detailId!),
241+
queryKey: ["requirement-source-images", inspectionId, requirementId],
242+
queryFn: () => fetchRequirementSourceImages(inspectionId, requirementId),
250243
select: (data: RequirementImage[]) => {
251244
return data.map((image) => ({
252245
...image,
253246
dbId: image.id,
254247
}));
255248
},
256-
enabled: !!inspectionId && !!requirementId && !!detailId,
249+
enabled: !!inspectionId && !!requirementId,
257250
refetchOnWindowFocus: false,
258-
staleTime: Infinity,
251+
// staleTime: Infinity,
259252
});
260253
};
261254

compliance-web/src/models/Image.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface RequirementImage {
1414
relative_url?: string;
1515
url?: string;
1616
is_active?: boolean;
17+
req_detail_id?: number;
1718
}
1819

1920
export interface RequirementImages {

0 commit comments

Comments
 (0)