From d948b4e1891535bad2d584b88aa0322d8295f9fe Mon Sep 17 00:00:00 2001 From: Dennis Kigen Date: Tue, 29 Oct 2024 11:59:35 +0300 Subject: [PATCH] Useful tweaks --- .../camera-media-uploader.component.tsx | 4 +- .../file-review.component.tsx | 140 ++++++++++-------- .../media-uploader.component.tsx | 10 +- .../upload-status.component.tsx | 13 +- .../src/routes.json | 4 +- .../translations/en.json | 7 +- 6 files changed, 95 insertions(+), 83 deletions(-) diff --git a/packages/esm-patient-attachments-app/src/camera-media-uploader/camera-media-uploader.component.tsx b/packages/esm-patient-attachments-app/src/camera-media-uploader/camera-media-uploader.component.tsx index 6cd1eab718..d0efbd41a7 100644 --- a/packages/esm-patient-attachments-app/src/camera-media-uploader/camera-media-uploader.component.tsx +++ b/packages/esm-patient-attachments-app/src/camera-media-uploader/camera-media-uploader.component.tsx @@ -2,16 +2,15 @@ import React, { useState, useCallback, useMemo, useEffect, useRef, useContext } import { useTranslation } from 'react-i18next'; import { Tabs, Tab, TabList, TabPanels, TabPanel, ModalHeader, ModalBody, InlineNotification } from '@carbon/react'; import { type FetchResponse, type UploadedFile } from '@openmrs/esm-framework'; +import { useAllowedFileExtensions } from '@openmrs/esm-patient-common-lib'; import CameraComponent from './camera.component'; import CameraMediaUploaderContext from './camera-media-uploader-context.resources'; import FileReviewContainer from './file-review.component'; import MediaUploaderComponent from './media-uploader.component'; import UploadStatusComponent from './upload-status.component'; import styles from './camera-media-uploader.scss'; -import { useAllowedFileExtensions } from '@openmrs/esm-patient-common-lib'; interface CameraMediaUploaderModalProps { - allowedExtensions: Array | null; cameraOnly?: boolean; closeModal: () => void; collectDescription?: boolean; @@ -26,7 +25,6 @@ interface CameraMediaUploadTabsProps { } const CameraMediaUploaderModal: React.FC = ({ - allowedExtensions, cameraOnly, closeModal, collectDescription, diff --git a/packages/esm-patient-attachments-app/src/camera-media-uploader/file-review.component.tsx b/packages/esm-patient-attachments-app/src/camera-media-uploader/file-review.component.tsx index ddbb7397b3..175b49cecd 100644 --- a/packages/esm-patient-attachments-app/src/camera-media-uploader/file-review.component.tsx +++ b/packages/esm-patient-attachments-app/src/camera-media-uploader/file-review.component.tsx @@ -1,5 +1,8 @@ import React, { type SyntheticEvent, useCallback, useState, useContext } from 'react'; import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Controller, useForm, type SubmitHandler } from 'react-hook-form'; import { Button, Form, ModalBody, ModalFooter, ModalHeader, Stack, TextArea, TextInput } from '@carbon/react'; import { DocumentPdf, DocumentUnknown } from '@carbon/react/icons'; import { type UploadedFile, UserHasAccess } from '@openmrs/esm-framework'; @@ -17,6 +20,7 @@ interface FilePreviewProps { moveToNextFile: () => void; onSaveFile: (dataUri: UploadedFile) => void; title?: string; + // TODO: Constrain the file type to a more specific type that only allows image and pdf uploadedFile: UploadedFile; } @@ -71,28 +75,48 @@ const FilePreview: React.FC = ({ }) => { const { t } = useTranslation(); const { allowedExtensions } = useContext(CameraMediaUploaderContext); - const fileExtension = uploadedFile.fileName.match(/\.[^\\/.]+$/)?.[0] || ''; - const [fileName, setFileName] = useState(uploadedFile.fileName.replace(/\.[^\\/.]+$/, '')); - const [fileDescription, setFileDescription] = useState(uploadedFile.fileDescription); - const [emptyName, setEmptyName] = useState(false); + const fileNameWithoutExtension = uploadedFile.fileName.trim().replace(/\.[^\\/.]+$/, ''); + + const schema = z.object({ + fileName: z.string({ + required_error: t('nameIsRequired', 'Name is required'), + }), + fileDescription: z.string().optional(), + }); + + const { + control, + handleSubmit, + formState: { errors }, + } = useForm>({ + resolver: zodResolver(schema), + defaultValues: { + fileName: fileNameWithoutExtension, + }, + }); - const saveImageOrPdf = useCallback( - (event: SyntheticEvent) => { - event.preventDefault(); + const onSubmit: SubmitHandler> = (data) => { + const { fileName, fileDescription } = data; - const sanitizedFileName = allowedExtensions?.reduce((name, ext) => { - const regex = new RegExp(`\\.(${ext})+$`, 'i'); + const sanitizedFileName = + allowedExtensions?.reduce((name, extension) => { + const regex = new RegExp(`\\.(${extension})+$`, 'i'); return name.replace(regex, ''); }, fileName) || fileName; - onSaveFile?.({ - ...uploadedFile, - fileName: `${sanitizedFileName}${fileExtension}`, - fileDescription, - }); - }, - [onSaveFile, fileName, fileExtension, allowedExtensions, fileDescription, uploadedFile], - ); + onSaveFile?.({ + ...uploadedFile, + fileName: `${sanitizedFileName}${fileExtension}`, + fileDescription, + }); + }; + + const getFileExtension = useCallback((filename: string): string => { + const validExtension = filename.match(/\.[0-9a-z]+$/i); + return validExtension ? validExtension[0].toLowerCase() : ''; + }, []); + + const fileExtension = getFileExtension(uploadedFile.fileName); const cancelCapture = useCallback( (event: SyntheticEvent) => { @@ -102,34 +126,11 @@ const FilePreview: React.FC = ({ [clearData], ); - const updateFileName = useCallback( - (event: React.ChangeEvent) => { - event.preventDefault(); - - if (event.target.value === '') { - setEmptyName(true); - } else if (emptyName) { - setEmptyName(false); - } - - setFileName(event.target.value); - }, - [setEmptyName, setFileName, emptyName], - ); - - const updateDescription = useCallback( - (event: React.ChangeEvent) => { - event.preventDefault(); - setFileDescription(event.target.value); - }, - [setFileDescription], - ); - return ( -
+ {uploadedFile.fileType === 'image' ? ( - placeholder + {t('imagePlaceholder', ) : uploadedFile.fileType === 'pdf' ? (
@@ -142,30 +143,39 @@ const FilePreview: React.FC = ({
- ( + + )} />
{collectDescription && ( -