Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/submission photos integration #20

Merged
merged 3 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/frontend/src/api/Submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ export const SubmissionService = (url: string) => {
};
};

export const GetSubmissionPhotosService = (url: string) => {
export const GetSubmissionPhotosService = (url: string, params: { project_id: number }) => {
return async (dispatch: AppDispatch) => {
dispatch(SubmissionActions.SetSubmissionPhotosLoading(true));
const getSubmissionPhotos = async (url: string) => {
try {
const response: AxiosResponse<{ image_urls: string[] }> = await axios.get(url);
const response: AxiosResponse<{ image_urls: string[] }> = await axios.get(url, { params });
dispatch(SubmissionActions.SetSubmissionPhotos(response?.data?.image_urls));
dispatch(SubmissionActions.SetSubmissionPhotosLoading(false));
} catch (error) {
Expand Down
7 changes: 6 additions & 1 deletion src/frontend/src/utilfunctions/extractGeojsonFromObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ export const convertCoordinateStringToFeature = (key: string, coordinateString:
});
return [coordinate[1], coordinate[0]];
});
feature = { ...feature, geometry: { type: 'Polygon', coordinates: [coordinates] }, properties: { label: key } };
// if initial and last coordinates are same, it's a Polygon else LineString
if (coordinates?.[0]?.toString() === coordinates?.[coordinates?.length - 1]?.toString()) {
feature = { ...feature, geometry: { type: 'Polygon', coordinates: [coordinates] }, properties: { label: key } };
} else {
feature = { ...feature, geometry: { type: 'LineString', coordinates: coordinates }, properties: { label: key } };
}
} else {
// if feature is Point in JavaRosa format it contains string of array
const splittedCoord = coordinateString?.split(' ');
Expand Down
134 changes: 64 additions & 70 deletions src/frontend/src/views/SubmissionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,61 +12,9 @@ import useDocumentTitle from '@/utilfunctions/useDocumentTitle';
import Accordion from '@/components/common/Accordion';
import { GetProjectComments } from '@/api/Project';
import SubmissionComments from '@/components/SubmissionInstance/SubmissionComments';
import ImageSlider from '@/components/common/ImageSlider';
import { convertCoordinateStringToFeature, extractGeojsonFromObject } from '@/utilfunctions/extractGeojsonFromObject';
import { submission_status } from '@/types/enums';

const renderValue = (value: any, key: string = '') => {
if (key === 'start' || key === 'end') {
return (
<>
<div className="fmtm-capitalize fmtm-text-base fmtm-font-bold fmtm-text-[#555]">{key}</div>
<p className="fmtm-text-sm fmtm-text-[#555]">
{value?.split('T')[0]}, {value?.split('T')[1]}
</p>
</>
);
} else if (typeof value === 'object' && Object.values(value).includes('Point')) {
return (
<>
{renderValue(
`${value?.type} (${value?.coordinates?.[0]},${value?.coordinates?.[1]},${value?.coordinates?.[2]}`,
key,
)}
<div className="fmtm-border-b fmtm-my-3" />
{renderValue(value?.properties?.accuracy, 'accuracy')}
</>
);
} else if (typeof value === 'object') {
return (
<ul className="fmtm-flex fmtm-flex-col fmtm-gap-1">
<Accordion
className="fmtm-rounded-xl fmtm-px-6"
onToggle={() => {}}
hasSeperator={false}
header={<p className="fmtm-text-xl fmtm-text-[#555]">{key}</p>}
body={
<div className="fmtm-flex fmtm-flex-col fmtm-gap-2">
{Object.entries(value).map(([key, nestedValue]) => {
return <div key={key}>{renderValue(nestedValue, key)}</div>;
})}
</div>
}
/>
</ul>
);
} else {
return (
<>
<div className="fmtm-capitalize fmtm-text-base fmtm-font-bold fmtm-leading-normal fmtm-text-[#555] fmtm-break-words">
{key}
</div>
<span className="fmtm-text-sm fmtm-text-[#555] fmtm-break-words">{value}</span>
</>
);
}
};

function removeNullValues(obj: Record<string, any>) {
const newObj = {};
for (const [key, value] of Object.entries(obj)) {
Expand Down Expand Up @@ -104,6 +52,65 @@ const SubmissionDetails = () => {
const { start, end, today, deviceid, ...restSubmissionDetails } = submissionDetails || {};
const dateDeviceDetails = { start, end, today, deviceid };

const renderValue = (value: any, key: string = '') => {
if (key === 'start' || key === 'end') {
return (
<>
<div className="fmtm-capitalize fmtm-text-base fmtm-font-bold fmtm-text-[#555]">{key}</div>
<p className="fmtm-text-sm fmtm-text-[#555]">
{value?.split('T')[0]}, {value?.split('T')[1]}
</p>
</>
);
} else if (typeof value === 'object' && Object.values(value).includes('Point')) {
return (
<>
{renderValue(
`${value?.type} (${value?.coordinates?.[0]},${value?.coordinates?.[1]},${value?.coordinates?.[2]}`,
key,
)}
<div className="fmtm-border-b fmtm-my-3" />
{renderValue(value?.properties?.accuracy, 'accuracy')}
</>
);
} else if (typeof value === 'object') {
return (
<ul className="fmtm-flex fmtm-flex-col fmtm-gap-1">
<Accordion
className="fmtm-rounded-xl fmtm-px-6"
onToggle={() => {}}
hasSeperator={false}
header={<p className="fmtm-text-xl fmtm-text-[#555]">{key}</p>}
body={
<div className="fmtm-flex fmtm-flex-col fmtm-gap-2">
{Object.entries(value).map(([key, nestedValue]) => {
return <div key={key}>{renderValue(nestedValue, key)}</div>;
})}
</div>
}
/>
</ul>
);
} else {
return (
<>
<div className="fmtm-capitalize fmtm-text-base fmtm-font-bold fmtm-leading-normal fmtm-text-[#555] fmtm-break-words">
{key}
</div>
{typeof value === 'string' && value?.includes('.jpg') ? (
submissionPhotosLoading ? (
<CoreModules.Skeleton style={{ width: '16rem', height: '8rem' }} className="!fmtm-rounded-lg" />
) : (
<img src={submissionPhotos[value]} alt={key} className="fmtm-max-w-[16rem]" />
)
) : (
<span className="fmtm-text-sm fmtm-text-[#555] fmtm-break-words">{value}</span>
)}
</>
);
}
};

useEffect(() => {
dispatch(GetSubmissionDashboard(`${import.meta.env.VITE_API_URL}/submission/${projectId}/dashboard`));
}, []);
Expand All @@ -126,7 +133,11 @@ const SubmissionDetails = () => {

useEffect(() => {
if (paramsInstanceId) {
dispatch(GetSubmissionPhotosService(`${import.meta.env.VITE_API_URL}/submission/${paramsInstanceId}/photos`));
dispatch(
GetSubmissionPhotosService(`${import.meta.env.VITE_API_URL}/submission/${paramsInstanceId}/photos`, {
project_id: projectId,
}),
);
}
}, [paramsInstanceId]);

Expand Down Expand Up @@ -247,23 +258,6 @@ const SubmissionDetails = () => {
<SubmissionComments />
</div>
</div>
{/* submission photos */}
{submissionPhotosLoading ? (
<div className="fmtm-flex fmtm-gap-x-3 fmtm-overflow-x-scroll scrollbar fmtm-bg-white fmtm-p-6 fmtm-rounded-xl">
{Array.from({ length: 5 }).map((_, i) => (
<CoreModules.Skeleton
key={i}
style={{ width: '9.688rem', height: '10.313rem' }}
className="!fmtm-rounded-lg"
/>
))}
</div>
) : submissionPhotos?.length > 0 ? (
<div className="fmtm-bg-white fmtm-rounded-xl fmtm-p-6">
<p className="fmtm-text-base fmtm-font-bold fmtm-text-[#555] fmtm-mb-2">Images</p>
<ImageSlider images={submissionPhotos || []} />
</div>
) : null}
</div>
</>
);
Expand Down
Loading