From 0fd3547ac6044f7f27d827dcab89018488d797aa Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Tue, 28 Jan 2025 14:53:45 +0530 Subject: [PATCH 1/5] dummy care plan UI --- public/locale/en.json | 2 + src/Routers/routes/ConsultationRoutes.tsx | 12 ++ src/pages/CarePlan/CarePlan.tsx | 104 +++++++++++++ src/pages/Encounters/EncounterShow.tsx | 2 + .../Encounters/tabs/EncounterCarePlanTab.tsx | 62 ++++++++ src/types/emr/careplan/careplan.ts | 37 +++++ src/types/emr/careplan/careplanApi.ts | 143 ++++++++++++++++++ tailwind.config.js | 3 +- 8 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 src/pages/CarePlan/CarePlan.tsx create mode 100644 src/pages/Encounters/tabs/EncounterCarePlanTab.tsx create mode 100644 src/types/emr/careplan/careplan.ts create mode 100644 src/types/emr/careplan/careplanApi.ts diff --git a/public/locale/en.json b/public/locale/en.json index 1262e486770..68d48cef39c 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -48,6 +48,7 @@ "DOMESTIC_HEALTHCARE_SUPPORT__NO_SUPPORT": "No support", "DOMESTIC_HEALTHCARE_SUPPORT__PAID_CAREGIVER": "Paid caregiver", "ENCOUNTER_TAB__abdm": "ABDM Records", + "ENCOUNTER_TAB__care-plan": "Care Plan", "ENCOUNTER_TAB__feed": "Feed", "ENCOUNTER_TAB__files": "Files", "ENCOUNTER_TAB__medicines": "Medicines", @@ -491,6 +492,7 @@ "care": "CARE", "care_backend": "Care Backend", "care_frontend": "Care Frontend", + "care_plan": "Care Plan", "category": "Category", "category_description": "Choose the category that best describes the resource needed.", "caution": "Caution", diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx index 41fee1a8479..e58b913c343 100644 --- a/src/Routers/routes/ConsultationRoutes.tsx +++ b/src/Routers/routes/ConsultationRoutes.tsx @@ -3,6 +3,7 @@ import EncounterQuestionnaire from "@/components/Patient/EncounterQuestionnaire" import FileUploadPage from "@/components/Patient/FileUploadPage"; import { AppRoutes } from "@/Routers/AppRouter"; +import CarePlan from "@/pages/CarePlan/CarePlan"; import { EncounterShow } from "@/pages/Encounters/EncounterShow"; import { PrintPrescription } from "@/pages/Encounters/PrintPrescription"; @@ -75,6 +76,17 @@ const consultationRoutes: AppRoutes = { type="encounter" /> ), + "/facility/:facilityId/encounter/:encounterId/care-plan/:careplanId": ({ + facilityId, + encounterId, + careplanId, + }) => ( + + ), }; export default consultationRoutes; diff --git a/src/pages/CarePlan/CarePlan.tsx b/src/pages/CarePlan/CarePlan.tsx new file mode 100644 index 00000000000..fab8823ecd6 --- /dev/null +++ b/src/pages/CarePlan/CarePlan.tsx @@ -0,0 +1,104 @@ +import { useQuery } from "@tanstack/react-query"; +import dayjs from "dayjs"; +import { useTranslation } from "react-i18next"; + +import { Button } from "@/components/ui/button"; +import { + Table, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +import PageTitle from "@/components/Common/PageTitle"; + +import { DummyCarePlanRetrieve } from "@/types/emr/careplan/careplanApi"; + +export default function CarePlan(props: { + facilityId: string; + encounterId: string; + careplanId: string; +}) { + const { t } = useTranslation(); + + const planQuery = useQuery({ + queryKey: ["care-plan", props.careplanId], + queryFn: () => DummyCarePlanRetrieve(props.careplanId), + }); + + const careplan = planQuery.data; + + return ( +
+
+ +
+ {dayjs(careplan?.end_date).isBefore(dayjs()) ? ( + "Completed" + ) : dayjs(careplan?.start_date).isAfter(dayjs()) ? ( + Upcoming + ) : ( +
+
+ Active +
+ )} +
+
+ {dayjs(careplan?.start_date).isAfter(dayjs()) + ? "Starts on" + : "Started On"}{" "} + : {dayjs(careplan?.start_date).format("DD/MM/YYYY hh:mm a")} +
+ {dayjs(careplan?.end_date).isAfter(dayjs()) + ? "Ends on" + : "Ended On"}{" "} + : {dayjs(careplan?.end_date).format("DD/MM/YYYY hh:mm a")} +
+
{careplan?.description}
+
+ Addresses + + + + Code + Detail + + + {careplan?.addresses.map((code, i) => ( + + {code.code} + {code.display} + + ))} +
+
+
+ Goals +
+
+
+ + + + + +
+
+ ); +} diff --git a/src/pages/Encounters/EncounterShow.tsx b/src/pages/Encounters/EncounterShow.tsx index 5776ed85fe8..49f9d67143f 100644 --- a/src/pages/Encounters/EncounterShow.tsx +++ b/src/pages/Encounters/EncounterShow.tsx @@ -20,6 +20,7 @@ import { EncounterUpdatesTab } from "@/pages/Encounters/tabs/EncounterUpdatesTab import { Encounter } from "@/types/emr/encounter"; import { Patient } from "@/types/emr/newPatient"; +import { EncounterCarePlanTab } from "./tabs/EncounterCarePlanTab"; import { EncounterNotesTab } from "./tabs/EncounterNotesTab"; export interface EncounterTabProps { @@ -35,6 +36,7 @@ const defaultTabs = { medicines: EncounterMedicinesTab, files: EncounterFilesTab, notes: EncounterNotesTab, + "care-plan": EncounterCarePlanTab, // nursing: EncounterNursingTab, // neurological_monitoring: EncounterNeurologicalMonitoringTab, // pressure_sore: EncounterPressureSoreTab, diff --git a/src/pages/Encounters/tabs/EncounterCarePlanTab.tsx b/src/pages/Encounters/tabs/EncounterCarePlanTab.tsx new file mode 100644 index 00000000000..4269d5a4ab3 --- /dev/null +++ b/src/pages/Encounters/tabs/EncounterCarePlanTab.tsx @@ -0,0 +1,62 @@ +import { useQuery } from "@tanstack/react-query"; +import dayjs from "dayjs"; +import { Link } from "raviger"; + +import SubHeading from "@/CAREUI/display/SubHeading"; + +import { EncounterTabProps } from "@/pages/Encounters/EncounterShow"; +import { DummyCarePlanGet } from "@/types/emr/careplan/careplanApi"; + +export const EncounterCarePlanTab = (props: EncounterTabProps) => { + const { patient, encounter, facilityId } = props; + const { data: carePlans } = useQuery({ + queryKey: ["patient", patient.id, "care-plans"], + queryFn: DummyCarePlanGet, + }); + return ( +
+ +
+ {carePlans?.results.map((careplan, i) => ( + + {careplan.title} +
+ {dayjs(careplan.end_date).isBefore(dayjs()) ? ( + "Completed" + ) : dayjs(careplan.start_date).isAfter(dayjs()) ? ( + Upcoming + ) : ( +
+
+ Active +
+ )} +
+
{careplan.description}
+
+ {dayjs(careplan.start_date).isAfter(dayjs()) + ? "Starts on" + : "Started On"}{" "} + : {dayjs(careplan.start_date).format("DD/MM/YYYY hh:mm a")} +
+ {dayjs(careplan.end_date).isAfter(dayjs()) + ? "Ends on" + : "Ended On"}{" "} + : {dayjs(careplan.end_date).format("DD/MM/YYYY hh:mm a")} +
+
+ 3/4 Goals Complete +
+
+
+
+ + ))} +
+
+ ); +}; diff --git a/src/types/emr/careplan/careplan.ts b/src/types/emr/careplan/careplan.ts new file mode 100644 index 00000000000..9b83a1b9537 --- /dev/null +++ b/src/types/emr/careplan/careplan.ts @@ -0,0 +1,37 @@ +import { BaseModel } from "@/Utils/types"; + +export const CARE_PLAN_STATUS = [ + "draft", + "active", + "completed", + "on-hold", + "cancelled", +] as const; + +export const CARE_PLAN_INTENT = [ + "proposal", + "plan", + "order", + "option", + "directive", +] as const; + +interface Code { + code: string; + display: string; + system: string; +} + +export interface CarePlan extends BaseModel { + status: (typeof CARE_PLAN_STATUS)[number]; + intent: (typeof CARE_PLAN_INTENT)[number]; + title: string; + description: string; + start_date: string; + end_date: string; + patient: string; + encounter: string; + custodian: string; + addresses: Code[]; + notes?: string; +} diff --git a/src/types/emr/careplan/careplanApi.ts b/src/types/emr/careplan/careplanApi.ts new file mode 100644 index 00000000000..5a0d3a1d34a --- /dev/null +++ b/src/types/emr/careplan/careplanApi.ts @@ -0,0 +1,143 @@ +import { UserBareMinimum } from "@/components/Users/models"; + +import { HttpMethod, Type } from "@/Utils/request/api"; +import { PaginatedResponse } from "@/Utils/request/types"; + +import { CarePlan } from "./careplan"; + +const dummyUser: UserBareMinimum = { + id: 1, + first_name: "District", + last_name: "Admin", + last_login: "2025-01-28T07:38:56.574089Z", + user_type: "staff", + username: "devdistrictadmin", + email: "sample@email.com", + external_id: "123", +}; + +const dummyCarePlans: CarePlan[] = [ + { + id: "12345", + created_date: "2025-01-28T10:30:00Z", + modified_date: "2025-01-28T10:45:00Z", + status: "active", + intent: "plan", + title: "Chronic Condition Management Plan", + description: + "A care plan designed to manage the patient's chronic diabetes condition with regular check-ups and medication adherence.", + start_date: "2025-01-01", + end_date: "2025-12-31", + patient: "patient-001", + encounter: "encounter-456", + custodian: "Dr. John Doe", + addresses: [ + { + code: "E11", + display: "Type 2 diabetes mellitus without complications", + system: "ICD-10", + }, + { + code: "R73.03", + display: "Prediabetes", + system: "ICD-10", + }, + ], + notes: + "Patient to follow up every three months with the care team. Adherence to medication and diet plan is critical for achieving desired outcomes.", + created_by: dummyUser, + updated_by: dummyUser, + }, + { + id: "12345", + created_date: "2025-01-28T10:30:00Z", + modified_date: "2025-01-28T10:45:00Z", + status: "active", + intent: "plan", + title: "Chronic Condition Management Plan", + description: + "A care plan designed to manage the patient's chronic diabetes condition with regular check-ups and medication adherence.", + start_date: "2025-01-01", + end_date: "2025-1-21", + patient: "patient-001", + encounter: "encounter-456", + custodian: "Dr. John Doe", + addresses: [ + { + code: "E11", + display: "Type 2 diabetes mellitus without complications", + system: "ICD-10", + }, + { + code: "R73.03", + display: "Prediabetes", + system: "ICD-10", + }, + ], + notes: + "Patient to follow up every three months with the care team. Adherence to medication and diet plan is critical for achieving desired outcomes.", + created_by: dummyUser, + updated_by: dummyUser, + }, + { + id: "12345", + created_date: "2025-01-28T10:30:00Z", + modified_date: "2025-01-28T10:45:00Z", + status: "active", + intent: "plan", + title: "Chronic Condition Management Plan", + description: + "A care plan designed to manage the patient's chronic diabetes condition with regular check-ups and medication adherence.", + start_date: "2025-02-01", + end_date: "2025-12-31", + patient: "patient-001", + encounter: "encounter-456", + custodian: "Dr. John Doe", + addresses: [ + { + code: "E11", + display: "Type 2 diabetes mellitus without complications", + system: "ICD-10", + }, + { + code: "R73.03", + display: "Prediabetes", + system: "ICD-10", + }, + ], + notes: + "Patient to follow up every three months with the care team. Adherence to medication and diet plan is critical for achieving desired outcomes.", + created_by: dummyUser, + updated_by: dummyUser, + }, +]; + +export const DummyCarePlanGet: () => Promise< + PaginatedResponse +> = async () => { + return { + count: 1, + next: "", + previous: "", + results: dummyCarePlans, + }; +}; + +export const DummyCarePlanRetrieve: ( + id: string, +) => Promise = async (id: string) => { + return dummyCarePlans.find((cp) => cp.id === id); +}; + +export default { + getCarePlans: { + path: "/api/v1/patient/{patientId}/care_plan/", + method: HttpMethod.GET, + TRes: Type>(), + }, + retrieveCarePlan: { + path: "/api/v1/patient/{patientId}/care_plan/{carePlanId}/", + method: HttpMethod.GET, + TRes: Type(), + }, +}; diff --git a/tailwind.config.js b/tailwind.config.js index 3235b2673d7..9494c606cfe 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ const colors = require("tailwindcss/colors"); const secondary = { @@ -116,7 +115,7 @@ module.exports = { }, content: [ "./src/**/*.{html,md,js,jsx,ts,tsx}", - "./apps/**/*.{html,md,js,jsx,ts,tsx}", + "./apps/**/!(node_modules)/*.{html,md,js,jsx,ts,tsx}", "./index.html", ], plugins: [ From b329d815d937666e499b0c19b4084623e956b491 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Wed, 29 Jan 2025 15:06:02 +0530 Subject: [PATCH 2/5] some more updates --- src/pages/CarePlan/CarePlan.tsx | 44 +++++++++--- .../Encounters/tabs/EncounterCarePlanTab.tsx | 6 +- src/types/emr/careplan/careplan.ts | 45 ++++++++++++ src/types/emr/careplan/careplanApi.ts | 68 +++++++++++++++++-- 4 files changed, 147 insertions(+), 16 deletions(-) diff --git a/src/pages/CarePlan/CarePlan.tsx b/src/pages/CarePlan/CarePlan.tsx index fab8823ecd6..a96e0613380 100644 --- a/src/pages/CarePlan/CarePlan.tsx +++ b/src/pages/CarePlan/CarePlan.tsx @@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { Table, + TableBody, TableCell, TableHead, TableHeader, @@ -13,7 +14,10 @@ import { import PageTitle from "@/components/Common/PageTitle"; -import { DummyCarePlanRetrieve } from "@/types/emr/careplan/careplanApi"; +import { + dummyCarePlanGoalList, + dummyCarePlanRetrieve, +} from "@/types/emr/careplan/careplanApi"; export default function CarePlan(props: { facilityId: string; @@ -24,10 +28,16 @@ export default function CarePlan(props: { const planQuery = useQuery({ queryKey: ["care-plan", props.careplanId], - queryFn: () => DummyCarePlanRetrieve(props.careplanId), + queryFn: () => dummyCarePlanRetrieve(props.careplanId), + }); + + const goalsQuery = useQuery({ + queryKey: ["care-plan-goals", props.careplanId], + queryFn: () => dummyCarePlanGoalList(props.careplanId), }); const careplan = planQuery.data; + const goals = goalsQuery.data?.results; return (
@@ -70,16 +80,34 @@ export default function CarePlan(props: { Detail - {careplan?.addresses.map((code, i) => ( - - {code.code} - {code.display} - - ))} + + {careplan?.addresses.map((code, i) => ( + + {code.code} + {code.display} + + ))} +
Goals +
+ {goals?.map((goal, i) => ( +
+
{goal.description}
+ + {goal.lifecycle_status} + + +
+ ))} +
diff --git a/src/pages/Encounters/tabs/EncounterCarePlanTab.tsx b/src/pages/Encounters/tabs/EncounterCarePlanTab.tsx index 4269d5a4ab3..65bbcdfdb08 100644 --- a/src/pages/Encounters/tabs/EncounterCarePlanTab.tsx +++ b/src/pages/Encounters/tabs/EncounterCarePlanTab.tsx @@ -5,13 +5,13 @@ import { Link } from "raviger"; import SubHeading from "@/CAREUI/display/SubHeading"; import { EncounterTabProps } from "@/pages/Encounters/EncounterShow"; -import { DummyCarePlanGet } from "@/types/emr/careplan/careplanApi"; +import { dummyCarePlanList } from "@/types/emr/careplan/careplanApi"; export const EncounterCarePlanTab = (props: EncounterTabProps) => { const { patient, encounter, facilityId } = props; const { data: carePlans } = useQuery({ queryKey: ["patient", patient.id, "care-plans"], - queryFn: DummyCarePlanGet, + queryFn: dummyCarePlanList, }); return (
@@ -21,7 +21,7 @@ export const EncounterCarePlanTab = (props: EncounterTabProps) => { {careplan.title}
diff --git a/src/types/emr/careplan/careplan.ts b/src/types/emr/careplan/careplan.ts index 9b83a1b9537..71a62b88c80 100644 --- a/src/types/emr/careplan/careplan.ts +++ b/src/types/emr/careplan/careplan.ts @@ -16,6 +16,30 @@ export const CARE_PLAN_INTENT = [ "directive", ] as const; +export const CARE_PLAN_LIFECYCLE_STATUS = [ + "proposed", + "planned", + "accepted", + "active", + "on-hold", + "completed", + "cancelled", + "entered-in-error", + "rejected", +] as const; + +export const CARE_PLAN_ACHIEVEMENT_STATUS = [ + "in-progress", + "improving", + "worsening", + "no-change", + "achieved", + "sustaining", + "not-achieved", + "no-progress", + "not-attainable", +] as const; + interface Code { code: string; display: string; @@ -35,3 +59,24 @@ export interface CarePlan extends BaseModel { addresses: Code[]; notes?: string; } + +export interface CarePlanGoal extends BaseModel { + care_plan: string; + lifecycle_status: (typeof CARE_PLAN_LIFECYCLE_STATUS)[number]; + achievement_status: (typeof CARE_PLAN_ACHIEVEMENT_STATUS)[number]; + is_continuous: boolean; + priority: number; + description: string; + start_date: string; + requested_by_patient: boolean; + notes?: string; + outcome?: unknown; + targets?: unknown; + permitted_groups?: unknown; +} + +export interface GoalUpdate extends BaseModel { + goal: string; + target_values: unknown; + notes?: string; +} diff --git a/src/types/emr/careplan/careplanApi.ts b/src/types/emr/careplan/careplanApi.ts index 5a0d3a1d34a..c2a32d784fa 100644 --- a/src/types/emr/careplan/careplanApi.ts +++ b/src/types/emr/careplan/careplanApi.ts @@ -3,7 +3,7 @@ import { UserBareMinimum } from "@/components/Users/models"; import { HttpMethod, Type } from "@/Utils/request/api"; import { PaginatedResponse } from "@/Utils/request/types"; -import { CarePlan } from "./careplan"; +import { CarePlan, CarePlanGoal, GoalUpdate } from "./careplan"; const dummyUser: UserBareMinimum = { id: 1, @@ -16,6 +16,36 @@ const dummyUser: UserBareMinimum = { external_id: "123", }; +const dummyGoals: CarePlanGoal[] = [ + { + care_plan: "12345", + id: "12345", + created_date: "2025-01-28T10:30:00Z", + modified_date: "2025-01-28T10:45:00Z", + lifecycle_status: "accepted", + achievement_status: "achieved", + is_continuous: true, + priority: 1, + description: "Reduce patient temperature to below 37°C", + start_date: "2025-01-01", + requested_by_patient: false, + created_by: dummyUser, + updated_by: dummyUser, + }, +]; + +const dummyGoalUpdates: GoalUpdate[] = [ + { + id: "12345", + created_date: "2025-01-28T10:30:00Z", + modified_date: "2025-01-28T10:45:00Z", + created_by: dummyUser, + updated_by: dummyUser, + goal: "12345", + target_values: {}, + }, +]; + const dummyCarePlans: CarePlan[] = [ { id: "12345", @@ -23,9 +53,9 @@ const dummyCarePlans: CarePlan[] = [ modified_date: "2025-01-28T10:45:00Z", status: "active", intent: "plan", - title: "Chronic Condition Management Plan", + title: "Fever Management Plan", description: - "A care plan designed to manage the patient's chronic diabetes condition with regular check-ups and medication adherence.", + "A care plan designed to manage the patient's fever with regular check-ups and medication adherence.", start_date: "2025-01-01", end_date: "2025-12-31", patient: "patient-001", @@ -112,7 +142,35 @@ const dummyCarePlans: CarePlan[] = [ }, ]; -export const DummyCarePlanGet: () => Promise< +export const dummyCarePlanGoalList: ( + id: string, +) => Promise> = async (id: string) => { + return { + count: 1, + next: "", + previous: "", + results: dummyGoals.filter((goal) => goal.care_plan === id), + }; +}; + +export const dummyCarePlanGoalRetrieve: ( + id: string, +) => Promise = async (id: string) => { + return dummyGoals.find((goal) => goal.id === id); +}; + +export const dummyCarePlanGoalUpdateList: ( + id: string, +) => Promise> = async (id: string) => { + return { + count: 1, + next: "", + previous: "", + results: dummyGoalUpdates.filter((update) => update.goal === id), + }; +}; + +export const dummyCarePlanList: () => Promise< PaginatedResponse > = async () => { return { @@ -123,7 +181,7 @@ export const DummyCarePlanGet: () => Promise< }; }; -export const DummyCarePlanRetrieve: ( +export const dummyCarePlanRetrieve: ( id: string, ) => Promise = async (id: string) => { return dummyCarePlans.find((cp) => cp.id === id); From 6126694ea90937003a0f85f2801a887546b33afd Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Thu, 30 Jan 2025 03:29:10 +0530 Subject: [PATCH 3/5] Added care plan question --- public/locale/en.json | 11 + src/Routers/routes/ConsultationRoutes.tsx | 10 + .../QuestionTypes/CarePlanQuestion.tsx | 530 ++++++++++++++++++ .../QuestionTypes/QuestionInput.tsx | 40 +- .../Questionnaire/data/StructuredFormData.tsx | 21 + .../Questionnaire/structured/handlers.ts | 18 + .../Questionnaire/structured/types.ts | 3 + src/pages/CarePlan/CarePlan.tsx | 30 +- src/pages/CarePlan/CarePlanGoalPage.tsx | 188 +++++++ src/types/emr/careplan/careplan.ts | 57 +- src/types/emr/careplan/careplanApi.ts | 109 +++- src/types/questionnaire/form.ts | 7 +- src/types/questionnaire/question.ts | 3 +- 13 files changed, 981 insertions(+), 46 deletions(-) create mode 100644 src/components/Questionnaire/QuestionTypes/CarePlanQuestion.tsx create mode 100644 src/pages/CarePlan/CarePlanGoalPage.tsx diff --git a/public/locale/en.json b/public/locale/en.json index 5de2c398c1a..998b59df1df 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -289,12 +289,14 @@ "add_attachments": "Add Attachments", "add_beds": "Add Bed(s)", "add_beds_to_configure_presets": "Add beds to this location to configure presets for them.", + "add_care_plan": "Add Care Plan", "add_consultation": "Add consultation", "add_consultation_update": "Add Consultation Update", "add_details_of_patient": "Add Details of Patient", "add_exception": "Add Exception", "add_facility": "Add Facility", "add_files": "Add Files", + "add_goal": "Add Goal", "add_insurance_details": "Add Insurance Details", "add_location": "Add Location", "add_new_beds": "Add New Bed(s)", @@ -318,6 +320,7 @@ "add_skill": "Add Skill", "add_spoke": "Add Spoke Facility", "add_tags": "Add Tags", + "add_target": "Add Target", "add_user": "Add User", "additional_information": "Additional Information", "additional_instructions": "Additional Instructions", @@ -493,6 +496,9 @@ "care_backend": "Care Backend", "care_frontend": "Care Frontend", "care_plan": "Care Plan", + "care_plan_goal": "Care Plan Goal", + "careplan_intent": "Care Plan Intent", + "careplan_status": "Care Plan Status", "category": "Category", "category_description": "Choose the category that best describes the resource needed.", "caution": "Caution", @@ -896,6 +902,7 @@ "encounter_suggestion__R": "Consultation", "encounter_suggestion_edit_disallowed": "Not allowed to switch to this option in edit consultation", "encounters": "Encounters", + "end_date": "End Date", "end_datetime": "End Date/Time", "end_dose": "End Dose", "end_time": "End Time", @@ -1034,6 +1041,7 @@ "getting_location": "Getting Location...", "go_back": "Go Back", "goal": "Our goal is to continuously improve the quality and accessibility of public healthcare services using digital tools.", + "goals": "Goals", "granted_on": "Granted On", "has_allergies": "Has Allergies", "has_domestic_healthcare_support": "Has domestic healthcare support?", @@ -1161,6 +1169,7 @@ "license": "License", "licenses_description": "Third-party software is used in Care, including the respective licenses and versions.", "licenses_title": "Third-Party Software and Licenses", + "lifecycle_status": "Lifecycle Status", "link_abha_number": "Link ABHA Number", "link_abha_profile": "Link ABHA Profile", "link_camera_and_bed": "Link bed to Camera", @@ -1972,6 +1981,7 @@ "tag_slug": "Tag Slug", "taper_titrate_dosage": "Taper & Titrate Dosage", "target_dosage": "Target Dosage", + "targets": "Targets", "template_deleted": "Template has been deleted", "test_type": "Type of test done", "tested_on": "Tested on", @@ -1980,6 +1990,7 @@ "third_party_software_licenses": "Third Party Software Licenses", "time": "Time", "time_slot": "Time Slot", + "title": "Title", "title_of_request": "Title of Request", "titrate_dosage": "Titrate Dosage", "to": "to", diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx index e58b913c343..2beb93bb1a9 100644 --- a/src/Routers/routes/ConsultationRoutes.tsx +++ b/src/Routers/routes/ConsultationRoutes.tsx @@ -4,6 +4,7 @@ import FileUploadPage from "@/components/Patient/FileUploadPage"; import { AppRoutes } from "@/Routers/AppRouter"; import CarePlan from "@/pages/CarePlan/CarePlan"; +import CarePlanGoalPage from "@/pages/CarePlan/CarePlanGoalPage"; import { EncounterShow } from "@/pages/Encounters/EncounterShow"; import { PrintPrescription } from "@/pages/Encounters/PrintPrescription"; @@ -87,6 +88,15 @@ const consultationRoutes: AppRoutes = { careplanId={careplanId} /> ), + "/facility/:facilityId/encounter/:encounterId/care-plan/:careplanId/goal/:goalId": + ({ facilityId, encounterId, careplanId, goalId }) => ( + + ), }; export default consultationRoutes; diff --git a/src/components/Questionnaire/QuestionTypes/CarePlanQuestion.tsx b/src/components/Questionnaire/QuestionTypes/CarePlanQuestion.tsx new file mode 100644 index 00000000000..7bde1f8c52c --- /dev/null +++ b/src/components/Questionnaire/QuestionTypes/CarePlanQuestion.tsx @@ -0,0 +1,530 @@ +import { DotsVerticalIcon, MinusCircledIcon } from "@radix-ui/react-icons"; +import { useTranslation } from "react-i18next"; + +import { Button } from "@/components/ui/button"; +import { DateTimePicker } from "@/components/ui/date-time-picker"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Textarea } from "@/components/ui/textarea"; + +import { + CARE_PLAN_INTENT, + CARE_PLAN_LIFECYCLE_STATUS, + CARE_PLAN_STATUS, + CarePlanGoalRequest, + CarePlanRequest, + GoalTarget, +} from "@/types/emr/careplan/careplan"; +import { + QuestionnaireResponse, + ResponseValue, +} from "@/types/questionnaire/form"; +import { Question } from "@/types/questionnaire/question"; + +interface CarePlanQuestionProps { + question: Question; + questionnaireResponse: QuestionnaireResponse; + updateQuestionnaireResponseCB: ( + values: ResponseValue[], + questionId: string, + note?: string, + ) => void; + disabled?: boolean; + encounterId?: string; + patientId: string; + facilityId: string; +} + +const sampleCodes = [ + { + code: "8480-6", + system: "http://loinc.org", + display: "Systolic", + }, + { + code: "8462-4", + system: "http://loinc.org", + display: "Diastolic", + }, + { + code: "8867-4", + system: "http://loinc.org", + display: "Pulse", + }, + { + code: "8310-5", + system: "http://loinc.org", + display: "Temperature", + }, + { + code: "9279-1", + system: "http://loinc.org", + display: "Respiratory Rate", + }, +]; + +export default function CarePlanQuestion({ + questionnaireResponse, + updateQuestionnaireResponseCB, + disabled, + patientId, + encounterId, + facilityId, +}: CarePlanQuestionProps) { + const { t } = useTranslation(); + const careplans = + (questionnaireResponse.values?.[0] + ?.value as unknown as CarePlanRequest[]) || []; + + const handleUpdate = (index: number, updates: Partial) => { + const newCarePlans = careplans.map((careplan, i) => + i === index ? { ...careplan, ...updates } : careplan, + ); + + updateQuestionnaireResponseCB( + [{ type: "care_plan", value: newCarePlans }], + questionnaireResponse.question_id, + ); + }; + + const handleGoalUpdate = ( + cpIndex: number, + index: number, + updates: Partial, + ) => { + const newCarePlans = careplans.map((careplan, i) => + i === cpIndex + ? { + ...careplan, + goals: careplan.goals?.map((goal, j) => + j === index ? { ...goal, ...updates } : goal, + ), + } + : careplan, + ); + + updateQuestionnaireResponseCB( + [{ type: "care_plan", value: newCarePlans }], + questionnaireResponse.question_id, + ); + }; + + const handleGoalTargetUpdate = ( + cpIndex: number, + goalIndex: number, + index: number, + updates: Partial, + ) => { + const newCarePlans = careplans.map((careplan, i) => + i === cpIndex + ? { + ...careplan, + goals: careplan.goals?.map((goal, j) => + j === goalIndex + ? { + ...goal, + target: goal.target?.map((target, k) => + k === index ? { ...target, ...updates } : target, + ), + } + : goal, + ), + } + : careplan, + ); + + updateQuestionnaireResponseCB( + [{ type: "care_plan", value: newCarePlans }], + questionnaireResponse.question_id, + ); + }; + + return ( +
+
+ {careplans.map((careplan, i) => ( +
+
+ + + + + + { + const newCarePlans = careplans.filter((_, j) => j !== i); + updateQuestionnaireResponseCB( + [{ type: "care_plan", value: newCarePlans }], + questionnaireResponse.question_id, + ); + }} + > + + {t("remove")} + + + +
+
+
+ + handleUpdate(i, { title: e.target.value })} + disabled={disabled} + /> +
+
+
+ + +
+
+ + +
+
+
+ +