diff --git a/assets/@types/app.ts b/assets/@types/app.ts index 38f8b1b9..340405fe 100644 --- a/assets/@types/app.ts +++ b/assets/@types/app.ts @@ -259,6 +259,13 @@ export type StoredDataReport = { processing_executions: StoredDataReportProcessingExecution[]; }; +export type DeliveryReport = { + input_upload: Upload & { + file_tree: UploadTree; + checks: CheckDetailed[]; + }; +}; + export type StoredDataReportProcessingExecution = ProcessingExecution & { output: ProcessingExecutionOutputStoredDataDto; logs?: CheckOrProcessingExecutionLogs; diff --git a/assets/entrepot/api/upload.ts b/assets/entrepot/api/upload.ts index 43cf55ab..625c1bc4 100644 --- a/assets/entrepot/api/upload.ts +++ b/assets/entrepot/api/upload.ts @@ -1,5 +1,6 @@ import SymfonyRouting from "../../modules/Routing"; import { jsonFetch } from "../../modules/jsonFetch"; +import { DeliveryReport } from "../../@types/app"; import { Upload, UploadTree, UploadTypeEnum } from "../../@types/app"; const getList = (datastoreId: string, type?: UploadTypeEnum, otherOptions: RequestInit = {}) => { @@ -61,6 +62,13 @@ const remove = (datastoreId: string, uploadId: string) => { return jsonFetch(url, { method: "DELETE" }); }; +const getDeliveryReport = (datastoreId: string, uploadId: string, otherOptions: RequestInit = {}) => { + const url = SymfonyRouting.generate("cartesgouvfr_api_upload_get_delivery_report", { datastoreId, uploadId }); + return jsonFetch(url, { + ...otherOptions, + }); +}; + const upload = { getList, add, @@ -69,6 +77,7 @@ const upload = { pingIntegrationProgress, getFileTree, remove, + getDeliveryReport, }; export default upload; diff --git a/assets/entrepot/pages/datasheet/DatasheetView/DatasetListTab/UnfinishedUploadList.tsx b/assets/entrepot/pages/datasheet/DatasheetView/DatasetListTab/UnfinishedUploadList.tsx index 5ed363fe..ea2a8b7a 100644 --- a/assets/entrepot/pages/datasheet/DatasheetView/DatasetListTab/UnfinishedUploadList.tsx +++ b/assets/entrepot/pages/datasheet/DatasheetView/DatasetListTab/UnfinishedUploadList.tsx @@ -28,8 +28,11 @@ const UnfinishedUploadList: FC = ({ datastoreId, uplo
- +
diff --git a/assets/entrepot/pages/stored_data/StoredDataDetails/DeliveryDetails.tsx b/assets/entrepot/pages/stored_data/StoredDataDetails/DeliveryDetails.tsx new file mode 100644 index 00000000..2b5dbdcd --- /dev/null +++ b/assets/entrepot/pages/stored_data/StoredDataDetails/DeliveryDetails.tsx @@ -0,0 +1,95 @@ +import { fr } from "@codegouvfr/react-dsfr"; +import Alert from "@codegouvfr/react-dsfr/Alert"; +import Button from "@codegouvfr/react-dsfr/Button"; +import Tabs from "@codegouvfr/react-dsfr/Tabs"; +import { useQuery } from "@tanstack/react-query"; +import { FC, useMemo } from "react"; + +import { DeliveryReport } from "../../../../@types/app"; +import DatastoreLayout from "../../../../components/Layout/DatastoreLayout"; +import LoadingIcon from "../../../../components/Utils/LoadingIcon"; +import RQKeys from "../../../../modules/entrepot/RQKeys"; +import { CartesApiException } from "../../../../modules/jsonFetch"; +import { routes } from "../../../../router/router"; +import api from "../../../api"; +import DeliveryPreviewTab from "./PreviewTab/DeliveryPreviewTab"; +import ReportTab from "./ReportTab/ReportTab"; + +type DeliveryDetailsProps = { + datastoreId: string; + uploadDataId: string; +}; + +const DeliveryDetails: FC = ({ datastoreId, uploadDataId }) => { + const datastoreQuery = useQuery({ + queryKey: RQKeys.datastore(datastoreId), + queryFn: ({ signal }) => api.datastore.get(datastoreId, { signal }), + staleTime: 3600000, + }); + + const reportQuery = useQuery({ + queryKey: RQKeys.datastore_delivery_report(datastoreId, uploadDataId), + queryFn: ({ signal }) => api.upload.getDeliveryReport(datastoreId, uploadDataId, { signal }), + staleTime: 3600000, + }); + + const datasheetName = useMemo(() => reportQuery?.data?.input_upload?.tags?.datasheet_name, [reportQuery?.data?.input_upload?.tags?.datasheet_name]); + + return ( + +
+ {datasheetName ? ( +
+ +
+ {reportQuery.isError && } +
+ {reportQuery?.data?.input_upload?.name && ( +
+

{reportQuery?.data?.input_upload?.name}

+
+ )} + + {reportQuery.data && ( +
+
+ , + }, + { + label: "Rapport de génération", + content: , + }, + ]} + /> +
+
+ )} +
+ ); +}; + +export default DeliveryDetails; diff --git a/assets/entrepot/pages/stored_data/StoredDataDetails/PreviewTab/DeliveryPreviewTab.tsx b/assets/entrepot/pages/stored_data/StoredDataDetails/PreviewTab/DeliveryPreviewTab.tsx new file mode 100644 index 00000000..473e43e1 --- /dev/null +++ b/assets/entrepot/pages/stored_data/StoredDataDetails/PreviewTab/DeliveryPreviewTab.tsx @@ -0,0 +1,42 @@ +import { fr } from "@codegouvfr/react-dsfr"; +import Accordion from "@codegouvfr/react-dsfr/Accordion"; +import { FC } from "react"; + +import { DeliveryReport } from "../../../../../@types/app"; +import { niceBytes } from "../../../../../utils"; +import ReportStatusBadge from "../ReportTab/ReportStatusBadge"; + +type DeliveryPreviewTabProps = { + reportData: DeliveryReport; +}; + +const DeliveryPreviewTab: FC = ({ reportData }) => { + const uploadData = reportData.input_upload; + + return ( + +
    +
  • + Nom : {uploadData.name} +
  • +
  • + Identifiant technique : {uploadData._id} +
  • +
  • + Projection : {uploadData.srs} +
  • +
  • + Statut : {} +
  • +
  • + Taille : {uploadData.size && niceBytes(uploadData.size.toString())} +
  • +
  • + Type : {uploadData.type} +
  • +
+
+ ); +}; + +export default DeliveryPreviewTab; diff --git a/assets/entrepot/pages/stored_data/StoredDataDetails/ReportTab/ReportTab.tsx b/assets/entrepot/pages/stored_data/StoredDataDetails/ReportTab/ReportTab.tsx index 11f2f809..67c06129 100644 --- a/assets/entrepot/pages/stored_data/StoredDataDetails/ReportTab/ReportTab.tsx +++ b/assets/entrepot/pages/stored_data/StoredDataDetails/ReportTab/ReportTab.tsx @@ -3,7 +3,7 @@ import Accordion from "@codegouvfr/react-dsfr/Accordion"; import { UseQueryResult } from "@tanstack/react-query"; import { FC } from "react"; -import { StoredDataReport as ReportTab } from "../../../../../@types/app"; +import { DeliveryReport, StoredDataReport as ReportTab } from "../../../../../@types/app"; import { CheckingExecutionDetailResponseDtoStatusEnum, ProcessingExecutionDetailResponseDtoStatusEnum } from "../../../../../@types/entrepot"; import { CartesApiException } from "../../../../../modules/jsonFetch"; import { niceBytes } from "../../../../../utils"; @@ -14,7 +14,7 @@ import UploadFileTree from "./UploadFileTree"; type ReportTabProps = { datastoreName?: string; - reportQuery: UseQueryResult; + reportQuery: UseQueryResult; }; const ReportTab: FC = ({ datastoreName, reportQuery }) => { @@ -65,24 +65,25 @@ const ReportTab: FC = ({ datastoreName, reportQuery }) => { ))} - {reportQuery?.data.processing_executions.map((procExec) => ( - -

{`${step++}. Traitement : ${procExec.processing.name}`}

- - - } - defaultExpanded={[ - ProcessingExecutionDetailResponseDtoStatusEnum.FAILURE, - ProcessingExecutionDetailResponseDtoStatusEnum.ABORTED, - ].includes(procExec.status)} - > - -
- ))} + {"processing_executions" in reportQuery.data && + reportQuery?.data.processing_executions.map((procExec) => ( + +

{`${step++}. Traitement : ${procExec.processing.name}`}

+ + + } + defaultExpanded={[ + ProcessingExecutionDetailResponseDtoStatusEnum.FAILURE, + ProcessingExecutionDetailResponseDtoStatusEnum.ABORTED, + ].includes(procExec.status)} + > + +
+ ))} ) ); diff --git a/assets/modules/entrepot/RQKeys.ts b/assets/modules/entrepot/RQKeys.ts index 8159a76d..d5ab9221 100644 --- a/assets/modules/entrepot/RQKeys.ts +++ b/assets/modules/entrepot/RQKeys.ts @@ -20,6 +20,7 @@ const RQKeys = { datastore_stored_data: (datastoreId: string, storedDataId: string): string[] => ["datastore", datastoreId, "stored_data", storedDataId], datastore_stored_data_uses: (datastoreId: string, storedDataId: string): string[] => ["datastore", datastoreId, "stored_data", storedDataId, "uses"], datastore_stored_data_report: (datastoreId: string, storedDataId: string): string[] => ["datastore", datastoreId, "stored_data", storedDataId, "report"], + datastore_delivery_report: (datastoreId: string, uploadDataId: string): string[] => ["datastore", datastoreId, "upload_data", uploadDataId, "report"], datastore_datasheet_list: (datastoreId: string): string[] => ["datastore", datastoreId, "datasheet"], datastore_datasheet: (datastoreId: string, dataName: string): string[] => ["datastore", datastoreId, "datasheet", dataName], diff --git a/assets/router/RouterRenderer.tsx b/assets/router/RouterRenderer.tsx index 5fd209aa..c7f70fbd 100644 --- a/assets/router/RouterRenderer.tsx +++ b/assets/router/RouterRenderer.tsx @@ -43,6 +43,7 @@ const DatasheetUploadIntegrationPage = lazy(() => import("../entrepot/pages/data const DatasheetView = lazy(() => import("../entrepot/pages/datasheet/DatasheetView/DatasheetView")); const StoredDataDetails = lazy(() => import("../entrepot/pages/stored_data/StoredDataDetails/StoredDataDetails")); +const DeliveryReport = lazy(() => import("../entrepot/pages/stored_data/StoredDataDetails/DeliveryDetails")); const DatastoreCreationForm = lazy(() => import("../entrepot/pages/datastore/DatastoreCreationForm")); const Confirm = lazy(() => import("../entrepot/pages/datastore/Confirmation")); @@ -157,6 +158,8 @@ const RouterRenderer: FC = () => { return ; case "datastore_stored_data_details": return ; + case "datastore_delivery_details": + return ; case "datastore_wfs_service_new": return ; case "datastore_wfs_service_edit": diff --git a/assets/router/router.ts b/assets/router/router.ts index 57280d63..03851e33 100644 --- a/assets/router/router.ts +++ b/assets/router/router.ts @@ -141,6 +141,13 @@ const routeDefs = { }, (p) => `${appRoot}/entrepot/${p.datastoreId}/donnees/${p.storedDataId}/details` ), + datastore_delivery_details: defineRoute( + { + datastoreId: param.path.string, + uploadDataId: param.path.string, + }, + (p) => `${appRoot}/entrepot/${p.datastoreId}/donnees/${p.uploadDataId}/rapport` + ), // Creer et publier un service WFS datastore_wfs_service_new: defineRoute( diff --git a/src/Controller/Entrepot/UploadController.php b/src/Controller/Entrepot/UploadController.php index e04e0eaa..2a08120e 100644 --- a/src/Controller/Entrepot/UploadController.php +++ b/src/Controller/Entrepot/UploadController.php @@ -308,4 +308,33 @@ public function delete(string $datastoreId, string $uploadId): JsonResponse throw new CartesApiException($ex->getMessage(), JsonResponse::HTTP_INTERNAL_SERVER_ERROR); } } + + #[Route('/{uploadId}/delivery-report', name: 'get_delivery_report', methods: ['GET'])] + public function getDeliveryReport(string $datastoreId, string $uploadId): JsonResponse + { + try { + // Récupération des détails de l'upload ayant échoué + $inputUpload = $this->uploadApiService->get($datastoreId, $uploadId); + $inputUpload['file_tree'] = $this->uploadApiService->getFileTree($datastoreId, $inputUpload['_id']); + $inputUpload['checks'] = []; + $uploadChecks = $this->uploadApiService->getCheckExecutions($datastoreId, $inputUpload['_id']); + + foreach ($uploadChecks as &$checkType) { + foreach ($checkType as &$checkExecution) { + $checkExecution = array_merge($checkExecution, $this->uploadApiService->getCheckExecution($datastoreId, $checkExecution['_id'])); + try { + $checkExecution['logs'] = $this->uploadApiService->getCheckExecutionLogs($datastoreId, $checkExecution['_id']); + } catch (ApiException $ex) { + } + $inputUpload['checks'][] = $checkExecution; + } + } + + return $this->json([ + 'input_upload' => $inputUpload, + ]); + } catch (ApiException $ex) { + throw new CartesApiException($ex->getMessage(), $ex->getStatusCode(), $ex->getDetails()); + } + } }