diff --git a/public/locale/en.json b/public/locale/en.json index 30cc365303e..7ab5fc505f9 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -381,6 +381,7 @@ "all_patients": "All Patients", "allergen": "Allergen", "allergies": "Allergies", + "allergies_empty_message": "No allergies recorded", "allow_transfer": "Allow Transfer", "allowed_formats_are": "Allowed formats are {{formats}}.", "already_a_member": "Already a member?", @@ -767,6 +768,7 @@ "diagnosis__unconfirmed": "Unconfirmed", "diagnosis_already_added": "This diagnosis was already added", "diagnosis_at_discharge": "Diagnosis at Discharge", + "diagnosis_empty_message": "No diagnoses recorded", "diastolic": "Diastolic", "didnt_receive_a_message": "Didn't receive a message?", "diet_preference": "Diet Preference", @@ -2028,6 +2030,7 @@ "switch_bed": "Switch Bed", "switch_camera_is_not_available": "Switch camera is not available.", "symptoms": "Symptoms", + "symptoms_empty_message": "No symptoms recorded", "systolic": "Systolic", "tachycardia": "Tachycardia", "tag_name": "Tag Name", diff --git a/src/components/Common/SkeletonLoading.tsx b/src/components/Common/SkeletonLoading.tsx new file mode 100644 index 00000000000..9200b38095f --- /dev/null +++ b/src/components/Common/SkeletonLoading.tsx @@ -0,0 +1,111 @@ +import { Card, CardContent } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; + +export const TableSkeleton = ({ count }: { count: number }) => ( +
+ + {/* Header Skeleton */} + + + + + + + + + + {/* Body Skeleton */} + + {Array.from({ length: count }).map((_, i) => ( + + + + + + + + ))} + +
+ + + + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+); + +export const CardListSkeleton = ({ count }: { count: number }) => + Array.from({ length: count }, (_, index) => ( +
+
+
+
+
+ + +
+
+
+
+ )); + +export const CardGridSkeleton = ({ count }: { count: number }) => + Array.from({ length: count }, (_, index) => ( +
+ + +
+
+ +
+
+ +
+ + +
+
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+
+
+ )); diff --git a/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx b/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx index 3cbad3a75c6..8d701f09393 100644 --- a/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx +++ b/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx @@ -5,6 +5,8 @@ import PaginatedList from "@/CAREUI/misc/PaginatedList"; import { Card } from "@/components/ui/card"; +import { CardListSkeleton } from "@/components/Common/SkeletonLoading"; + import routes from "@/Utils/request/api"; import { formatDateTime, properCase } from "@/Utils/utils"; import { Encounter } from "@/types/emr/encounter"; @@ -147,22 +149,8 @@ export default function QuestionnaireResponsesList({ encounter }: Props) { -
- {[1, 2, 3].map((i) => ( - -
-
-
-
-
-
-
-
- - ))} +
+
diff --git a/src/components/Facility/FacilityUsers.tsx b/src/components/Facility/FacilityUsers.tsx index 5296ea4e29b..ceb15d6bfef 100644 --- a/src/components/Facility/FacilityUsers.tsx +++ b/src/components/Facility/FacilityUsers.tsx @@ -5,12 +5,14 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Badge } from "@/components/ui/badge"; -import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; -import { Skeleton } from "@/components/ui/skeleton"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import Page from "@/components/Common/Page"; +import { + CardGridSkeleton, + TableSkeleton, +} from "@/components/Common/SkeletonLoading"; import UserListAndCardView from "@/components/Users/UserListAndCard"; import useFilters from "@/hooks/useFilters"; @@ -18,103 +20,6 @@ import useFilters from "@/hooks/useFilters"; import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; -const UserCardSkeleton = () => ( -
-
- {Array.from({ length: 6 }).map((_, i) => ( - - -
-
- -
-
- -
- - -
-
-
-
- -
-
- - -
-
- - -
-
- -
- -
-
-
-
- ))} -
-
-); - -const UserListSkeleton = () => ( -
- - {/* Header Skeleton */} - - - - - - - - - - {/* Body Skeleton */} - - {Array.from({ length: 7 }).map((_, i) => ( - - - - - - - - ))} - -
- - - - - - - - - -
-
- -
- - -
-
-
- - - - - - - -
-
-); - export default function FacilityUsers(props: { facilityId: string }) { const { t } = useTranslation(); const { qParams, updateQuery, Pagination } = useFilters({ @@ -141,7 +46,13 @@ export default function FacilityUsers(props: { facilityId: string }) { if (userListLoading || !userListData) { usersList = - activeTab === "card" ? : ; + activeTab === "card" ? ( +
+ +
+ ) : ( + + ); } else { usersList = (
diff --git a/src/components/Patient/PatientDetailsTab/patientUpdates.tsx b/src/components/Patient/PatientDetailsTab/patientUpdates.tsx index b4a2447d841..72cc2509391 100644 --- a/src/components/Patient/PatientDetailsTab/patientUpdates.tsx +++ b/src/components/Patient/PatientDetailsTab/patientUpdates.tsx @@ -7,6 +7,8 @@ import PaginatedList from "@/CAREUI/misc/PaginatedList"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; +import { CardListSkeleton } from "@/components/Common/SkeletonLoading"; + import routes from "@/Utils/request/api"; import { formatDateTime, properCase } from "@/Utils/utils"; import { QuestionnaireResponse } from "@/types/questionnaire/questionnaireResponse"; @@ -48,21 +50,7 @@ export const Updates = (props: PatientProps) => {
- {[1, 2, 3].map((i) => ( - -
-
-
-
-
-
-
-
- - ))} +
diff --git a/src/pages/Appointments/components/AppointmentTokenCard.tsx b/src/pages/Appointments/components/AppointmentTokenCard.tsx index 7dc65f84c3f..eeba2a98116 100644 --- a/src/pages/Appointments/components/AppointmentTokenCard.tsx +++ b/src/pages/Appointments/components/AppointmentTokenCard.tsx @@ -23,7 +23,7 @@ const AppointmentTokenCard = ({ id, appointment, facility }: Props) => { return (
diff --git a/src/pages/Encounters/EncounterList.tsx b/src/pages/Encounters/EncounterList.tsx index b7195f4b115..59dbbadfed8 100644 --- a/src/pages/Encounters/EncounterList.tsx +++ b/src/pages/Encounters/EncounterList.tsx @@ -30,11 +30,11 @@ import { SelectValue, } from "@/components/ui/select"; import { Separator } from "@/components/ui/separator"; -import { Skeleton } from "@/components/ui/skeleton"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import Page from "@/components/Common/Page"; import SearchByMultipleFields from "@/components/Common/SearchByMultipleFields"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import useFilters from "@/hooks/useFilters"; @@ -100,36 +100,6 @@ const buildQueryParams = ( return params; }; -function EncounterCardSkeleton() { - return ( - - -
- - -
- -
- -
-
- - -
-
- - -
- -
- -
-
-
-
- ); -} - function EmptyState() { return ( @@ -690,14 +660,7 @@ export function EncounterList({
{isLoading ? ( - <> - - - - - - - + ) : encounters.length === 0 ? (
diff --git a/src/pages/Encounters/tabs/EncounterNotesTab.tsx b/src/pages/Encounters/tabs/EncounterNotesTab.tsx index d10fa051d24..9a898de5ba6 100644 --- a/src/pages/Encounters/tabs/EncounterNotesTab.tsx +++ b/src/pages/Encounters/tabs/EncounterNotesTab.tsx @@ -34,7 +34,6 @@ import { Input } from "@/components/ui/input"; import { Markdown } from "@/components/ui/markdown"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Sheet, SheetContent } from "@/components/ui/sheet"; -import { Skeleton } from "@/components/ui/skeleton"; import { Textarea } from "@/components/ui/textarea"; import { Tooltip, @@ -45,6 +44,7 @@ import { import { Avatar } from "@/components/Common/Avatar"; import Loading from "@/components/Common/Loading"; +import { CardListSkeleton } from "@/components/Common/SkeletonLoading"; import useAuthUser from "@/hooks/useAuthUser"; @@ -69,23 +69,6 @@ const threadTemplates = [ "Lab Results Discussion", ] as const; -// Component to display loading skeleton for messages -const MessageSkeleton = () => ( -
- {[1, 2, 3].map((i) => ( -
-
-
-
- - -
-
-
- ))} -
-); - // Info tooltip component for help text const InfoTooltip = ({ content }: { content: string }) => ( @@ -568,7 +551,9 @@ export const EncounterNotesTab = ({ encounter }: EncounterTabProps) => { <> {messagesLoading ? (
- +
+ +
) : ( <> @@ -593,7 +578,9 @@ export const EncounterNotesTab = ({ encounter }: EncounterTabProps) => { )} {isFetchingNextPage && (
- +
+ +
)}
diff --git a/src/pages/FacilityOrganization/FacilityOrganizationIndex.tsx b/src/pages/FacilityOrganization/FacilityOrganizationIndex.tsx index 81a3dc4042a..9f81df9b8f0 100644 --- a/src/pages/FacilityOrganization/FacilityOrganizationIndex.tsx +++ b/src/pages/FacilityOrganization/FacilityOrganizationIndex.tsx @@ -16,6 +16,7 @@ import { import { Skeleton } from "@/components/ui/skeleton"; import Page from "@/components/Common/Page"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; @@ -41,19 +42,9 @@ export default function FacilityOrganizationIndex({ return (
- +
- {Array.from({ length: 6 }).map((_, i) => ( - - - - - - - - - - ))} +
); diff --git a/src/pages/FacilityOrganization/FacilityOrganizationUsers.tsx b/src/pages/FacilityOrganization/FacilityOrganizationUsers.tsx index f467018fe08..ba50a278d52 100644 --- a/src/pages/FacilityOrganization/FacilityOrganizationUsers.tsx +++ b/src/pages/FacilityOrganization/FacilityOrganizationUsers.tsx @@ -8,6 +8,10 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Avatar } from "@/components/Common/Avatar"; +import { + CardGridSkeleton, + CardListSkeleton, +} from "@/components/Common/SkeletonLoading"; import { UserStatusIndicator } from "@/components/Users/UserListAndCard"; import routes from "@/Utils/request/api"; @@ -49,20 +53,11 @@ export default function FacilityOrganizationUsers({ id, facilityId }: Props) { if (isLoadingUsers) { return ( -
- {Array.from({ length: 3 }).map((_, i) => ( - - -
-
-
-
-
-
-
- - - ))} +
+ +
+ +
); diff --git a/src/pages/FacilityOrganization/FacilityOrganizationView.tsx b/src/pages/FacilityOrganization/FacilityOrganizationView.tsx index c49087ef6f4..83e34e5df7a 100644 --- a/src/pages/FacilityOrganization/FacilityOrganizationView.tsx +++ b/src/pages/FacilityOrganization/FacilityOrganizationView.tsx @@ -11,6 +11,7 @@ import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import Pagination from "@/components/Common/Pagination"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; @@ -79,19 +80,7 @@ export default function FacilityOrganizationView({ id, facilityId }: Props) { {isLoading ? (
- {Array.from({ length: 6 }).map((_, i) => ( - - -
-
-
-
-
-
-
- - - ))} +
) : (
diff --git a/src/pages/FacilityOrganization/components/FacilityOrganizationLayout.tsx b/src/pages/FacilityOrganization/components/FacilityOrganizationLayout.tsx index f975d5e2cdd..914429d74fe 100644 --- a/src/pages/FacilityOrganization/components/FacilityOrganizationLayout.tsx +++ b/src/pages/FacilityOrganization/components/FacilityOrganizationLayout.tsx @@ -11,8 +11,10 @@ import { BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; import { Menubar, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar"; +import { Skeleton } from "@/components/ui/skeleton"; import Page from "@/components/Common/Page"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; @@ -61,7 +63,16 @@ export default function FacilityOrganizationLayout({ }); if (isLoading) { - return
Loading...
; + return ( +
+ + + +
+ +
+
+ ); } // add loading state if (!org) { diff --git a/src/pages/Organization/OrganizationFacilities.tsx b/src/pages/Organization/OrganizationFacilities.tsx index 2dbec2da6ce..cf913cd796b 100644 --- a/src/pages/Organization/OrganizationFacilities.tsx +++ b/src/pages/Organization/OrganizationFacilities.tsx @@ -10,6 +10,7 @@ import { Card, CardContent, CardFooter } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Avatar } from "@/components/Common/Avatar"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import useFilters from "@/hooks/useFilters"; @@ -93,31 +94,7 @@ export default function OrganizationFacilities({ data-cy="facility-cards" > {isLoading ? ( - Array.from({ length: 6 }).map((_, i) => ( - -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - )) + ) : facilities?.results?.length === 0 ? ( diff --git a/src/pages/Organization/OrganizationIndex.tsx b/src/pages/Organization/OrganizationIndex.tsx index 1719b70caab..3cfbb299460 100644 --- a/src/pages/Organization/OrganizationIndex.tsx +++ b/src/pages/Organization/OrganizationIndex.tsx @@ -1,5 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { Link } from "raviger"; +import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; @@ -12,9 +13,9 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; import Page from "@/components/Common/Page"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import query from "@/Utils/request/query"; import { @@ -29,25 +30,12 @@ export default function OrganizationIndex() { queryFn: query(organizationApi.listMine), }); + const { t } = useTranslation(); if (isLoading) { return ( - -
- {Array.from({ length: 3 }).map((_, i) => ( - - - - - - - - - - - - - - ))} + +
+
); @@ -55,7 +43,7 @@ export default function OrganizationIndex() { if (!data?.results?.length) { return ( - + @@ -80,7 +68,7 @@ export default function OrganizationIndex() { } return ( - +
{data.results.map((org: Organization) => ( diff --git a/src/pages/Organization/OrganizationPatients.tsx b/src/pages/Organization/OrganizationPatients.tsx index 300d4aa0363..50f814e20fd 100644 --- a/src/pages/Organization/OrganizationPatients.tsx +++ b/src/pages/Organization/OrganizationPatients.tsx @@ -12,6 +12,7 @@ import { Card, CardContent } from "@/components/ui/card"; import { Avatar } from "@/components/Common/Avatar"; import SearchByMultipleFields from "@/components/Common/SearchByMultipleFields"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import useFilters from "@/hooks/useFilters"; @@ -124,35 +125,7 @@ export default function OrganizationPatients({ id, navOrganizationId }: Props) {
{isLoading ? ( - Array.from({ length: 6 }).map((_, i) => ( - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - )) + ) : patients?.results?.length === 0 ? ( diff --git a/src/pages/Organization/OrganizationUsers.tsx b/src/pages/Organization/OrganizationUsers.tsx index 272e38c2eb4..7b9dcb3d276 100644 --- a/src/pages/Organization/OrganizationUsers.tsx +++ b/src/pages/Organization/OrganizationUsers.tsx @@ -9,6 +9,7 @@ import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Avatar } from "@/components/Common/Avatar"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import { UserStatusIndicator } from "@/components/Users/UserListAndCard"; import useFilters from "@/hooks/useFilters"; @@ -104,19 +105,9 @@ export default function OrganizationUsers({ id, navOrganizationId }: Props) { />
{isLoadingUsers ? ( - Array.from({ length: 3 }).map((_, i) => ( - - -
-
-
-
-
-
-
- - - )) +
+ +
) : (
{users?.results?.length === 0 ? ( diff --git a/src/pages/Organization/OrganizationView.tsx b/src/pages/Organization/OrganizationView.tsx index f42279386fc..2bb8c31b698 100644 --- a/src/pages/Organization/OrganizationView.tsx +++ b/src/pages/Organization/OrganizationView.tsx @@ -11,6 +11,7 @@ import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import Pagination from "@/components/Common/Pagination"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import query from "@/Utils/request/query"; import { Organization, getOrgLabel } from "@/types/organization/organization"; @@ -78,19 +79,7 @@ export default function OrganizationView({ id, navOrganizationId }: Props) { {isLoading ? (
- {Array.from({ length: 6 }).map((_, i) => ( - - -
-
-
-
-
-
-
- - - ))} +
) : (
diff --git a/src/pages/Organization/components/OrganizationLayout.tsx b/src/pages/Organization/components/OrganizationLayout.tsx index 0ea786aec15..e10168c8257 100644 --- a/src/pages/Organization/components/OrganizationLayout.tsx +++ b/src/pages/Organization/components/OrganizationLayout.tsx @@ -12,14 +12,13 @@ import { BreadcrumbList, BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; -import { Card, CardContent } from "@/components/ui/card"; import { Menubar, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar"; -import { Skeleton } from "@/components/ui/skeleton"; import Page from "@/components/Common/Page"; import query from "@/Utils/request/query"; import { usePermissions } from "@/context/PermissionContext"; +import OrganizationLayoutSkeleton from "@/pages/Organization/components/OrganizationLayoutSkeleton"; import { Organization, OrganizationParent, @@ -70,39 +69,7 @@ export default function OrganizationLayout({ }, [org, setOrganization]); if (isLoading) { - return ( -
- - -
- {[...Array(4)].map((_, index) => ( - - ))} -
- - -
- {Array.from({ length: 6 }).map((_, i) => ( - - -
-
- -
- - -
-
-
- -
-
-
-
- ))} -
-
- ); + return ; } // add loading state if (!org) { diff --git a/src/pages/Organization/components/OrganizationLayoutSkeleton.tsx b/src/pages/Organization/components/OrganizationLayoutSkeleton.tsx new file mode 100644 index 00000000000..7d9955a7367 --- /dev/null +++ b/src/pages/Organization/components/OrganizationLayoutSkeleton.tsx @@ -0,0 +1,23 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +import { + CardGridSkeleton, + CardListSkeleton, +} from "@/components/Common/SkeletonLoading"; + +export default function OrganizationLayoutSkeleton() { + return ( +
+ + +
+ +
+ + +
+ +
+
+ ); +}