diff --git a/lib/hooks/QueryCell.tsx b/lib/hooks/QueryCell.tsx new file mode 100644 index 0000000000..9ed48341bd --- /dev/null +++ b/lib/hooks/QueryCell.tsx @@ -0,0 +1,54 @@ +import { + QueryObserverIdleResult, + QueryObserverLoadingErrorResult, + QueryObserverLoadingResult, + QueryObserverRefetchErrorResult, + QueryObserverSuccessResult, + UseQueryResult, +} from "react-query"; + +import Loader from "@components/Loader"; +import { Alert } from "@components/ui/Alert"; + +type ErrorLike = { + message: string; +}; +interface QueryCellOptions { + query: UseQueryResult; + success: (query: QueryObserverSuccessResult) => JSX.Element; + error?: ( + query: QueryObserverLoadingErrorResult | QueryObserverRefetchErrorResult + ) => JSX.Element; + loading?: (query: QueryObserverLoadingResult) => JSX.Element; + idle?: (query: QueryObserverIdleResult) => JSX.Element; + /** + * If there's no data (`null`, `undefined`, or `[]`), render this component + */ + empty?: (query: QueryObserverSuccessResult) => JSX.Element; +} + +export function QueryCell(opts: QueryCellOptions) { + const { query } = opts; + + if (query.status === "success") { + if (opts.empty && (query.data == null || (Array.isArray(query.data) && query.data.length === 0))) { + return opts.empty(query); + } + return opts.success(query); + } + if (query.status === "error") { + return ( + opts.error?.(query) ?? ( + + ) + ); + } + if (query.status === "loading") { + return opts.loading?.(query) ?? ; + } + if (query.status === "idle") { + return null; + } + // impossible state + return null; +} diff --git a/pages/bookings/[status].tsx b/pages/bookings/[status].tsx index 080ecf8555..6079889f38 100644 --- a/pages/bookings/[status].tsx +++ b/pages/bookings/[status].tsx @@ -1,14 +1,13 @@ import { CalendarIcon } from "@heroicons/react/outline"; import { useRouter } from "next/router"; +import { QueryCell } from "@lib/hooks/QueryCell"; import { inferQueryInput, trpc } from "@lib/trpc"; import BookingsShell from "@components/BookingsShell"; import EmptyScreen from "@components/EmptyScreen"; -import Loader from "@components/Loader"; import Shell from "@components/Shell"; import BookingListItem from "@components/booking/BookingListItem"; -import { Alert } from "@components/ui/Alert"; type BookingListingStatus = inferQueryInput<"viewer.bookings">["status"]; @@ -16,7 +15,6 @@ export default function Bookings() { const router = useRouter(); const status = router.query?.status as BookingListingStatus; const query = trpc.useQuery(["viewer.bookings", { status }]); - const bookings = query.data; return ( @@ -24,28 +22,27 @@ export default function Bookings() {
- {query.status === "error" && ( - - )} - {query.status === "loading" && } - {bookings && - (bookings.length === 0 ? ( - - ) : ( + (
- {bookings.map((booking) => ( + {data.map((booking) => ( ))}
- ))} + )} + empty={() => ( + + )} + />