Skip to content

Commit

Permalink
Merge branch 'issues/6006/removeLodash' of https://github.com/Swanand…
Browse files Browse the repository at this point in the history
…Bhuskute/care_fe into issues/6006/removeLodash
  • Loading branch information
SwanandBhuskute committed Nov 29, 2024
2 parents 449d574 + eac2760 commit 2c521a1
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 31 deletions.
42 changes: 27 additions & 15 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
Care is a React Typescript Project, built with Vite and styled with TailwindCSS.
You are an expert in TypeScript, React, Shadcn UI, Tailwind.

Care uses a Plugin Architecture. Apps are installed in /apps.
Key Principles

Care uses a custom useQuery hook to fetch data from the API. APIs are defined in the api.tsx file
- Write concise, technical TypeScript code with accurate examples.
- Use functional and declarative programming patterns; avoid classes.
- Prefer iteration and modularization over code duplication.
- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).

Here's an example of how to use the useQuery hook to fetch data from the API:
Naming Conventions

```
useQuery from "@/common/hooks/useQuery";
const { data, loading, error } = useQuery(routes.getFacilityUsers, {
facility_id: "1",
});
- Use lowercase with dashes for directories (e.g., components/auth-wizard).
- Favor named exports for components.

request from "@/common/utils/request";
const { res } = await request(routes.partialUpdateAsset, {
pathParams: { external_id: assetId },
body: data,
});
```
TypeScript Usage

- Use TypeScript for all code; prefer interfaces over types.
- Avoid enums; use maps instead.
- Use functional components with TypeScript interfaces.

Syntax and Formatting

- Use the "function" keyword for pure functions.
- Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements.
- Use declarative JSX.

UI and Styling

- Use Shadcn UI, Radix, and Tailwind for components and styling.
- Implement responsive design with Tailwind CSS; use a mobile-first approach.

General Guidelines

- Care uses a custom useQuery hook to fetch data from the API. (Docs @ /Utils/request/useQuery)
- APIs are defined in the api.tsx file.
3 changes: 2 additions & 1 deletion public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@
"date_and_time": "Date and Time",
"date_declared_positive": "Date of declaring positive",
"date_of_admission": "Date of Admission",
"date_of_birth": "Date of birth",
"date_of_birth": "Date of Birth",
"date_of_positive_covid_19_swab": "Date of Positive Covid 19 Swab",
"date_of_result": "Covid confirmation date",
"date_of_return": "Date of Return",
Expand Down Expand Up @@ -1433,6 +1433,7 @@
"why_the_asset_is_not_working": "Why the asset is not working?",
"width": "Width ({{unit}})",
"working_status": "Working Status",
"year_of_birth": "Year of Birth",
"years": "years",
"years_of_experience": "Years of Experience",
"years_of_experience_of_the_doctor": "Years of Experience of the Doctor",
Expand Down
113 changes: 105 additions & 8 deletions src/components/Common/FilePreviewDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
SetStateAction,
Suspense,
lazy,
useEffect,
useState,
} from "react";
import { useTranslation } from "react-i18next";
Expand All @@ -15,6 +16,8 @@ import CircularProgress from "@/components/Common/CircularProgress";
import DialogModal from "@/components/Common/Dialog";
import { StateInterface } from "@/components/Files/FileUpload";

import { FileUploadModel } from "../Patient/models";

const PDFViewer = lazy(() => import("@/components/Common/PDFViewer"));

export const zoom_values = [
Expand All @@ -40,6 +43,9 @@ type FilePreviewProps = {
className?: string;
titleAction?: ReactNode;
fixedWidth?: boolean;
uploadedFiles?: FileUploadModel[];
loadFile?: (file: FileUploadModel, associating_id: string) => void;
currentIndex: number;
};

const previewExtensions = [
Expand All @@ -56,12 +62,28 @@ const previewExtensions = [
];

const FilePreviewDialog = (props: FilePreviewProps) => {
const { show, onClose, file_state, setFileState, downloadURL, fileUrl } =
props;
const {
show,
onClose,
file_state,
setFileState,
downloadURL,
fileUrl,
uploadedFiles,
loadFile,
currentIndex,
} = props;
const { t } = useTranslation();

const [page, setPage] = useState(1);
const [numPages, setNumPages] = useState(1);
const [index, setIndex] = useState<number>(currentIndex);

useEffect(() => {
if (uploadedFiles && show) {
setIndex(currentIndex);
}
}, [uploadedFiles, show, currentIndex]);

const handleZoomIn = () => {
const checkFull = file_state.zoom === zoom_values.length;
Expand All @@ -79,9 +101,27 @@ const FilePreviewDialog = (props: FilePreviewProps) => {
});
};

const handleNext = (newIndex: number) => {
if (
!uploadedFiles?.length ||
!loadFile ||
newIndex < 0 ||
newIndex >= uploadedFiles.length
)
return;

const nextFile = uploadedFiles[newIndex];
if (!nextFile?.id) return;

const associating_id = nextFile.associating_id || "";
loadFile(nextFile, associating_id);
setIndex(newIndex);
};

const handleClose = () => {
setPage(1);
setNumPages(1);
setIndex(-1);
onClose?.();
};

Expand All @@ -102,23 +142,53 @@ const FilePreviewDialog = (props: FilePreviewProps) => {
: `rotate-${normalizedRotation}`;
}

useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (!show) return;
if (e.key === "ArrowLeft" && index > 0) {
handleNext(index - 1);
}
if (e.key === "ArrowRight" && index < (uploadedFiles?.length || 0) - 1) {
handleNext(index + 1);
}
};

window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [show, index, uploadedFiles]);

return (
<DialogModal
fixedWidth={false}
className="z-10 h-full w-full max-w-5xl flex-col gap-4 rounded-lg bg-white p-4 shadow-xl md:p-6"
onClose={() => {
handleClose();
}}
title={t("file_preview")}
title={<span className="text-sm text-gray-600">{t("file_preview")}</span>}
show={show}
>
{fileUrl ? (
<>
<div className="mb-2 flex flex-col items-center justify-between md:flex-row">
<p className="text-md font-semibold text-secondary-700">
{file_state.name}.{file_state.extension}
</p>
<div className="flex gap-4">
<div className="mb-2 flex flex-col items-start justify-between md:flex-row">
<div>
<p className="text-2xl font-bold text-gray-800">
{file_state.name}.{file_state.extension}
</p>
{uploadedFiles &&
uploadedFiles[index] &&
uploadedFiles[index].created_date && (
<p className="mt-1 text-sm text-gray-600">
Created on{" "}
{new Date(
uploadedFiles[index].created_date!,
).toLocaleString("en-US", {
dateStyle: "long",
timeStyle: "short",
})}
</p>
)}
</div>
<div className="flex gap-4 mt-2 md:mt-0">
{downloadURL && downloadURL.length > 0 && (
<ButtonV2>
<a
Expand All @@ -135,6 +205,19 @@ const FilePreviewDialog = (props: FilePreviewProps) => {
</div>
</div>
<div className="flex flex-1 items-center justify-center">
{uploadedFiles && uploadedFiles.length > 1 && (
<ButtonV2
className="cursor-pointer bg-primary-500 rounded-md mr-4"
onClick={() => handleNext(index - 1)}
disabled={index <= 0}
aria-label="Previous file"
onKeyDown={(e) =>
e.key === "ArrowLeft" && handleNext(index - 1)
}
>
<CareIcon icon="l-arrow-left" className="h-4 w-4" />
</ButtonV2>
)}
<div className="flex h-[75vh] w-full items-center justify-center overflow-scroll rounded-lg border border-secondary-200">
{file_state.isImage ? (
<img
Expand Down Expand Up @@ -173,6 +256,20 @@ const FilePreviewDialog = (props: FilePreviewProps) => {
</div>
)}
</div>

{uploadedFiles && uploadedFiles.length > 1 && (
<ButtonV2
className="cursor-pointer bg-primary-500 rounded-md ml-4"
onClick={() => handleNext(index + 1)}
disabled={index >= uploadedFiles.length - 1}
aria-label="Next file"
onKeyDown={(e) =>
e.key === "ArrowRight" && handleNext(index + 1)
}
>
<CareIcon icon="l-arrow-right" className="h-4 w-4" />
</ButtonV2>
)}
</div>
<div className="flex items-center justify-between">
<div className="mt-2 flex w-full flex-col justify-center gap-3 md:flex-row">
Expand Down
12 changes: 10 additions & 2 deletions src/components/Files/FileUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export interface StateInterface {
isZoomInDisabled: boolean;
isZoomOutDisabled: boolean;
rotation: number;
id?: string;
associating_id?: string;
}

export const FileUpload = (props: FileUploadProps) => {
Expand Down Expand Up @@ -208,8 +210,15 @@ export const FileUpload = (props: FileUploadProps) => {
type,
onArchive: refetchAll,
onEdit: refetchAll,
uploadedFiles:
fileQuery?.data?.results
.slice()
.reverse()
.map((file) => ({
...file,
associating_id: associatedId,
})) || [],
});

const dischargeSummaryFileManager = useFileManager({
type: "DISCHARGE_SUMMARY",
onArchive: refetchAll,
Expand Down Expand Up @@ -244,7 +253,6 @@ export const FileUpload = (props: FileUploadProps) => {
id: "record-audio",
},
];

return (
<div className={`md:p-4 ${props.className}`}>
{fileUpload.Dialogues}
Expand Down
2 changes: 0 additions & 2 deletions src/components/Patient/FileUploadPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ export default function FileUploadPage(props: {
type: "CONSULTATION" | "PATIENT";
}) {
const { facilityId, patientId, consultationId, type } = props;

const { data: patient } = useQuery(routes.getPatient, {
pathParams: { id: patientId },
prefetch: !!patientId,
});

return (
<Page
hideBack={false}
Expand Down
11 changes: 9 additions & 2 deletions src/components/Patient/PatientDetailsTab/Demography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,19 @@ export const Demography = (props: PatientProps) => {
),
},
{
label: t("date_of_birth"),
value: (
label: t(
patientData.date_of_birth ? "date_of_birth" : "year_of_birth",
),
value: patientData.date_of_birth ? (
<>
{dayjs(patientData.date_of_birth).format("DD MMM YYYY")} (
{formatPatientAge(patientData, true)})
</>
) : (
<>
{patientData.year_of_birth} ({formatPatientAge(patientData, true)}
)
</>
),
},
{
Expand Down
9 changes: 8 additions & 1 deletion src/hooks/useFileManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface FileManagerOptions {
type: string;
onArchive?: () => void;
onEdit?: () => void;
uploadedFiles?: FileUploadModel[];
}
export interface FileManagerResult {
viewFile: (file: FileUploadModel, associating_id: string) => void;
Expand All @@ -48,7 +49,7 @@ export interface FileManagerResult {
export default function useFileManager(
options: FileManagerOptions,
): FileManagerResult {
const { type: fileType, onArchive, onEdit } = options;
const { type: fileType, onArchive, onEdit, uploadedFiles } = options;

const [file_state, setFileState] = useState<StateInterface>({
open: false,
Expand All @@ -72,6 +73,7 @@ export default function useFileManager(
const [editDialogueOpen, setEditDialogueOpen] =
useState<FileUploadModel | null>(null);
const [editError, setEditError] = useState("");
const [currentIndex, setCurrentIndex] = useState<number>(-1);

const getExtension = (url: string) => {
const div1 = url.split("?")[0].split(".");
Expand All @@ -80,6 +82,8 @@ export default function useFileManager(
};

const viewFile = async (file: FileUploadModel, associating_id: string) => {
const index = uploadedFiles?.findIndex((f) => f.id === file.id) ?? -1;
setCurrentIndex(index);
setFileUrl("");
setFileState({ ...file_state, open: true });
const { data } = await request(routes.retrieveUpload, {
Expand Down Expand Up @@ -225,9 +229,12 @@ export default function useFileManager(
file_state={file_state}
setFileState={setFileState}
downloadURL={downloadURL}
uploadedFiles={uploadedFiles}
onClose={handleFilePreviewClose}
fixedWidth={false}
className="h-[80vh] w-full md:h-screen"
loadFile={viewFile}
currentIndex={currentIndex}
/>
<DialogModal
show={
Expand Down

0 comments on commit 2c521a1

Please sign in to comment.