diff --git a/cypress/e2e/users_spec/user_creation.cy.ts b/cypress/e2e/users_spec/user_creation.cy.ts index 62a4bd53601..0e2707b921d 100644 --- a/cypress/e2e/users_spec/user_creation.cy.ts +++ b/cypress/e2e/users_spec/user_creation.cy.ts @@ -1,10 +1,10 @@ -import { UserCreation } from "../../pageObject/Users/UserCreation"; -import { FacilityCreation } from "../../pageObject/facility/FacilityCreation"; +import { UserCreation } from "pageObject/Users/UserCreation"; +import { FacilityCreation } from "pageObject/facility/FacilityCreation"; import { generateName, generatePhoneNumber, generateUsername, -} from "../../utils/commonUtils"; +} from "utils/commonUtils"; describe("User Creation", () => { const facilityCreation = new FacilityCreation(); @@ -30,7 +30,6 @@ describe("User Creation", () => { confirmPassword: defaultPassword, email: `${generateUsername(firstName)}@test.com`, phoneNumber: generatePhoneNumber(), - dateOfBirth: "1990-01-01", userType: "Doctor", state: "Kerala", district: "Ernakulam", diff --git a/cypress/pageObject/Users/UserCreation.ts b/cypress/pageObject/Users/UserCreation.ts index 181ad9d0649..c85b5998343 100644 --- a/cypress/pageObject/Users/UserCreation.ts +++ b/cypress/pageObject/Users/UserCreation.ts @@ -5,7 +5,6 @@ export interface UserData { password?: string; email?: string; phoneNumber?: string; - dateOfBirth?: string; userType?: string; state?: string; district?: string; @@ -78,17 +77,11 @@ export class UserCreation { label: "Alternate Phone Number", message: "Phone number must start with +91 followed by 10 digits", }, - { label: "Date of Birth", message: "Required" }, { label: "State", message: "Required" }, ]); return this; } - fillDateOfBirth(dateOfBirth: string) { - cy.typeIntoField('[data-cy="dob-input"]', dateOfBirth); - return this; - } - selectUserType(userType: string) { cy.clickAndSelectOption('[data-cy="user-type-select"]', userType); return this; @@ -125,7 +118,6 @@ export class UserCreation { } if (userData.email) this.fillEmail(userData.email); if (userData.phoneNumber) this.fillPhoneNumber(userData.phoneNumber); - if (userData.dateOfBirth) this.fillDateOfBirth(userData.dateOfBirth); if (userData.state) this.selectState(userData.state); if (userData.district) this.selectDistrict(userData.district); if (userData.localBody) this.selectLocalBody(userData.localBody); diff --git a/package-lock.json b/package-lock.json index 1699ecaff7b..bd5d23bae55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "@radix-ui/react-tooltip": "^1.1.6", "@sentry/browser": "^8.50.0", "@tanstack/react-query": "^5.64.1", - "@tanstack/react-query-devtools": "^5.64.0", + "@tanstack/react-query-devtools": "^5.64.2", "@vitejs/plugin-react": "^4.3.4", "@yudiel/react-qr-scanner": "^2.1.0", "bowser": "^2.11.0", @@ -64,7 +64,7 @@ "i18next-http-backend": "^3.0.1", "input-otp": "^1.4.2", "lodash-es": "^4.17.21", - "lucide-react": "^0.471.1", + "lucide-react": "^0.473.0", "markdown-it": "^14.1.0", "next-themes": "^0.4.3", "postcss-loader": "^8.1.1", @@ -6288,9 +6288,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.64.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.64.1.tgz", - "integrity": "sha512-978Wx4Wl4UJZbmvU/rkaM9cQtXXrbhK0lsz/UZhYIbyKYA8E4LdomTwyh2GHZ4oU0BKKoDH4YlKk2VscCUgNmg==", + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.64.2.tgz", + "integrity": "sha512-hdO8SZpWXoADNTWXV9We8CwTkXU88OVWRBcsiFrk7xJQnhm6WRlweDzMD+uH+GnuieTBVSML6xFa17C2cNV8+g==", "license": "MIT", "funding": { "type": "github", @@ -6298,9 +6298,9 @@ } }, "node_modules/@tanstack/query-devtools": { - "version": "5.62.16", - "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.62.16.tgz", - "integrity": "sha512-3ff6UBJr0H3nIhfLSl9911rvKqXf0u4B58jl0uYdDWLqPk9pCvYIbxC35cGxK2+8INl4IaFVUHb/IdgWrNkg3Q==", + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.64.2.tgz", + "integrity": "sha512-3DautR5UpVZdk/qNIhioZVF7g8fdQZ1U98sBEEk4Tzz3tihSBNMPgwlP40nzgbPEDBIrn/j/oyyvNBVSo083Vw==", "license": "MIT", "funding": { "type": "github", @@ -6308,12 +6308,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.64.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.64.1.tgz", - "integrity": "sha512-vW5ggHpIO2Yjj44b4sB+Fd3cdnlMJppXRBJkEHvld6FXh3j5dwWJoQo7mGtKI2RbSFyiyu/PhGAy0+Vv5ev9Eg==", + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.64.2.tgz", + "integrity": "sha512-3pakNscZNm8KJkxmovvtZ4RaXLyiYYobwleTMvpIGUoKRa8j8VlrQKNl5W8VUEfVfZKkikvXVddLuWMbcSCA1Q==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.64.1" + "@tanstack/query-core": "5.64.2" }, "funding": { "type": "github", @@ -6324,19 +6324,19 @@ } }, "node_modules/@tanstack/react-query-devtools": { - "version": "5.64.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.64.0.tgz", - "integrity": "sha512-XORJjlbcBwPJaNbWBfZudaVVMi5TtlN1lYkHYU71hlG2c/jYpceO2yfAhZfgeyTNtqmTJ7jXOitgoGqtunsBAA==", + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.64.2.tgz", + "integrity": "sha512-+ZjJVnPzc8BUV/Eklu2k9T/IAyAyvwoCHqOaOrk2sbU33LFhM52BpX4eyENXn0bx5LwV3DJZgEQlIzucoemfGQ==", "license": "MIT", "dependencies": { - "@tanstack/query-devtools": "5.62.16" + "@tanstack/query-devtools": "5.64.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-query": "^5.64.0", + "@tanstack/react-query": "^5.64.2", "react": "^18 || ^19" } }, @@ -13805,9 +13805,9 @@ } }, "node_modules/lucide-react": { - "version": "0.471.1", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.471.1.tgz", - "integrity": "sha512-syOxwPhf62gg2YOsz72HRn+CIpeudFy67AeKnSR8Hn/fIIF4ubhNbRF+pQ2CaJrl+X9Os4PL87z2DXQi3DVeDA==", + "version": "0.473.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.473.0.tgz", + "integrity": "sha512-KW6u5AKeIjkvrxXZ6WuCu9zHE/gEYSXCay+Gre2ZoInD0Je/e3RBtP4OHpJVJ40nDklSvjVKjgH7VU8/e2dzRw==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" diff --git a/package.json b/package.json index bf233d16823..24c9c3fef89 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "@radix-ui/react-tooltip": "^1.1.6", "@sentry/browser": "^8.50.0", "@tanstack/react-query": "^5.64.1", - "@tanstack/react-query-devtools": "^5.64.0", + "@tanstack/react-query-devtools": "^5.64.2", "@vitejs/plugin-react": "^4.3.4", "@yudiel/react-qr-scanner": "^2.1.0", "bowser": "^2.11.0", @@ -102,7 +102,7 @@ "i18next-http-backend": "^3.0.1", "input-otp": "^1.4.2", "lodash-es": "^4.17.21", - "lucide-react": "^0.471.1", + "lucide-react": "^0.473.0", "markdown-it": "^14.1.0", "next-themes": "^0.4.3", "postcss-loader": "^8.1.1", diff --git a/public/locale/en.json b/public/locale/en.json index 1ec66f4c093..3959e0fe9ae 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -349,6 +349,7 @@ "add_skill": "Add Skill", "add_spoke": "Add Spoke Facility", "add_tags": "Add Tags", + "add_user": "Add User", "additional_information": "Additional Information", "additional_instructions": "Additional Instructions", "address": "Address", @@ -675,8 +676,8 @@ "copying_is_not_allowed": "Copying is not allowed", "could_not_load_page": "We are facing some difficulties showing the Page you were looking for. Our Engineers have been notified and we'll make sure that this is resolved on the fly!", "countries_travelled": "Countries travelled", - "cover_image_deleted.": "Cover image deleted.", - "cover_image_updated.": "Cover image updated.", + "cover_image_deleted": "Cover Image Deleted", + "cover_image_updated": "Cover Image Updated", "covid_19_cat_gov": "Covid_19 Clinical Category as per Govt. of Kerala guideline (A/B/C)", "covid_19_death_reporting_form_1": "Covid-19 Death Reporting : Form 1", "covid_details": "Covid Details", @@ -702,6 +703,7 @@ "create_tag": "Create Tag", "create_template": "Create Template", "create_user": "Create User", + "create_user_and_add_to_org": "Create a new user and add them to the organization.", "created": "Created", "created_by": "Created By", "created_date": "Created Date", @@ -831,6 +833,7 @@ "edit_profile": "Edit Profile", "edit_role": "Edit Role", "edit_schedule_template": "Edit Schedule Template", + "edit_user": "Edit User", "edit_user_profile": "Edit Profile", "edit_user_role": "Edit User Role", "edited_by": "Edited by", @@ -961,6 +964,7 @@ "enter_aadhaar_otp": "Enter OTP sent to the registered mobile with Aadhaar", "enter_abha_address": "Enter ABHA Address", "enter_any_id": "Enter any ID linked with your ABHA number", + "enter_dosage_instructions": "Enter Dosage Instructions", "enter_file_name": "Enter File Name", "enter_message": "Start typing...", "enter_mobile_number": "Enter Mobile Number", @@ -1161,6 +1165,7 @@ "insurance__policy_name": "Policy ID / Policy Name", "insurance_details_detail": "Include details of all the Insurance Policies held by the Patient for smooth insurance processing", "insurer_name_required": "Insurer Name is required", + "intended": "Intended", "intent": "Intent", "international_mobile": "International Mobile", "invalid_asset_id_msg": "Oops! The asset ID you entered does not appear to be valid.", @@ -1312,6 +1317,7 @@ "medical_council_registration_required": "Medical Council Registration is required", "medical_records": "Medical Records", "medical_worker": "Medical Worker", + "medication": "Medication", "medication_taken_between": "Medication Taken Between", "medicine": "Medicine", "medicine_administration_history": "Medicine Administration History", @@ -1444,6 +1450,8 @@ "noshow": "No-show", "not_eligible": "Not Eligible", "not_specified": "Not Specified", + "not_taken": "Not Taken", + "note": "Note", "notes": "Notes", "notes_placeholder": "Type your Notes", "notice_board": "Notice Board", @@ -1467,6 +1475,7 @@ "old_password": "Current Password", "on": "on", "on_emergency_basis": " on emergency basis", + "on_hold": "On Hold", "ongoing_medications": "Ongoing Medications", "only_indian_mobile_numbers_supported": "Currently only Indian numbers are supported", "op_encounter": "OP Encounter", @@ -1661,6 +1670,7 @@ "priority": "Priority", "prn_prescription": "PRN Prescription", "prn_prescriptions": "PRN Prescriptions", + "prn_reason": "PRN Reason", "procedure_suggestions": "Procedure Suggestions", "procedures_select_placeholder": "Select procedures to add details", "professional_info": "Professional Information", @@ -1716,6 +1726,7 @@ "register_patient": "Register Patient", "reject": "Reject", "rejected": "Rejected", + "related_person": "Related Person", "reload": "Reload", "remarks": "Remarks", "remarks_placeholder": "Enter remarks", @@ -1869,6 +1880,7 @@ "select_eligible_policy": "Select an Eligible Insurance Policy", "select_facility": "Select Facility", "select_facility_description": "Select the healthcare facility that will provide the requested resource.", + "select_facility_feature": "Select Facility Features", "select_facility_for_discharged_patients_warning": "Facility needs to be selected to view discharged patients.", "select_facility_type": "Select Facility Type", "select_for_administration": "Select for Administration", @@ -1986,6 +1998,7 @@ "status": "Status", "stop": "Stop", "stop_recording": "Stop Recording", + "stopped": "Stopped", "stream_stop_due_to_inativity": "The live feed will stop streaming due to inactivity", "stream_stopped_due_to_inativity": "The live feed has stopped streaming due to inactivity", "stream_uuid": "Stream UUID", @@ -2126,6 +2139,7 @@ "update_shift_request": "Update Shift Request", "update_status": "Update Status", "update_status_details": "Update Status/Details", + "update_user": "Update User", "update_user_role_organization": "Update the role for this user in the organization", "update_volunteer": "Reassign Volunteer", "updated": "Updated", @@ -2163,6 +2177,7 @@ "user_removed_success": "User removed from organization successfully", "user_role_update_success": "User role updated successfully", "user_type": "User Type", + "user_updated_successfully": "User updated successfully", "username": "Username", "username_already_exists": "This username already exists", "username_available": "Username is available", diff --git a/src/common/constants.tsx b/src/common/constants.tsx index a88b50e35b0..bf0c68def2f 100644 --- a/src/common/constants.tsx +++ b/src/common/constants.tsx @@ -104,6 +104,10 @@ export const GENDER_TYPES = [ { id: "non_binary", text: "Non Binary", icon: "TRANS" }, ] as const; +export const GENDERS = GENDER_TYPES.map((gender) => gender.id) as [ + (typeof GENDER_TYPES)[number]["id"], +]; + export const CONSULTATION_SUGGESTION = [ { id: "HI", text: "Home Isolation", deprecated: true }, // # Deprecated. Preserving option for backward compatibility (use only for readonly operations) { id: "A", text: "Admission" }, diff --git a/src/components/Common/ComboboxQuantityInput.tsx b/src/components/Common/ComboboxQuantityInput.tsx index 20cb540ed13..db7b8caa4d2 100644 --- a/src/components/Common/ComboboxQuantityInput.tsx +++ b/src/components/Common/ComboboxQuantityInput.tsx @@ -92,6 +92,14 @@ export function ComboboxQuantityInput({ } }; + React.useEffect(() => { + setInputValue(quantity?.value.toString() || ""); + }, [quantity?.value]); + + React.useEffect(() => { + setSelectedUnit(quantity?.unit); + }, [quantity?.unit]); + return (
diff --git a/src/components/Facility/FacilityForm.tsx b/src/components/Facility/FacilityForm.tsx index 858842bcd41..4d5250804c9 100644 --- a/src/components/Facility/FacilityForm.tsx +++ b/src/components/Facility/FacilityForm.tsx @@ -19,6 +19,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { MultiSelect } from "@/components/ui/multi-select"; import { Select, SelectContent, @@ -29,7 +30,6 @@ import { import { Textarea } from "@/components/ui/textarea"; import { FacilityModel } from "@/components/Facility/models"; -import { MultiSelectFormField } from "@/components/Form/FormFields/SelectFormField"; import { useStateAndDistrictFromPincode } from "@/hooks/useStateAndDistrictFromPincode"; @@ -47,6 +47,7 @@ import { parsePhoneNumber } from "@/Utils/utils"; import GovtOrganizationSelector from "@/pages/Organization/components/GovtOrganizationSelector"; import { BaseFacility } from "@/types/facility/facility"; import { Organization } from "@/types/organization/organization"; +import organizationApi from "@/types/organization/organizationApi"; interface FacilityProps { organizationId?: string; @@ -58,7 +59,7 @@ export default function FacilityForm(props: FacilityProps) { const { t } = useTranslation(); const queryClient = useQueryClient(); const [isGettingLocation, setIsGettingLocation] = useState(false); - const { facilityId, onSubmitSuccess } = props; + const { facilityId, organizationId, onSubmitSuccess } = props; const [selectedLevels, setSelectedLevels] = useState([]); const [showAutoFilledPincode, setShowAutoFilledPincode] = useState(false); @@ -112,14 +113,18 @@ export default function FacilityForm(props: FacilityProps) { onSubmitSuccess?.(); }, }); - const { mutate: updateFacility, isPending: isUpdatePending } = useMutation({ mutationFn: mutate(routes.updateFacility, { pathParams: { id: facilityId || "" }, }), onSuccess: (_data: FacilityModel) => { toast.success(t("facility_updated_successfully")); - queryClient.invalidateQueries({ queryKey: ["organizationFacilities"] }); + queryClient.invalidateQueries({ + queryKey: ["organizationFacilities"], + }); + queryClient.invalidateQueries({ + queryKey: ["facility"], + }); form.reset(); onSubmitSuccess?.(); }, @@ -148,8 +153,8 @@ export default function FacilityForm(props: FacilityProps) { } }; - const handleFeatureChange = (value: any) => { - const { value: features }: { value: Array } = value; + const handleFeatureChange = (value: string[]) => { + const features = value.map((val) => Number(val)); form.setValue("features", features); }; @@ -178,11 +183,21 @@ export default function FacilityForm(props: FacilityProps) { pincode: form.watch("pincode")?.toString() || "", }); + const { data: org } = useQuery({ + queryKey: ["organization", organizationId], + queryFn: query(organizationApi.get, { + pathParams: { id: organizationId }, + }), + enabled: !!organizationId && !facilityId, + }); + useEffect(() => { if (facilityId) return; const levels: Organization[] = []; if (stateOrg) levels.push(stateOrg); if (districtOrg) levels.push(districtOrg); + if (!stateOrg && !districtOrg && org) levels.push(org); + setSelectedLevels(levels); if (levels.length == 2) { @@ -193,7 +208,7 @@ export default function FacilityForm(props: FacilityProps) { return () => clearTimeout(timer); } return () => setShowAutoFilledPincode(false); - }, [stateOrg, districtOrg, facilityId]); + }, [stateOrg, districtOrg, organizationId, facilityId]); // Update form when facility data is loaded useEffect(() => { @@ -273,7 +288,6 @@ export default function FacilityForm(props: FacilityProps) { )} />
- )} /> - ( - - Features - - o.name} - optionValue={(o) => o.id} - onChange={handleFeatureChange} - error={form.formState.errors.features?.message} - id="facility-features" - /> - - - - )} + render={({ field }) => { + return ( + + {t("features")} + + ({ + value: obj.id.toString(), + label: obj.name, + icon: obj.icon, + }))} + onValueChange={handleFeatureChange} + value={field.value.map((val) => val.toString())} + placeholder={t("select_facility_feature")} + id="facility-features" + /> + + + + ); + }} /> @@ -519,6 +534,7 @@ export default function FacilityForm(props: FacilityProps) { + + ), + )} + + )} + + + + { + setActiveTab(Number(value)); + }} + className="w-fit" + > + + {options.map((option, index) => ( + + {option.label} + + ))} + +
+ { + options[activeTab].onSelect(value); + if (activeTab < options.length - 1) { + setActiveTab(activeTab + 1); + } else { + setOpen(false); + } + }} + placeholder={options[activeTab].placeholder || t("search")} + disabled={disabled} + count={count} + searchPostFix={searchPostFix} + wrapTextForSmallScreen + hideTrigger + controlledOpen={open} + /> +
+
+
+ + + ); +} diff --git a/src/components/Medicine/utils.ts b/src/components/Medicine/utils.ts new file mode 100644 index 00000000000..d83f4b62c46 --- /dev/null +++ b/src/components/Medicine/utils.ts @@ -0,0 +1,38 @@ +import { MedicationRequestDosageInstruction } from "@/types/emr/medicationRequest"; + +// Helper function to format dosage in Rx style +export function formatDosage(instruction: MedicationRequestDosageInstruction) { + if (!instruction.dose_and_rate) return ""; + + if (instruction.dose_and_rate.type === "calculated") { + const { dose_range } = instruction.dose_and_rate; + if (!dose_range) return ""; + return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`; + } + + const { dose_quantity } = instruction.dose_and_rate; + if (!dose_quantity?.value || !dose_quantity.unit) return ""; + + return `${dose_quantity.value} ${dose_quantity.unit.display}`; +} +// Helper function to format dosage instructions in Rx style +export function formatSig(instruction: MedicationRequestDosageInstruction) { + const parts: string[] = []; + + // Add route if present + if (instruction.route?.display) { + parts.push(`Via ${instruction.route.display}`); + } + + // Add method if present + if (instruction.method?.display) { + parts.push(`by ${instruction.method.display}`); + } + + // Add site if present + if (instruction.site?.display) { + parts.push(`to ${instruction.site.display}`); + } + + return parts.join(" "); +} diff --git a/src/components/Patient/PatientDetailsTab/ResourceRequests.tsx b/src/components/Patient/PatientDetailsTab/ResourceRequests.tsx index 0cddd924e66..0df4ec5b404 100644 --- a/src/components/Patient/PatientDetailsTab/ResourceRequests.tsx +++ b/src/components/Patient/PatientDetailsTab/ResourceRequests.tsx @@ -40,14 +40,19 @@ export const ResourceRequests = (props: PatientProps) => { const getStatusBadge = (status: ResourceRequest["status"]) => { const statusColors: Record = { - PENDING: "bg-yellow-100 text-yellow-800", - APPROVED: "bg-green-100 text-green-800", - REJECTED: "bg-red-100 text-red-800", - COMPLETED: "bg-blue-100 text-blue-800", + PENDING: + "bg-yellow-100 text-yellow-800 hover:bg-yellow-200 hover:text-yellow-900", + APPROVED: + "bg-green-100 text-green-800 hover:bg-green-200 hover:text-green-900", + REJECTED: "bg-red-100 text-red-800 hover:bg-red-200 hover:text-red-900", + COMPLETED: + "bg-blue-100 text-blue-800 hover:bg-blue-200 hover:text-blue-900", + DEFAULT: + "bg-gray-100 text-gray-800 hover:bg-gray-200 hover:text-gray-900", }; return ( - + {status} ); diff --git a/src/components/Patient/PatientRegistration.tsx b/src/components/Patient/PatientRegistration.tsx index 69c3ff24e0b..543e87148f6 100644 --- a/src/components/Patient/PatientRegistration.tsx +++ b/src/components/Patient/PatientRegistration.tsx @@ -46,6 +46,7 @@ import { GENDER_TYPES, // OCCUPATION_TYPES, //RATION_CARD_CATEGORY, // SOCIOECONOMIC_STATUS_CHOICES , } from "@/common/constants"; +import { GENDERS } from "@/common/constants"; import countryList from "@/common/static/countries.json"; import { PLUGIN_Component } from "@/PluginEngine"; @@ -63,10 +64,6 @@ interface PatientRegistrationPageProps { patientId?: string; } -export const GENDERS = GENDER_TYPES.map((gender) => gender.id) as [ - (typeof GENDER_TYPES)[number]["id"], -]; - export const BLOOD_GROUPS = BLOOD_GROUP_CHOICES.map((bg) => bg.id) as [ (typeof BLOOD_GROUP_CHOICES)[number]["id"], ]; diff --git a/src/components/Questionnaire/QuestionRenderer.tsx b/src/components/Questionnaire/QuestionRenderer.tsx index 0b01894b9cf..b4f5168c60e 100644 --- a/src/components/Questionnaire/QuestionRenderer.tsx +++ b/src/components/Questionnaire/QuestionRenderer.tsx @@ -14,6 +14,7 @@ import { QuestionGroup } from "./QuestionTypes/QuestionGroup"; // Questions that should be rendered full width const FULL_WIDTH_QUESTION_TYPES: StructuredQuestionType[] = [ "medication_request", + "medication_statement", ]; interface QuestionRendererProps { diff --git a/src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx b/src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx index 59b49c4ce29..6e34f840b3d 100644 --- a/src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx +++ b/src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx @@ -1,4 +1,4 @@ -import { MinusCircledIcon } from "@radix-ui/react-icons"; +import { MinusCircledIcon, Pencil2Icon } from "@radix-ui/react-icons"; import { t } from "i18next"; import React, { useState } from "react"; @@ -37,6 +37,8 @@ import { } from "@/components/ui/select"; import { ComboboxQuantityInput } from "@/components/Common/ComboboxQuantityInput"; +import { MultiValueSetSelect } from "@/components/Medicine/MultiValueSetSelect"; +import { NotesInput } from "@/components/Questionnaire/QuestionTypes/NotesInput"; import ValueSetSelect from "@/components/Questionnaire/ValueSetSelect"; import useBreakpoints from "@/hooks/useBreakpoints"; @@ -169,7 +171,7 @@ export function MedicationRequestQuestion({ })} > {/* Header - Only show on desktop */} -
+
{t("medicine")}
@@ -185,9 +187,6 @@ export function MedicationRequestQuestion({
{t("instructions")}
-
- {t("additional_instructions")} -
{t("route")}
@@ -200,6 +199,9 @@ export function MedicationRequestQuestion({
{t("intent")}
+
+ {t("notes")} +
@@ -225,7 +227,7 @@ export function MedicationRequestQuestion({ className={cn( "flex items-center gap-2 px-2 py-0.5 rounded-md shadow-sm text-sm", expandedMedicationIndex === index - ? "bg-white" + ? "bg-gray-50" : "bg-gray-100", )} > @@ -237,6 +239,7 @@ export function MedicationRequestQuestion({
{expandedMedicationIndex !== index && ( )}
-
+
= ({ }; return ( -
+
{/* Medicine Name */}
@@ -651,33 +640,47 @@ const MedicationRequestGridRow: React.FC = ({ - - handleUpdateDosageInstruction({ as_needed_for: reason }) - } - placeholder={t("select_prn_reason")} - disabled={disabled || !dosageInstruction?.as_needed_boolean} - wrapTextForSmallScreen={true} - /> -
- {/* Additional Instructions */} -
- - - handleUpdateDosageInstruction({ - additional_instruction: [instruction], - }) - } - placeholder={t("select_additional_instructions")} - disabled={disabled} - /> + {dosageInstruction?.as_needed_boolean ? ( + { + handleUpdateDosageInstruction({ + as_needed_for: value || undefined, + }); + }, + }, + { + system: "system-additional-instruction", + value: dosageInstruction?.additional_instruction?.[0] || null, + label: t("additional_instructions"), + placeholder: t("select_additional_instructions"), + onSelect: (value: Code | null) => { + handleUpdateDosageInstruction({ + additional_instruction: value ? [value] : undefined, + }); + }, + }, + ]} + disabled={disabled} + /> + ) : ( + + handleUpdateDosageInstruction({ + additional_instruction: instruction ? [instruction] : undefined, + }) + } + placeholder={t("select_additional_instructions")} + disabled={disabled} + /> + )}
{/* Route */}
@@ -739,6 +742,39 @@ const MedicationRequestGridRow: React.FC = ({
+ {/* Notes */} +
+ + {desktopLayout ? ( + <> + + onUpdate?.({ note: e.target.value })} + placeholder={t("add_notes")} + disabled={disabled} + className="h-9 text-sm" + /> + + ) : ( + { + onUpdate?.({ note: response.note }); + }} + disabled={disabled} + /> + )} +
{/* Remove Button */}
+ )} + +
+
+ +
+ + handleUpdateMedication(index, updates) + } + onRemove={() => handleRemoveMedication(index)} + /> +
+
+ + ) : ( + + handleUpdateMedication(index, updates) + } + onRemove={() => handleRemoveMedication(index)} + /> + )} + + ))} +
+
+
)} - - +
+ +
+
); } -const MedicationStatementItem: React.FC<{ - medication: MedicationStatement; +interface MedicationStatementGridRowProps { + medication: MedicationStatementRequest; disabled?: boolean; - onUpdate: (medication: Partial) => void; - onRemove: () => void; - index: number; -}> = ({ medication, disabled, onUpdate, onRemove, index }) => { + onUpdate?: (medication: Partial) => void; + onRemove?: () => void; +} + +const MedicationStatementGridRow: React.FC = ({ + medication, + disabled, + onUpdate, + onRemove, +}) => { const { t } = useTranslation(); + const desktopLayout = useBreakpoints({ lg: true, default: false }); return ( -
-
-

- {index + 1}. {medication.medication?.display} -

-
-
- - -
- -
+
+ {/* Medicine Name */} +
+ + {medication.medication?.display} +
-
-
-
- - -
-
- - - onUpdate({ - dosage_text: event.target.value, - }) - } - disabled={disabled} - /> -
-
- - - onUpdate({ - effective_period: { - start: date?.from?.toISOString(), - end: date?.to?.toISOString(), - }, - }) - } - /> -
-
- {medication.reason !== undefined && ( -
- - onUpdate({ reason: e.target.value })} - /> -
- )} + {/* Source */} +
+ + +
- {medication.note !== undefined && ( -
-