From a70f26e643c4d6d2e98534a1c0f9d03b8ca95032 Mon Sep 17 00:00:00 2001 From: JavidSumra <112365664+JavidSumra@users.noreply.github.com> Date: Thu, 9 Jan 2025 20:21:00 +0530 Subject: [PATCH 1/2] Remove Manual keyboard-shortcut handle (#9816) --- public/locale/en.json | 7 +++ src/components/Common/FilePreviewDialog.tsx | 28 +++--------- src/components/Patient/PatientIndex.tsx | 49 +++++++++------------ src/components/ui/sidebar.tsx | 21 +++------ 4 files changed, 41 insertions(+), 64 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index eb59daa7444..67ac2188bd8 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -1265,6 +1265,8 @@ "no_notices_for_you": "No notices for you.", "no_observations": "No Observations", "no_ongoing_medications": "No Ongoing Medications", + "no_patient_record_found": "No Patient Records Found", + "no_patient_record_text": "No existing records found with this phone number. Would you like to register a new patient?", "no_patients": "No patients found", "no_patients_found": "No Patients Found", "no_patients_found_phone_number": "No patients found with this phone number. Please create a new patient to proceed with booking appointment.", @@ -1364,6 +1366,7 @@ "patient__social-profile": "Social Profile", "patient__volunteer-contact": "Volunteer Contact", "patient_address": "Patient Address", + "patient_birth_year_for_identity": "Please enter the patient's year of birth to verify their identity", "patient_body": "Patient Body", "patient_category": "Patient Category", "patient_consultation__admission": "Date of admission", @@ -1629,6 +1632,7 @@ "search_icd11_placeholder": "Search for ICD-11 Diagnoses", "search_investigation_placeholder": "Search Investigation & Groups", "search_medication": "Search Medication", + "search_patient_page_text": "Search for existing patients using their phone number or create a new patient record", "search_patients": "Search Patients", "search_resource": "Search Resource", "search_user": "Search User", @@ -1876,6 +1880,7 @@ "vaccinated": "Vaccinated", "vaccine_name": "Vaccine name", "valid_otp_found": "Valid OTP found, Navigating to Appointments", + "valid_year_of_birth": "Please enter a valid year of birth (YYYY)", "vehicle_preference": "Vehicle preference", "vendor_name": "Vendor Name", "ventilator_interface": "Respiratory Support Type", @@ -1885,6 +1890,7 @@ "ventilator_oxygen_modality": "Oxygen Modality", "ventilator_oxygen_modality_oxygen_rate": "Oxygen Flow Rate", "ventilator_spo2": "SpO₂", + "verify": "Verify", "verify_and_link": "Verify and Link", "verify_otp": "Verify OTP", "verify_otp_error": "Failed to verify OTP. Please try again later.", @@ -1892,6 +1898,7 @@ "verify_otp_success_login": "OTP has been verified successfully. Logging in.", "verify_patient": "Verify Patient", "verify_patient_identifier": "Please verify the patient identifier", + "verify_patient_identity": "Verify Patient Identity", "verify_using": "Verify Using", "video_call": "Video Call", "video_conference_link": "Video Conference Link", diff --git a/src/components/Common/FilePreviewDialog.tsx b/src/components/Common/FilePreviewDialog.tsx index 4d6709d427d..d9eb4db62b3 100644 --- a/src/components/Common/FilePreviewDialog.tsx +++ b/src/components/Common/FilePreviewDialog.tsx @@ -11,6 +11,7 @@ import { useState, } from "react"; import { useTranslation } from "react-i18next"; +import useKeyboardShortcut from "use-keyboard-shortcut"; import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; @@ -152,20 +153,11 @@ 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]); + useKeyboardShortcut(["ArrowLeft"], () => index > 0 && handleNext(index - 1)); + useKeyboardShortcut( + ["ArrowRight"], + () => index < (uploadedFiles?.length || 0) - 1 && handleNext(index + 1), + ); return ( { uploadedFiles[index] && uploadedFiles[index].created_date && (

- Created on{" "} + {t("created_on")}{" "} {new Date( uploadedFiles[index].created_date!, ).toLocaleString("en-US", { @@ -235,9 +227,6 @@ const FilePreviewDialog = (props: FilePreviewProps) => { onClick={() => handleNext(index - 1)} disabled={index <= 0} aria-label="Previous file" - onKeyDown={(e) => - e.key === "ArrowLeft" && handleNext(index - 1) - } > @@ -288,9 +277,6 @@ const FilePreviewDialog = (props: FilePreviewProps) => { onClick={() => handleNext(index + 1)} disabled={index >= uploadedFiles.length - 1} aria-label="Next file" - onKeyDown={(e) => - e.key === "ArrowRight" && handleNext(index + 1) - } > diff --git a/src/components/Patient/PatientIndex.tsx b/src/components/Patient/PatientIndex.tsx index 64b3480f925..ba416025b33 100644 --- a/src/components/Patient/PatientIndex.tsx +++ b/src/components/Patient/PatientIndex.tsx @@ -1,7 +1,9 @@ import { useMutation } from "@tanstack/react-query"; import { navigate } from "raviger"; import { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { toast } from "sonner"; +import useKeyboardShortcut from "use-keyboard-shortcut"; import { cn } from "@/lib/utils"; @@ -51,6 +53,7 @@ export default function PatientIndex({ facilityId }: { facilityId: string }) { results: [], count: 0, }); + const { t } = useTranslation(); const handleCreatePatient = useCallback(() => { const queryParams = phoneNumber ? { phone_number: phoneNumber } : {}; @@ -60,6 +63,8 @@ export default function PatientIndex({ facilityId }: { facilityId: string }) { }); }, [facilityId, phoneNumber]); + useKeyboardShortcut(["shift", "p"], handleCreatePatient); + function AddPatientButton({ outline }: { outline?: boolean }) { return ( - + diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index b6ad223fbed..c3d5eb952ea 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -2,6 +2,7 @@ import { Slot } from "@radix-ui/react-slot"; import { VariantProps, cva } from "class-variance-authority"; import { PanelLeftClose, PanelRightClose } from "lucide-react"; import * as React from "react"; +import useKeyboardShortcut from "use-keyboard-shortcut"; import { cn } from "@/lib/utils"; @@ -19,6 +20,8 @@ import { import { useIsMobile } from "@/hooks/use-mobile"; +import { isAppleDevice } from "@/Utils/utils"; + const SIDEBAR_COOKIE_NAME = "sidebar:state"; const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; const SIDEBAR_WIDTH = "16rem"; @@ -97,20 +100,10 @@ const SidebarProvider = React.forwardRef< }, [isMobile, setOpen, setOpenMobile]); // Adds a keyboard shortcut to toggle the sidebar. - React.useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if ( - event.key === SIDEBAR_KEYBOARD_SHORTCUT && - (event.metaKey || event.ctrlKey) - ) { - event.preventDefault(); - toggleSidebar(); - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [toggleSidebar]); + useKeyboardShortcut( + [isAppleDevice ? "Meta" : "Control", SIDEBAR_KEYBOARD_SHORTCUT], + toggleSidebar, + ); // We add a state so that we can do data-state="expanded" or "collapsed". // This makes it easier to style the sidebar with Tailwind classes. From f0589e67cf9c26293fd8cd9da09e78f4f4134d66 Mon Sep 17 00:00:00 2001 From: JavidSumra <112365664+JavidSumra@users.noreply.github.com> Date: Thu, 9 Jan 2025 20:31:52 +0530 Subject: [PATCH 2/2] Fix Online Status Indicator in Organization Tab (#9728) --- public/locale/en.json | 20 ++++- src/components/Users/UserListAndCard.tsx | 83 +++++++------------ .../ui/sidebar/facility-switcher.tsx | 8 +- .../FacilityOrganizationUsers.tsx | 30 +++---- .../components/EditFacilityUserRoleSheet.tsx | 58 +++++++------ src/pages/Organization/OrganizationUsers.tsx | 25 ++---- .../components/EditUserRoleSheet.tsx | 57 +++++++------ 7 files changed, 139 insertions(+), 142 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 67ac2188bd8..ab0afc7ca3c 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -28,7 +28,6 @@ "CONSCIOUSNESS_LEVEL__RESPONDS_TO_PAIN": "Responds to Pain", "CONSCIOUSNESS_LEVEL__RESPONDS_TO_VOICE": "Responds to Voice", "CONSCIOUSNESS_LEVEL__UNRESPONSIVE": "Unresponsive", - "Cancel": "Cancel", "DAYS_OF_WEEK_SHORT__0": "Mon", "DAYS_OF_WEEK_SHORT__1": "Tue", "DAYS_OF_WEEK_SHORT__2": "Wed", @@ -355,6 +354,7 @@ "age_input_warning_bold": "Recommended only when the patient's date of birth is unknown", "age_less_than_0": "Age cannot be less than 0", "age_notice": "Only year of birth will be stored for age.", + "ago": "ago", "all": "All", "all_changes_have_been_saved": "All changes have been saved", "all_details": "All Details", @@ -486,6 +486,7 @@ "cancelled": "Cancelled", "capture": "Capture", "capture_cover_photo": "Capture Cover Photo", + "card": "Card", "care": "CARE", "category": "Category", "caution": "Caution", @@ -658,6 +659,7 @@ "csv_file_in_the_specified_format": "Select a CSV file in the specified format", "current_address": "Current Address", "current_password": "Current Password", + "current_role": "Current Role", "current_status": "Current Status", "customer_support_email": "Customer Support Email", "customer_support_name": "Customer Support Name", @@ -678,7 +680,6 @@ "date_of_return": "Date of Return", "date_of_test": "Date of sample collection for Covid testing", "day": "Day", - "days": "Days", "death_report": "Death Report", "delete": "Delete", "delete_account": "Delete account", @@ -767,7 +768,9 @@ "edit_policy_description": "Add or edit patient's insurance details", "edit_prescriptions": "Edit Prescriptions", "edit_profile": "Edit Profile", + "edit_role": "Edit Role", "edit_user_profile": "Edit Profile", + "edit_user_role": "Edit User Role", "edited_by": "Edited by", "edited_on": "Edited on", "education": "Education", @@ -1033,7 +1036,6 @@ "home_facility_cleared_success": "Home Facility cleared successfully", "home_facility_updated_error": "Error while updating Home Facility", "home_facility_updated_success": "Home Facility updated successfully", - "hours": "hours", "hubs": "Hub Facilities", "i_declare": "I hereby declare that:", "icd11_as_recommended": "As per ICD-11 recommended by WHO", @@ -1117,6 +1119,7 @@ "last_administered": "Last administered", "last_discharge_reason": "Last Discharge Reason", "last_edited": "Last Edited", + "last_login": "Last Login", "last_modified": "Last Modified", "last_modified_by": "Last Modified By", "last_name": "Last Name", @@ -1230,6 +1233,7 @@ "nationality": "Nationality", "nearby_facilities": "Nearby Facilities", "never": "never", + "never_logged_in": "Never Logged In", "new_password": "New Password", "new_password_confirmation": "Confirm New Password", "new_password_same_as_old": "New password is same as old password, please enter a different new password.", @@ -1546,6 +1550,8 @@ "reload": "Reload", "remove": "Remove", "remove_user": "Remove User", + "remove_user_organization": "Remove User from Organization", + "remove_user_warn": "Are you sure you want to remove {{firstName}} {{lastName}} from this organization? This action cannot be undone.", "rename": "Rename", "replace_home_facility": "Replace Home Facility", "replace_home_facility_confirm": "Are you sure you want to replace", @@ -1643,7 +1649,9 @@ "select_all": "Select All", "select_date": "Select date", "select_department": "Select Department", + "select_diff_role": "Please select a different role", "select_eligible_policy": "Select an Eligible Insurance Policy", + "select_facility": "Select Facility", "select_facility_for_discharged_patients_warning": "Facility needs to be selected to view discharged patients.", "select_for_administration": "Select for Administration", "select_groups": "Select Groups", @@ -1651,6 +1659,7 @@ "select_investigation_groups": "Select Investigation Groups", "select_investigations": "Select Investigations", "select_local_body": "Select Local Body", + "select_new_role": "Select New Role", "select_patient": "Select Patient", "select_policy": "Select an Insurance Policy", "select_policy_to_add_items": "Select a Policy to Add Items", @@ -1837,9 +1846,11 @@ "update_preset_position_to_current": "Update preset's position to camera's current position", "update_record": "Update Record", "update_record_for_asset": "Update record for asset", + "update_role": "Update Role", "update_shift_request": "Update Shift Request", "update_status": "Update Status", "update_status_details": "Update Status/Details", + "update_user_role_organization": "Update the role for this user in the organization", "update_volunteer": "Reassign Volunteer", "updated": "Updated", "updated_on": "Updated On", @@ -1869,6 +1880,8 @@ "user_not_available_for_appointments": "This user is not available for appointments", "user_qualifications": "Qualifications", "user_qualifications_note": "Enter appropriate qualifications for this user", + "user_removed_success": "User removed from organization successfully", + "user_role_update_success": "User role updated successfully", "user_type": "User Type", "username": "Username", "username_already_exists": "This username already exists", @@ -1909,6 +1922,7 @@ "view_cns": "View CNS", "view_consultation": "View Latest Encounter", "view_consultation_and_log_updates": "View Consultation / Log Updates", + "view_dashboard": "View Dashboard", "view_details": "View Details", "view_facility": "View Facility", "view_files": "View Files", diff --git a/src/components/Users/UserListAndCard.tsx b/src/components/Users/UserListAndCard.tsx index e436afe6e15..6422f096f2f 100644 --- a/src/components/Users/UserListAndCard.tsx +++ b/src/components/Users/UserListAndCard.tsx @@ -4,6 +4,8 @@ import { useTranslation } from "react-i18next"; import Card from "@/CAREUI/display/Card"; import CareIcon from "@/CAREUI/icons/CareIcon"; +import { Badge } from "@/components/ui/badge"; + import { Avatar } from "@/components/Common/Avatar"; import Tabs from "@/components/Common/Tabs"; import SearchInput from "@/components/Form/SearchInput"; @@ -14,12 +16,7 @@ import useWindowDimensions from "@/hooks/useWindowDimensions"; import { USER_TYPE_OPTIONS } from "@/common/constants"; -import { - classNames, - formatName, - isUserOnline, - relativeTime, -} from "@/Utils/utils"; +import { formatName, isUserOnline, relativeTime } from "@/Utils/utils"; import { UserBase } from "@/types/user/user"; export const GetUserTypes = () => { @@ -65,11 +62,7 @@ const GetDetailsButton = (username: string) => { ); }; -const getNameAndStatusCard = ( - user: UserBase, - cur_online: boolean, - showDetailsButton = false, -) => { +const getNameAndStatusCard = (user: UserBase, showDetailsButton = false) => { return (

@@ -78,14 +71,7 @@ const getNameAndStatusCard = (

{formatName(user)}

-
- -
+
{ const authUser = useAuthUser(); const isAuthUser = user.id === authUser.external_id; - const isOnline = isUserOnline(user) || isAuthUser; const { t } = useTranslation(); return ( -
- - - {isOnline - ? t("online") - : user.last_login - ? relativeTime(user.last_login) - : t("never")} - -
+ {isUserOnline(user) || isAuthUser ? ( + + + {t("online")} + + ) : user.last_login ? ( + + + + {relativeTime(user.last_login)} + + + ) : ( + + + {t("never_logged_in")} + + )} +
); }; const UserCard = ({ user }: { user: UserBase }) => { @@ -173,7 +154,7 @@ const UserCard = ({ user }: { user: UserBase }) => {
{!isMediumScreen && - getNameAndStatusCard(user, userOnline, !isLessThanXLargeScreen)} + getNameAndStatusCard(user, !isLessThanXLargeScreen)}
{t("role")}
@@ -312,7 +293,7 @@ export default function UserListView({ text: (
- Card + {t("card")}
), value: 0, @@ -322,7 +303,7 @@ export default function UserListView({ text: (
- List + {t("list")}
), value: 1, @@ -345,7 +326,7 @@ export default function UserListView({ ) : (
- No Users Found + {t("no_users_found")}
)} diff --git a/src/components/ui/sidebar/facility-switcher.tsx b/src/components/ui/sidebar/facility-switcher.tsx index 59715e4e02c..0769f2a825f 100644 --- a/src/components/ui/sidebar/facility-switcher.tsx +++ b/src/components/ui/sidebar/facility-switcher.tsx @@ -1,6 +1,7 @@ import { CaretSortIcon, DashboardIcon } from "@radix-ui/react-icons"; import { Hospital } from "lucide-react"; import { navigate } from "raviger"; +import { useTranslation } from "react-i18next"; import { DropdownMenu, @@ -27,6 +28,7 @@ export function FacilitySwitcher({ selectedFacility: UserFacilityModel | null; }) { const { isMobile, setOpenMobile } = useSidebar(); + const { t } = useTranslation(); return ( @@ -42,7 +44,7 @@ export function FacilitySwitcher({
- {selectedFacility?.name || "Select Facility"} + {selectedFacility?.name || t("select_facility")}
@@ -56,10 +58,10 @@ export function FacilitySwitcher({ > navigate("/")}> - View Dashboard + {t("view_dashboard")} - Facilities + {t("facilities")} {facilities.map((facility, index) => ( (); + const { t } = useTranslation(); const openAddUserSheet = qParams.sheet === "add"; const openLinkUserSheet = qParams.sheet === "link"; @@ -70,7 +72,7 @@ export default function FacilityOrganizationUsers({ id, facilityId }: Props) {
-

Users

+

{t("users")}

- No users found. + {t("no_users_found")} ) : ( @@ -117,33 +119,25 @@ export default function FacilityOrganizationUsers({ id, facilityId }: Props) {

{userRole.user.first_name} {userRole.user.last_name}

-
- + + {userRole.user.username} - - - - online - - -
+ +
-
Role
+
{t("role")}
{userRole.role.name}
-
Phone Number
+
{t("phone_number")}
{userRole.user.phone_number}
@@ -165,7 +159,7 @@ export default function FacilityOrganizationUsers({ id, facilityId }: Props) { icon="l-arrow-up-right" className="h-4 w-4" /> - More details + {t("more_details")} } /> diff --git a/src/pages/FacilityOrganization/components/EditFacilityUserRoleSheet.tsx b/src/pages/FacilityOrganization/components/EditFacilityUserRoleSheet.tsx index 39fb57115d2..3d96f2210a7 100644 --- a/src/pages/FacilityOrganization/components/EditFacilityUserRoleSheet.tsx +++ b/src/pages/FacilityOrganization/components/EditFacilityUserRoleSheet.tsx @@ -1,5 +1,6 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { @@ -14,6 +15,7 @@ import { AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; import { Select, SelectContent, @@ -31,6 +33,7 @@ import { } from "@/components/ui/sheet"; import { Avatar } from "@/components/Common/Avatar"; +import { UserStatusIndicator } from "@/components/Users/UserListAndCard"; import routes from "@/Utils/request/api"; import mutate from "@/Utils/request/mutate"; @@ -53,6 +56,7 @@ export default function EditUserRoleSheet({ const queryClient = useQueryClient(); const [open, setOpen] = useState(false); const [selectedRole, setSelectedRole] = useState(userRole.role.id); + const { t } = useTranslation(); const { data: roles } = useQuery({ queryKey: ["roles"], @@ -74,7 +78,7 @@ export default function EditUserRoleSheet({ queryClient.invalidateQueries({ queryKey: ["facilityOrganizationUsers", facilityId, organizationId], }); - toast.success("User role updated successfully"); + toast.success(t("user_role_update_success")); setOpen(false); }, onError: (error) => { @@ -98,7 +102,7 @@ export default function EditUserRoleSheet({ queryClient.invalidateQueries({ queryKey: ["facilityOrganizationUsers", facilityId, organizationId], }); - toast.success("User removed from organization successfully"); + toast.success(t("user_removed_success")); setOpen(false); }, onError: (error) => { @@ -111,7 +115,7 @@ export default function EditUserRoleSheet({ const handleUpdateRole = () => { if (selectedRole === userRole.role.id) { - toast.error("Please select a different role"); + toast.error(t("select_diff_role")); return; } @@ -124,13 +128,13 @@ export default function EditUserRoleSheet({ return ( - {trigger || } + {trigger || } - Edit User Role + {t("edit_user_role")} - Update the role for this user in the organization. + {t("update_user_role_organization")}
@@ -153,29 +157,30 @@ export default function EditUserRoleSheet({
- Username + {t("username")}

{userRole.user.username}

- Current Role + + {t("current_role")} +

{userRole.role.name}

-
- Last Login -

- {userRole.user.last_login - ? new Date(userRole.user.last_login).toLocaleDateString() - : "Never"} -

+
+ + {t("last_login")}{" "} + +
-
- + - + {roles?.results?.map((role) => ( @@ -191,33 +197,34 @@ export default function EditUserRoleSheet({ onClick={handleUpdateRole} disabled={selectedRole === userRole.role.id} > - Update Role + {t("update_role")} - Remove User from Organization + {t("remove_user_organization")} - Are you sure you want to remove {userRole.user.first_name}{" "} - {userRole.user.last_name} from this organization? This - action cannot be undone. + {t("remove_user_warn", { + firstName: userRole.user.first_name, + lastName: userRole.user.last_name, + })} - Cancel + {t("cancel")} removeRole()} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > - Remove + {t("remove")}