💄handle empty data

This commit is contained in:
Fred KISSIE
2025-11-21 04:47:13 +01:00
parent d41bd3023f
commit 7924f195aa
2 changed files with 26 additions and 8 deletions

View File

@@ -2154,5 +2154,6 @@
"niceIdUpdateErrorDescription": "An error occurred while updating the Nice ID.", "niceIdUpdateErrorDescription": "An error occurred while updating the Nice ID.",
"niceIdCannotBeEmpty": "Nice ID cannot be empty", "niceIdCannotBeEmpty": "Nice ID cannot be empty",
"enterIdentifier": "Enter identifier", "enterIdentifier": "Enter identifier",
"identifier": "Identifier" "identifier": "Identifier",
"noData": "No Data"
} }

View File

@@ -11,7 +11,7 @@ import { useQuery } from "@tanstack/react-query";
import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
import { Card, CardContent, CardHeader } from "./ui/card"; import { Card, CardContent, CardHeader } from "./ui/card";
import { RefreshCw, XIcon } from "lucide-react"; import { LoaderIcon, RefreshCw, XIcon } from "lucide-react";
import { DateRangePicker, type DateTimeValue } from "./DateTimePicker"; import { DateRangePicker, type DateTimeValue } from "./DateTimePicker";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { cn } from "@app/lib/cn"; import { cn } from "@app/lib/cn";
@@ -74,7 +74,8 @@ export function LogAnalyticsData(props: AnalyticsContentProps) {
const { const {
data: stats, data: stats,
isFetching: isFetchingAnalytics, isFetching: isFetchingAnalytics,
refetch: refreshAnalytics refetch: refreshAnalytics,
isLoading: isLoadingAnalytics // only `true` when there is no data yet
} = useQuery( } = useQuery(
logQueries.requestAnalytics({ logQueries.requestAnalytics({
orgId: props.orgId, orgId: props.orgId,
@@ -296,6 +297,7 @@ export function LogAnalyticsData(props: AnalyticsContentProps) {
<TopCountriesList <TopCountriesList
countries={stats?.requestsPerCountry ?? []} countries={stats?.requestsPerCountry ?? []}
total={stats?.totalRequests ?? 0} total={stats?.totalRequests ?? 0}
isLoading={isLoadingAnalytics}
/> />
</CardContent> </CardContent>
</Card> </Card>
@@ -310,6 +312,7 @@ type TopCountriesListProps = {
count: number; count: number;
}[]; }[];
total: number; total: number;
isLoading: boolean;
}; };
function TopCountriesList(props: TopCountriesListProps) { function TopCountriesList(props: TopCountriesListProps) {
@@ -331,13 +334,27 @@ function TopCountriesList(props: TopCountriesListProps) {
return ( return (
<div className="h-full flex flex-col gap-2"> <div className="h-full flex flex-col gap-2">
<div className="grid grid-cols-7 text-sm text-muted-foreground font-medium h-4"> {props.countries.length > 0 && (
<div className="col-span-5">{t("countries")}</div> <div className="grid grid-cols-7 text-sm text-muted-foreground font-medium h-4">
<div className="text-end">{t("total")}</div> <div className="col-span-5">{t("countries")}</div>
<div className="text-end">%</div> <div className="text-end">{t("total")}</div>
</div> <div className="text-end">%</div>
</div>
)}
{/* `aspect-475/335` is the same aspect ratio as the world map component */} {/* `aspect-475/335` is the same aspect ratio as the world map component */}
<ol className="w-full overflow-auto grid gap-1 aspect-475/335"> <ol className="w-full overflow-auto grid gap-1 aspect-475/335">
{props.countries.length === 0 && (
<div className="flex items-center justify-center size-full text-muted-foreground font-mono gap-1">
{props.isLoading ? (
<>
<LoaderIcon className="size-4 animate-spin" />{" "}
{t("loading")}
</>
) : (
t("noData")
)}
</div>
)}
{props.countries.map((country) => { {props.countries.map((country) => {
const percent = country.count / props.total; const percent = country.count / props.total;
return ( return (