diff --git a/src/components/settings/sources-section.tsx b/src/components/settings/sources-section.tsx index b4c07876e..fb0f48f19 100644 --- a/src/components/settings/sources-section.tsx +++ b/src/components/settings/sources-section.tsx @@ -40,6 +40,7 @@ import { } from "lucide-react"; import { Button } from "@/components/ui/button"; +import { Skeleton } from "@/components/ui/skeleton"; import { useTranslations } from "@/lib/i18n/context"; import { queryKeys } from "@/lib/query-keys"; import { @@ -314,10 +315,7 @@ export function SourcesSection() {

{isLoading || !priority ? ( -
- - {t("common.loading")} -
+ ) : (
{SOURCE_PRIORITY_METRIC_KEYS.map((metric) => { @@ -564,3 +562,33 @@ export function SourcesSection() { ); } + +/** + * Skeleton placeholder rendered while `/api/auth/me/source-priority` + * is in flight. Reserves one row per `SOURCE_PRIORITY_METRIC_KEYS` + * entry at roughly the loaded height so the page does not jump when + * the fetched ladder list swaps in. The pulsing animation honours + * `prefers-reduced-motion` via Tailwind's `motion-reduce:animate-none`. + */ +function SourcesSkeletonList() { + return ( + + ); +} diff --git a/src/components/settings/thresholds-editor-section.tsx b/src/components/settings/thresholds-editor-section.tsx index 036142a11..2e337bf00 100644 --- a/src/components/settings/thresholds-editor-section.tsx +++ b/src/components/settings/thresholds-editor-section.tsx @@ -3,15 +3,11 @@ import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; -import { - SlidersHorizontal, - RotateCcw, - Loader2, - AlertTriangle, -} from "lucide-react"; +import { SlidersHorizontal, RotateCcw, AlertTriangle } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { Skeleton } from "@/components/ui/skeleton"; import { Switch } from "@/components/ui/switch"; import { Badge } from "@/components/ui/badge"; import { useTranslations, useFormatters } from "@/lib/i18n/context"; @@ -147,10 +143,7 @@ export function ThresholdsEditorSection({ id }: { id: string }) {
{isLoading || !data ? ( -
- - {t("common.loading")} -
+ ) : (
{METRIC_ORDER.map((metric) => ( @@ -170,6 +163,41 @@ export function ThresholdsEditorSection({ id }: { id: string }) { ); } +/** + * Skeleton placeholder rendered while `/api/user/thresholds` is in + * flight. Reserves one row per `METRIC_ORDER` entry at roughly the + * loaded height so the page does not jump when the fetched list + * swaps in. The pulsing animation honours `prefers-reduced-motion` + * via Tailwind's `motion-reduce:animate-none`. + */ +function ThresholdsSkeletonList() { + return ( + + ); +} + interface MetricRowProps { metric: ThresholdMetric; effective: EffectiveRange | undefined;