From d776478350a730c83e51bb55c561a72192380b82 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 11 Sep 2026 14:09:56 -0400 Subject: [PATCH] add better page loading indicator --- src/app/[orgId]/loading.tsx | 8 +-- src/components/LoadingDots.tsx | 56 +++++++++++++++++++ src/components/OrgRouteLoading.tsx | 45 +++++++++++++++ .../LauncherFlatResourceList.tsx | 5 +- .../resource-launcher/LauncherGroupList.tsx | 5 +- .../LauncherGroupSection.tsx | 5 +- 6 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 src/components/LoadingDots.tsx create mode 100644 src/components/OrgRouteLoading.tsx diff --git a/src/app/[orgId]/loading.tsx b/src/app/[orgId]/loading.tsx index 0f7ea0d31..0ac4b304d 100644 --- a/src/app/[orgId]/loading.tsx +++ b/src/app/[orgId]/loading.tsx @@ -1,9 +1,5 @@ -import { Loader2 } from "lucide-react"; +import OrgRouteLoading from "@app/components/OrgRouteLoading"; export default function OrgPageLoading() { - return ( -
- -
- ); + return ; } diff --git a/src/components/LoadingDots.tsx b/src/components/LoadingDots.tsx new file mode 100644 index 000000000..d1de6a19f --- /dev/null +++ b/src/components/LoadingDots.tsx @@ -0,0 +1,56 @@ +import { cn } from "@app/lib/cn"; + +type LoadingDotsProps = { + className?: string; + size?: "sm" | "md"; +}; + +const sizeClasses = { + sm: { + gap: "gap-1.5", + dot: "h-1.5 w-1.5" + }, + md: { + gap: "gap-2.5", + dot: "h-2.5 w-2.5" + } +} as const; + +export default function LoadingDots({ + className, + size = "md" +}: LoadingDotsProps) { + const classes = sizeClasses[size]; + + return ( + + + + + + ); +} diff --git a/src/components/OrgRouteLoading.tsx b/src/components/OrgRouteLoading.tsx new file mode 100644 index 000000000..29d12c5ed --- /dev/null +++ b/src/components/OrgRouteLoading.tsx @@ -0,0 +1,45 @@ +"use client"; + +import Link from "next/link"; +import BrandingLogo from "@app/components/BrandingLogo"; +import LoadingDots from "@app/components/LoadingDots"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; + +export default function OrgRouteLoading() { + const { env } = useEnvContext(); + const { isUnlocked } = useLicenseStatusContext(); + + const logoWidth = isUnlocked() + ? env.branding.logo?.navbar?.width || 98 + : 98; + const logoHeight = isUnlocked() + ? env.branding.logo?.navbar?.height || 32 + : 32; + + return ( +
+
+
+
+
+ + + +
+
+
+
+ +
+ +
+
+ ); +} diff --git a/src/components/resource-launcher/LauncherFlatResourceList.tsx b/src/components/resource-launcher/LauncherFlatResourceList.tsx index 7bbc01ccb..e14e6e452 100644 --- a/src/components/resource-launcher/LauncherFlatResourceList.tsx +++ b/src/components/resource-launcher/LauncherFlatResourceList.tsx @@ -9,6 +9,7 @@ import type { } from "@server/routers/launcher/types"; import { LAUNCHER_FLAT_GROUP_KEY } from "@server/routers/launcher/types"; import { useInfiniteQuery } from "@tanstack/react-query"; +import LoadingDots from "@app/components/LoadingDots"; import { Loader2 } from "lucide-react"; import { useEffect, useMemo, useRef } from "react"; import { LauncherEmptyState } from "./LauncherEmptyState"; @@ -81,8 +82,8 @@ export function LauncherFlatResourceList({ if (resources.length === 0) { if (isFetching) { return ( -
- +
+
); } diff --git a/src/components/resource-launcher/LauncherGroupList.tsx b/src/components/resource-launcher/LauncherGroupList.tsx index ecf60a773..46b4ba0b1 100644 --- a/src/components/resource-launcher/LauncherGroupList.tsx +++ b/src/components/resource-launcher/LauncherGroupList.tsx @@ -8,6 +8,7 @@ import type { LauncherViewConfig } from "@server/routers/launcher/types"; import { useInfiniteQuery } from "@tanstack/react-query"; +import LoadingDots from "@app/components/LoadingDots"; import { Loader2 } from "lucide-react"; import { useEffect, useMemo, useRef } from "react"; import { LauncherEmptyState } from "./LauncherEmptyState"; @@ -109,8 +110,8 @@ export function LauncherGroupList({ if (groups.length === 0) { if (isFetching) { return ( -
- +
+
); } diff --git a/src/components/resource-launcher/LauncherGroupSection.tsx b/src/components/resource-launcher/LauncherGroupSection.tsx index 91320b383..c40ceb7d0 100644 --- a/src/components/resource-launcher/LauncherGroupSection.tsx +++ b/src/components/resource-launcher/LauncherGroupSection.tsx @@ -22,6 +22,7 @@ import { LAUNCHER_UNLABELED_GROUP_KEY } from "@server/routers/launcher/types"; import { useInfiniteQuery } from "@tanstack/react-query"; +import LoadingDots from "@app/components/LoadingDots"; import { Loader2 } from "lucide-react"; import { useTranslations } from "next-intl"; import { useEffect, useRef, useState } from "react"; @@ -169,8 +170,8 @@ export function LauncherGroupSection({ {showInitialLoader ? ( -
- +
+
) : resources.length === 0 ? (