Files
cal.diy/packages/features/insights/components/booking/RecentNoShowGuestsChart.tsx
T
Eunjae LeeandDevin AI 6479adf14e fix: integer to text comparison in routing insights query (#25019)
* fix: integer to text comparison in routing insights query

Add explicit integer array cast to prevent PostgreSQL type mismatch error when comparing bookingUserId (integer) with user_id array values in getRoutedToPerPeriodData query

* add e2e tests

* refactor: remove LoadingInsight component and handle loading in ChartCard

- Enhanced ChartCard to render default loading UI when isPending is true
- Replaced all LoadingInsight usages with ChartCard that accepts isPending/isError props
- Removed LoadingInsight component and updated exports
- Updated 16 chart components to use the new pattern
- ChartCard now shows spinner and skeleton title during loading state

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: make children prop optional in ChartCard when isPending is true

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: remove unused loadingState prop from ChartCard

- Remove loadingState prop and ChartLoadingState type export
- Simplify computedLoadingState to derive state only from isPending/isError
- No functional changes - loadingState was not being used by any components
- data-loading-state attribute behavior remains unchanged for E2E tests

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: remove duplicate isPending early returns from chart components

- Update all 16 chart components to use single ChartCard return pattern
- Gate children rendering with !isPending && isSuccess && data checks
- Prevents data processing code from executing during loading state
- Improves code consistency and maintainability across all charts

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: remove redundant !isPending check from chart conditionals

- Simplify conditional rendering to use just 'isSuccess && data' or 'isSuccess'
- In TanStack Query, isSuccess and isPending are mutually exclusive
- The !isPending check was redundant since isSuccess already implies !isPending
- Applied to all 16 chart components for consistency
- Components with safe defaults (data ?? []) use just 'isSuccess'
- Components requiring data check use 'isSuccess && data'

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* revert the mistake

* apply feedback

* apply feedback

* fix e2e

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-12-01 15:07:57 +01:00

84 lines
3.0 KiB
TypeScript

"use client";
import { useCopy } from "@calcom/lib/hooks/useCopy";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { Button } from "@calcom/ui/components/button";
import { showToast } from "@calcom/ui/components/toast";
import { useInsightsBookingParameters } from "../../hooks/useInsightsBookingParameters";
import { ChartCard, ChartCardItem } from "../ChartCard";
export const RecentNoShowGuestsChart = () => {
const { t } = useLocale();
const { copyToClipboard, isCopied } = useCopy();
const insightsBookingParams = useInsightsBookingParameters();
const timeZone = insightsBookingParams.timeZone;
const { data, isSuccess, isPending, isError } = trpc.viewer.insights.recentNoShowGuests.useQuery(
insightsBookingParams,
{
staleTime: 180000,
refetchOnWindowFocus: false,
trpc: {
context: { skipBatch: true },
},
}
);
const handleCopyEmail = (email: string) => {
copyToClipboard(email);
showToast(t("email_copied"), "success");
};
return (
<ChartCard
title={t("recent_no_show_guests")}
titleTooltip={t("recent_no_show_guests_tooltip")}
className="h-full"
isPending={isPending}
isError={isError}>
{isSuccess && data ? (
<div className="h-full">
<div className="sm:max-h-[30.6rem] sm:overflow-y-auto">
{data.map((item) => (
<ChartCardItem key={item.bookingId}>
<div className="flex w-full items-center justify-between">
<div className="flex gap-2">
<div className="bg-subtle h-16 w-[2px] shrink-0 rounded-sm" />
<div className="flex flex-col stack-y-1">
<p className="text-sm font-medium">{item.guestName}</p>
<div className="text-subtle text-sm leading-tight">
<p>{item.eventTypeName}</p>
<p>
{Intl.DateTimeFormat(undefined, {
timeZone,
dateStyle: "medium",
timeStyle: "short",
}).format(new Date(item.startTime))}
</p>
</div>
</div>
</div>
<Button
color="minimal"
size="sm"
StartIcon={isCopied ? "clipboard-check" : "clipboard"}
onClick={() => handleCopyEmail(item.guestEmail)}>
{!isCopied ? t("email") : t("copied")}
</Button>
</div>
</ChartCardItem>
))}
</div>
{data.length === 0 && (
<div className="flex h-60 text-center">
<p className="m-auto text-sm font-light">{t("insights_no_data_found_for_filter")}</p>
</div>
)}
</div>
) : null}
</ChartCard>
);
};