diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx
index 080b90a0a54..570667419e0 100644
--- a/src/Routers/routes/ConsultationRoutes.tsx
+++ b/src/Routers/routes/ConsultationRoutes.tsx
@@ -29,6 +29,7 @@ const consultationRoutes: AppRoutes = {
facilityId={facilityId}
encounterId={encounterId}
patientId={patientId}
+ subjectType="encounter"
/>
),
"/facility/:facilityId/patient/:patientId/encounter/:encounterId/questionnaire/:slug":
@@ -38,35 +39,37 @@ const consultationRoutes: AppRoutes = {
encounterId={encounterId}
questionnaireSlug={slug}
patientId={patientId}
+ subjectType="encounter"
/>
),
+
"/facility/:facilityId/patient/:patientId/encounter/:encounterId/questionnaire_response/:id":
({ patientId, id }) => (
),
- "/facility/:facilityId/patient/:patientId/encounter/:encounterId/:tab": ({
- facilityId,
- patientId,
- encounterId,
- tab,
- }) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/encounter/:encounterId/:tab/:subPage":
- ({ facilityId, encounterId, patientId, tab, subPage }) => (
+ ...["facility", "organization"].reduce((acc: AppRoutes, identifier) => {
+ acc[`/${identifier}/:id/patient/:patientId/encounter/:encounterId/:tab`] =
+ ({ id, encounterId, tab, patientId }) => (
+
+ );
+ acc[
+ `/${identifier}/:id/patient/:patientId/encounter/:encounterId/:tab/:subPage`
+ ] = ({ id, encounterId, patientId, tab, subPage }) => (
- ),
+ );
+ return acc;
+ }, {}),
"/facility/:facilityId/patient/:patientId/consultation": ({
facilityId,
patientId,
@@ -87,6 +90,9 @@ const consultationRoutes: AppRoutes = {
subjectType="patient"
/>
),
+ "/patient/:patientId/questionnaire": ({ patientId }) => (
+
+ ),
};
export default consultationRoutes;
diff --git a/src/Routers/routes/PatientRoutes.tsx b/src/Routers/routes/PatientRoutes.tsx
index 26b2ff4aa38..090061a225a 100644
--- a/src/Routers/routes/PatientRoutes.tsx
+++ b/src/Routers/routes/PatientRoutes.tsx
@@ -1,7 +1,4 @@
-import {
- facilityPatientTabs,
- patientTabs,
-} from "@/components/Patient/PatientDetailsTab";
+import { patientTabs } from "@/components/Patient/PatientDetailsTab";
import { PatientHome } from "@/components/Patient/PatientHome";
import PatientIndex from "@/components/Patient/PatientIndex";
import PatientRegistration from "@/components/Patient/PatientRegistration";
@@ -34,7 +31,7 @@ const PatientRoutes: AppRoutes = {
"/facility/:facilityId/patient/:id": ({ facilityId, id }) => (
),
- ...facilityPatientTabs.reduce((acc: AppRoutes, tab) => {
+ ...patientTabs.reduce((acc: AppRoutes, tab) => {
acc["/facility/:facilityId/patient/:id/" + tab.route] = ({
facilityId,
id,
diff --git a/src/components/Auth/Login.tsx b/src/components/Auth/Login.tsx
index 87eb7c10004..e8b3d128639 100644
--- a/src/components/Auth/Login.tsx
+++ b/src/components/Auth/Login.tsx
@@ -69,8 +69,14 @@ interface LoginProps {
const Login = (props: LoginProps) => {
const { signIn, patientLogin, isAuthenticating } = useAuthContext();
- const { reCaptchaSiteKey, urls, stateLogo, customLogo, customLogoAlt } =
- careConfig;
+ const {
+ reCaptchaSiteKey,
+ urls,
+ stateLogo,
+ customLogo,
+ customLogoAlt,
+ resendOtpTimeout,
+ } = careConfig;
const customDescriptionHtml = __CUSTOM_DESCRIPTION_HTML__;
const initForm: any = {
username: "",
@@ -93,6 +99,21 @@ const Login = (props: LoginProps) => {
const [otp, setOtp] = useState("");
const [otpError, setOtpError] = useState
("");
const [otpValidationError, setOtpValidationError] = useState("");
+ const [resendOtpCountdown, setResendOtpCountdown] =
+ useState(resendOtpTimeout);
+
+ // Timer Function for resend OTP
+ useEffect(() => {
+ if (resendOtpCountdown <= 0) {
+ return;
+ }
+
+ const timer = setInterval(() => {
+ setResendOtpCountdown((prevTime) => prevTime - 1);
+ }, 1000);
+
+ return () => clearInterval(timer);
+ }, []);
// Remember the last login mode
useEffect(() => {
@@ -272,6 +293,7 @@ const Login = (props: LoginProps) => {
if (!isOtpSent) {
sendOtp({ phone_number: phone });
+ setResendOtpCountdown(resendOtpTimeout);
} else {
verifyOtp({ phone_number: phone, otp });
}
@@ -673,6 +695,28 @@ const Login = (props: LoginProps) => {
t("send_otp")
)}
+ {isOtpSent &&
+ (resendOtpCountdown <= 0 ? (
+
+
+
+ ) : (
+
+ {t("resend_otp_timer", {
+ time: resendOtpCountdown,
+ })}
+
+ ))}
diff --git a/src/components/Common/FilePreviewDialog.tsx b/src/components/Common/FilePreviewDialog.tsx
index 7aaabf84dd5..ea03c164153 100644
--- a/src/components/Common/FilePreviewDialog.tsx
+++ b/src/components/Common/FilePreviewDialog.tsx
@@ -1,6 +1,3 @@
-import { TooltipContent, TooltipTrigger } from "@radix-ui/react-tooltip";
-import { TooltipProvider } from "@radix-ui/react-tooltip";
-import { Tooltip } from "@radix-ui/react-tooltip";
import {
Dispatch,
ReactNode,
@@ -24,6 +21,7 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
+import { TooltipComponent } from "@/components/ui/tooltip";
import CircularProgress from "@/components/Common/CircularProgress";
import { FileUploadModel } from "@/components/Patient/models";
@@ -191,20 +189,11 @@ const FilePreviewDialog = (props: FilePreviewProps) => {
<>
-
-
-
-
- {fileNameTooltip}
-
-
-
-
- {fileName}
-
-
-
-
+
+
+ {fileNameTooltip}
+
+
{uploadedFiles &&
uploadedFiles[index] &&
uploadedFiles[index].created_date && (
diff --git a/src/components/Encounter/EncounterActions.tsx b/src/components/Encounter/EncounterActions.tsx
new file mode 100644
index 00000000000..5919290ddfc
--- /dev/null
+++ b/src/components/Encounter/EncounterActions.tsx
@@ -0,0 +1,202 @@
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { Link } from "raviger";
+import { useTranslation } from "react-i18next";
+import { toast } from "sonner";
+
+import { cn } from "@/lib/utils";
+
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/components/ui/alert-dialog";
+import { Button, buttonVariants } from "@/components/ui/button";
+import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
+
+import { PLUGIN_Component } from "@/PluginEngine";
+import routes from "@/Utils/request/api";
+import mutate from "@/Utils/request/mutate";
+import { Encounter, completedEncounterStatus } from "@/types/emr/encounter";
+
+interface EncounterActionsProps {
+ encounter: Encounter;
+ variant?: "default" | "outline" | "ghost";
+ size?: "default" | "sm" | "lg" | "icon";
+ disableButtons?: boolean;
+ className?: string;
+ layout?: "dropdown" | "standalone";
+}
+
+export default function EncounterActions({
+ encounter,
+ variant = "outline",
+ size = "default",
+ disableButtons = false,
+ className,
+ layout = "standalone",
+}: EncounterActionsProps) {
+ const { t } = useTranslation();
+ const queryClient = useQueryClient();
+
+ const { mutate: updateEncounter } = useMutation({
+ mutationFn: mutate(routes.encounter.update, {
+ pathParams: { id: encounter.id },
+ }),
+ onSuccess: () => {
+ toast.success(t("encounter_marked_as_complete"));
+ queryClient.invalidateQueries({ queryKey: ["encounter", encounter.id] });
+ },
+ onError: () => {
+ toast.error(t("error_updating_encounter"));
+ },
+ });
+
+ const handleMarkAsComplete = () => {
+ updateEncounter({
+ ...encounter,
+ status: "completed",
+ organizations: encounter.organizations.map((org) => org.id),
+ patient: encounter.patient.id,
+ encounter_class: encounter.encounter_class,
+ period: encounter.period,
+ hospitalization: encounter.hospitalization,
+ priority: encounter.priority,
+ external_identifier: encounter.external_identifier,
+ facility: encounter.facility.id,
+ });
+ };
+
+ if (completedEncounterStatus.includes(encounter.status) || disableButtons) {
+ return null;
+ }
+
+ const ActionItems = () => {
+ if (layout === "dropdown") {
+ return (
+ <>
+
+
+ {t("treatment_summary")}
+
+
+
+
+ {t("discharge_summary")}
+
+
+
+
+ e.preventDefault()}>
+ {t("mark_as_complete")}
+
+
+
+
+ {t("mark_as_complete")}
+
+ {t("mark_encounter_as_complete_confirmation")}
+
+
+
+
+
+
+ {t("cancel")}
+
+ {t("mark_as_complete")}
+
+
+
+
+ >
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ {t("mark_as_complete")}
+
+ {t("mark_encounter_as_complete_confirmation")}
+
+
+
+
+
+
+ {t("cancel")}
+
+ {t("mark_as_complete")}
+
+
+
+
+
+
+ );
+ };
+
+ return ActionItems();
+}
diff --git a/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx b/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
index c6fc5667222..bba948cfd72 100644
--- a/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
+++ b/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
@@ -25,6 +25,7 @@ import { QuestionnaireResponse } from "@/types/questionnaire/questionnaireRespon
interface Props {
encounter?: Encounter;
patientId: string;
+ facilityId?: string;
isPrintPreview?: boolean;
onlyUnstructured?: boolean;
}
@@ -312,6 +313,7 @@ export default function QuestionnaireResponsesList({
}),
encounter: encounter?.id,
only_unstructured: onlyUnstructured,
+ subject_type: encounter ? "encounter" : "patient",
},
maxPages: isPrintPreview ? undefined : 1,
pageSize: isPrintPreview ? 100 : RESULTS_PER_PAGE_LIMIT,
diff --git a/src/components/Facility/ConsultationDetails/QuickAccess.tsx b/src/components/Facility/ConsultationDetails/QuickAccess.tsx
index 649bfe6ef8b..6c23a0b90ed 100644
--- a/src/components/Facility/ConsultationDetails/QuickAccess.tsx
+++ b/src/components/Facility/ConsultationDetails/QuickAccess.tsx
@@ -6,6 +6,7 @@ import CareIcon from "@/CAREUI/icons/CareIcon";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
+import EncounterActions from "@/components/Encounter/EncounterActions";
import LinkDepartmentsSheet from "@/components/Patient/LinkDepartmentsSheet";
import useQuestionnaireOptions from "@/hooks/useQuestionnaireOptions";
@@ -44,6 +45,15 @@ export default function QuickAccess({ encounter }: QuickAccessProps) {
)}
+ {/* Encounter Actions */}
+
+
+ {t("actions")}
+
+
+
+
+
{/* Departments and Teams */}
diff --git a/src/components/Facility/EncounterCard.tsx b/src/components/Facility/EncounterCard.tsx
index a9fc72dff59..f20a58d7bee 100644
--- a/src/components/Facility/EncounterCard.tsx
+++ b/src/components/Facility/EncounterCard.tsx
@@ -16,10 +16,11 @@ import { Encounter, completedEncounterStatus } from "@/types/emr/encounter";
interface EncounterCardProps {
encounter: Encounter;
+ facilityId?: string;
}
export const EncounterCard = (props: EncounterCardProps) => {
- const { encounter } = props;
+ const { encounter, facilityId } = props;
const Icon = encounterIcons[encounter.encounter_class];
@@ -118,7 +119,11 @@ export const EncounterCard = (props: EncounterCardProps) => {