fix: Loading Skeleton for survey summary page (#3108)

Co-authored-by: Johannes <johannes@formbricks.com>
Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com>
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
Co-authored-by: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com>
This commit is contained in:
RajuGangitla
2024-09-09 20:41:56 +05:30
committed by GitHub
parent 0532f2744b
commit 5cc071e5a8
4 changed files with 81 additions and 28 deletions

View File

@@ -6,20 +6,25 @@ interface SummaryMetadataProps {
setShowDropOffs: React.Dispatch<React.SetStateAction<boolean>>;
showDropOffs: boolean;
surveySummary: TSurveySummary["meta"];
isLoading: boolean;
}
const StatCard = ({ label, percentage, value, tooltipText }) => (
const StatCard = ({ label, percentage, value, tooltipText, isLoading }) => (
<TooltipProvider delayDuration={50}>
<Tooltip>
<TooltipTrigger>
<div className="flex h-full cursor-default flex-col justify-between space-y-2 rounded-xl border border-slate-200 bg-white p-4 text-left shadow-sm">
<p className="flex items-center gap-1 text-sm text-slate-600">
{label}
{typeof percentage === "number" && !isNaN(percentage) && (
{typeof percentage === "number" && !isNaN(percentage) && !isLoading && (
<span className="ml-1 rounded-xl bg-slate-100 px-2 py-1 text-xs">{percentage}%</span>
)}
</p>
<p className="text-2xl font-bold text-slate-800">{value}</p>
{isLoading ? (
<div className="h-6 w-12 animate-pulse rounded-full bg-slate-200"></div>
) : (
<p className="text-2xl font-bold text-slate-800">{value}</p>
)}
</div>
</TooltipTrigger>
<TooltipContent>
@@ -44,7 +49,12 @@ const formatTime = (ttc) => {
return formattedValue;
};
export const SummaryMetadata = ({ setShowDropOffs, showDropOffs, surveySummary }: SummaryMetadataProps) => {
export const SummaryMetadata = ({
setShowDropOffs,
showDropOffs,
surveySummary,
isLoading,
}: SummaryMetadataProps) => {
const {
completedPercentage,
completedResponses,
@@ -64,18 +74,21 @@ export const SummaryMetadata = ({ setShowDropOffs, showDropOffs, surveySummary }
percentage={null}
value={displayCount === 0 ? <span>-</span> : displayCount}
tooltipText="Number of times the survey has been viewed."
isLoading={isLoading}
/>
<StatCard
label="Starts"
percentage={Math.round(startsPercentage) > 100 ? null : Math.round(startsPercentage)}
value={totalResponses === 0 ? <span>-</span> : totalResponses}
tooltipText="Number of times the survey has been started."
isLoading={isLoading}
/>
<StatCard
label="Completed"
percentage={Math.round(completedPercentage) > 100 ? null : Math.round(completedPercentage)}
value={completedResponses === 0 ? <span>-</span> : completedResponses}
tooltipText="Number of times the survey has been completed."
isLoading={isLoading}
/>
<TooltipProvider delayDuration={50}>
@@ -86,21 +99,29 @@ export const SummaryMetadata = ({ setShowDropOffs, showDropOffs, surveySummary }
className="group flex h-full w-full cursor-pointer flex-col justify-between space-y-2 rounded-lg border border-slate-200 bg-white p-4 text-left shadow-sm">
<span className="text-sm text-slate-600">
Drop-Offs
{`${Math.round(dropOffPercentage)}%` !== "NaN%" && (
{`${Math.round(dropOffPercentage)}%` !== "NaN%" && !isLoading && (
<span className="ml-1 rounded-xl bg-slate-100 px-2 py-1 text-xs">{`${Math.round(dropOffPercentage)}%`}</span>
)}
</span>
<div className="flex w-full items-end justify-between">
<span className="text-2xl font-bold text-slate-800">
{dropOffCount === 0 ? <span>-</span> : dropOffCount}
</span>
<span className="ml-1 flex items-center rounded-md bg-slate-800 px-2 py-1 text-xs text-slate-50 group-hover:bg-slate-700">
{showDropOffs ? (
<ChevronUpIcon className="h-4 w-4" />
{isLoading ? (
<div className="h-6 w-12 animate-pulse rounded-full bg-slate-200"></div>
) : dropOffCount === 0 ? (
<span>-</span>
) : (
<ChevronDownIcon className="h-4 w-4" />
dropOffCount
)}
</span>
{!isLoading && (
<span className="ml-1 flex items-center rounded-md bg-slate-800 px-2 py-1 text-xs text-slate-50 group-hover:bg-slate-700">
{showDropOffs ? (
<ChevronUpIcon className="h-4 w-4" />
) : (
<ChevronDownIcon className="h-4 w-4" />
)}
</span>
)}
</div>
</div>
</TooltipTrigger>
@@ -114,6 +135,7 @@ export const SummaryMetadata = ({ setShowDropOffs, showDropOffs, surveySummary }
percentage={null}
value={ttcAverage === 0 ? <span>-</span> : `${formatTime(ttcAverage)}`}
tooltipText="Average time to complete the survey."
isLoading={isLoading}
/>
</div>
</div>

View File

@@ -67,6 +67,7 @@ export const SummaryPage = ({
const [responseCount, setResponseCount] = useState<number | null>(null);
const [surveySummary, setSurveySummary] = useState<TSurveySummary>(initialSurveySummary);
const [showDropOffs, setShowDropOffs] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState(true);
const { selectedFilter, dateRange, resetState } = useResponseFilter();
@@ -104,28 +105,39 @@ export const SummaryPage = ({
});
};
const handleInitialData = async () => {
const handleInitialData = async (isInitialLoad = false) => {
if (isInitialLoad) {
setIsLoading(true);
}
try {
const updatedResponseCountData = await getResponseCount();
const updatedSurveySummary = await getSummary();
const [updatedResponseCountData, updatedSurveySummary] = await Promise.all([
getResponseCount(),
getSummary(),
]);
const responseCount = updatedResponseCountData?.data ?? 0;
const surveySummary = updatedSurveySummary?.data ?? initialSurveySummary;
// Update the state with new data
setResponseCount(responseCount);
setSurveySummary(surveySummary);
} catch (error) {
console.error(error);
} finally {
if (isInitialLoad) {
setIsLoading(false);
}
}
};
useEffect(() => {
handleInitialData();
handleInitialData(true);
}, [JSON.stringify(filters), isSharingPage, sharingKey, surveyId]);
useIntervalWhenFocused(
() => {
handleInitialData();
handleInitialData(false);
},
10000,
!isShareEmbedModalOpen,
@@ -148,6 +160,7 @@ export const SummaryPage = ({
surveySummary={surveySummary.meta}
showDropOffs={showDropOffs}
setShowDropOffs={setShowDropOffs}
isLoading={isLoading}
/>
{showDropOffs && <SummaryDropOffs dropOff={surveySummary.dropOff} />}
<div className="flex gap-1.5">

View File

@@ -0,0 +1,20 @@
import { PageContentWrapper } from "@formbricks/ui/PageContentWrapper";
import { PageHeader } from "@formbricks/ui/PageHeader";
import { SkeletonLoader } from "@formbricks/ui/SkeletonLoader";
const Loading = () => {
return (
<PageContentWrapper>
<PageHeader pageTitle="Summary" />
<div className="flex h-9 animate-pulse gap-2">
<div className="h-9 w-36 rounded-full bg-slate-200"></div>
<div className="h-9 w-36 rounded-full bg-slate-200"></div>
<div className="h-9 w-36 rounded-full bg-slate-200"></div>
<div className="h-9 w-36 rounded-full bg-slate-200"></div>
</div>
<SkeletonLoader type="summary" />
</PageContentWrapper>
);
};
export default Loading;

View File

@@ -11,16 +11,14 @@ export const SkeletonLoader = ({ type }: SkeletonLoaderProps) => {
className="rounded-xl border border-slate-200 bg-white shadow-sm"
data-testid="skeleton-loader-summary">
<Skeleton className="group space-y-4 rounded-xl bg-white p-6">
<div className="flex items-center space-x-4">
<div className="h-6 w-full rounded-full bg-slate-100"></div>
</div>
<div className="space-y-4">
<div className="flex gap-4">
<div className="h-6 w-24 rounded-full bg-slate-100"></div>
<div className="h-6 w-24 rounded-full bg-slate-100"></div>
<div className="h-6 w-24 rounded-full bg-slate-200"></div>
<div className="h-6 w-24 rounded-full bg-slate-200"></div>
</div>
<div className="flex h-12 w-full items-center justify-center rounded-full bg-slate-50 text-sm text-slate-500 hover:bg-slate-100"></div>
<div className="h-12 w-full rounded-full bg-slate-50/50"></div>
<div className="flex h-12 w-full items-center justify-center rounded-full bg-slate-200 text-sm text-slate-500"></div>
<div className="h-12 w-full rounded-full bg-slate-200"></div>
<div className="h-12 w-full rounded-full bg-slate-200"></div>
</div>
</Skeleton>
</div>
@@ -31,13 +29,13 @@ export const SkeletonLoader = ({ type }: SkeletonLoaderProps) => {
return (
<div className="group space-y-4 rounded-lg bg-white p-6" data-testid="skeleton-loader-response">
<div className="flex items-center space-x-4">
<Skeleton className="h-12 w-12 flex-shrink-0 rounded-full bg-slate-100"></Skeleton>
<Skeleton className="h-6 w-full rounded-full bg-slate-100"></Skeleton>
<Skeleton className="h-12 w-12 flex-shrink-0 rounded-full bg-slate-200"></Skeleton>
<Skeleton className="h-6 w-full rounded-full bg-slate-200"></Skeleton>
</div>
<div className="space-y-4">
<Skeleton className="h-12 w-full rounded-full bg-slate-100"></Skeleton>
<Skeleton className="flex h-12 w-full items-center justify-center rounded-full bg-slate-50 text-sm text-slate-500 hover:bg-slate-100"></Skeleton>
<Skeleton className="h-12 w-full rounded-full bg-slate-50/50"></Skeleton>
<Skeleton className="h-12 w-full rounded-full bg-slate-200"></Skeleton>
<Skeleton className="flex h-12 w-full items-center justify-center rounded-full bg-slate-200 text-sm text-slate-500"></Skeleton>
<Skeleton className="h-12 w-full rounded-full bg-slate-200"></Skeleton>
</div>
</div>
);