Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | 21x 231x 279x 883x 1043x 1043x 1043x 1043x 2286x 382x 559x 21x 319x 319x 319x 319x 319x 319x 319x 37x 37x 306x 1021x 35x 350x | import type React from "react";
import { useSuspenseQuery } from "@tanstack/react-query";
import { Trans, useTranslation } from "react-i18next";
import { isNonNullish, values } from "remeda";
import { DebtsGroupSkeleton } from "~app/components/app/debts-group";
import { LoadablePeer } from "~app/components/app/loadable-peer";
import { SkeletonPeer } from "~app/components/app/peer";
import { EmptyCard } from "~app/components/empty-card";
import {
PaginationBlock,
PaginationBlockSkeleton,
} from "~app/components/pagination-block";
import { SuspendedOverlay } from "~app/components/pagination-overlay";
import { PeerDebtsGroup } from "~app/components/peer-debts-group";
import { suspendedFallback } from "~app/components/suspense-wrapper";
import { useCursorPaging } from "~app/hooks/use-cursor-paging";
import { useShowResolvedDebts } from "~app/hooks/use-show-resolved-debts";
import type {
SearchParamState,
SearchParamStateDefaulted,
} from "~app/utils/navigation";
import { useTRPC } from "~app/utils/trpc";
import { Card } from "~components/card";
import { Icon } from "~components/icons";
import { ButtonLink, CardLink } from "~components/link";
import { Text } from "~components/text";
import type { ViewReactNode } from "~components/view";
import { View } from "~components/view";
import type { PeerId } from "~db/ids";
const cardClassName =
"flex flex-row flex-wrap items-end justify-between gap-4 md:flex-row md:items-center";
const DebtsWrapper: React.FC<{ children: ViewReactNode }> = ({ children }) => (
<View className="gap-2">{children}</View>
);
const PeerDebtsCard: React.FC<{ peerId: PeerId }> = ({ peerId }) => {
const trpc = useTRPC();
const { data: debts } = useSuspenseQuery(
trpc.debts.getAllPeer.queryOptions({ peerId }),
);
const [showResolvedDebts] = useShowResolvedDebts();
if (
debts.items.filter((item) => item.sum !== 0).length === 0 &&
!showResolvedDebts
) {
return null;
}
return (
<CardLink
to="/debts/peer/$id"
params={{ id: peerId }}
bodyClassName={cardClassName}
testID="peer-debts-preview"
>
<LoadablePeer id={peerId} />
<View className="flex flex-row items-center justify-center gap-2">
<PeerDebtsGroup peerId={peerId} className="shrink-0" />
</View>
</CardLink>
);
};
type Props = {
limitState: SearchParamStateDefaulted<"/_protected/debts/", "limit">;
offsetState: SearchParamState<"/_protected/debts/", "offset">;
};
export const Debts = suspendedFallback<Props>(
({ limitState, offsetState }) => {
const { t } = useTranslation("debts");
const [showResolvedDebts] = useShowResolvedDebts();
const [limit, setLimit] = limitState;
const filters = {};
const trpc = useTRPC();
const { data, onPageChange, isPending } = useCursorPaging(
trpc.debts.getPeersPaged,
{ limit, filters: { showResolved: showResolvedDebts, ...filters } },
offsetState,
);
if (!data.count) {
Eif (values(filters).filter(isNonNullish).length === 0) {
return (
<Text variant="h3" className="text-center">
{t("list.filters.noResults")}
</Text>
);
}
return (
<EmptyCard title={t("list.empty.title")}>
<View className="items-center gap-4">
<View className="flex-row items-center">
<Trans
t={t}
i18nKey="list.empty.description"
components={{
text: <Text className="text-xl" />,
button: (
<ButtonLink
to="/debts/add"
color="primary"
title={t("list.buttons.add")}
variant="bordered"
isIconOnly
className="mx-2"
>
<Icon name="add" className="size-6" />
</ButtonLink>
),
}}
/>
</View>
</View>
</EmptyCard>
);
}
return (
<>
<PaginationBlock
totalCount={data.count}
limit={limit}
setLimit={setLimit}
offset={offsetState[0]}
onPageChange={onPageChange}
/>
<SuspendedOverlay isPending={isPending}>
<DebtsWrapper>
{data.items.map((peerId) => (
<PeerDebtsCard key={peerId} peerId={peerId} />
))}
</DebtsWrapper>
</SuspendedOverlay>
</>
);
},
({ limitState }) => (
<>
<PaginationBlockSkeleton limit={limitState[0]} />
<SuspendedOverlay isPending>
<DebtsWrapper>
{Array.from({ length: limitState[0] }).map((_, index) => (
<Card
// oxlint-disable-next-line react/no-array-index-key
key={index}
bodyClassName={cardClassName}
>
<SkeletonPeer />
<View className="flex flex-row items-center justify-center gap-2">
<DebtsGroupSkeleton className="shrink-0" amount={3} />
</View>
</Card>
))}
</DebtsWrapper>
</SuspendedOverlay>
</>
),
);
|