Skip to content

Commit 531baf0

Browse files
authored
Optimize Filter UI (reduce rerendering) (dubinc#3633)
1 parent 284a858 commit 531baf0

18 files changed

Lines changed: 721 additions & 637 deletions

File tree

apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsx

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ export function PayoutTable() {
4646
const { payouts, error, loading } = usePartnerPayouts();
4747
const { payoutsCount } = usePartnerPayoutsCount<number>();
4848

49-
const { filters, activeFilters, onSelect, onRemove, onRemoveAll } =
50-
usePayoutFilters();
51-
5249
const [detailsSheetState, setDetailsSheetState] = useState<
5350
| { open: false; payout: PartnerPayoutResponse | null }
5451
| { open: true; payout: PartnerPayoutResponse }
@@ -234,26 +231,7 @@ export function PayoutTable() {
234231
/>
235232
)}
236233
<div className="flex flex-col gap-3">
237-
<div className="flex flex-col gap-3">
238-
<Filter.Select
239-
className="w-full md:w-fit"
240-
filters={filters}
241-
activeFilters={activeFilters}
242-
onSelect={onSelect}
243-
onRemove={onRemove}
244-
/>
245-
<AnimatedSizeContainer height>
246-
{activeFilters.length > 0 && (
247-
<Filter.List
248-
filters={filters}
249-
activeFilters={activeFilters}
250-
onSelect={onSelect}
251-
onRemove={onRemove}
252-
onRemoveAll={onRemoveAll}
253-
/>
254-
)}
255-
</AnimatedSizeContainer>
256-
</div>
234+
<PartnerPayoutFilters />
257235
{payouts?.length !== 0 ? (
258236
<Table {...table} />
259237
) : (
@@ -273,6 +251,34 @@ export function PayoutTable() {
273251
);
274252
}
275253

254+
function PartnerPayoutFilters() {
255+
const { filters, activeFilters, onSelect, onRemove, onRemoveAll } =
256+
usePayoutFilters();
257+
258+
return (
259+
<div className="flex flex-col gap-3">
260+
<Filter.Select
261+
className="w-full md:w-fit"
262+
filters={filters}
263+
activeFilters={activeFilters}
264+
onSelect={onSelect}
265+
onRemove={onRemove}
266+
/>
267+
<AnimatedSizeContainer height>
268+
{activeFilters.length > 0 && (
269+
<Filter.List
270+
filters={filters}
271+
activeFilters={activeFilters}
272+
onSelect={onSelect}
273+
onRemove={onRemove}
274+
onRemoveAll={onRemoveAll}
275+
/>
276+
)}
277+
</AnimatedSizeContainer>
278+
</div>
279+
);
280+
}
281+
276282
function AmountRowItem({ payout }: { payout: PartnerPayoutResponse }) {
277283
const display = currencyFormatter(payout.amount);
278284

apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/use-payout-filters.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,11 @@ export function usePayoutFilters() {
101101
[queryParams],
102102
);
103103

104-
const isFiltered = useMemo(() => activeFilters.length > 0, [activeFilters]);
105-
106104
return {
107105
filters,
108106
activeFilters,
109107
onSelect,
110108
onRemove,
111109
onRemoveAll,
112-
isFiltered,
113110
};
114111
}

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-submissions-table.tsx

Lines changed: 89 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,6 @@ export function BountySubmissionsTable() {
105105
const { submissionsCount } =
106106
useBountySubmissionsCount<SubmissionsCountByStatus[]>();
107107

108-
const {
109-
filters,
110-
activeFilters,
111-
onSelect,
112-
onRemove,
113-
onRemoveAll,
114-
setSearch,
115-
setSelectedFilter,
116-
} = useBountySubmissionFilters({ bounty });
117-
118108
const {
119109
error,
120110
isLoading,
@@ -452,59 +442,13 @@ export function BountySubmissionsTable() {
452442

453443
<div className="flex flex-col gap-6">
454444
<div>
455-
<div className="flex w-full items-center justify-between gap-4">
456-
<Filter.Select
457-
className="w-full md:w-fit"
458-
filters={filters}
459-
activeFilters={activeFilters}
460-
onSelect={onSelect}
461-
onRemove={onRemove}
462-
onSearchChange={setSearch}
463-
onSelectedFilterChange={setSelectedFilter}
464-
/>
465-
{bountyInfo?.hasSocialMetrics && (submissions?.length ?? 0) > 0 && (
466-
<div className="flex shrink-0 items-center gap-3">
467-
{bounty?.socialMetricsLastSyncedAt ? (
468-
<span className="whitespace-nowrap text-xs font-medium text-neutral-500">
469-
Last sync{" "}
470-
{timeAgo(bounty.socialMetricsLastSyncedAt, {
471-
withAgo: true,
472-
})}
473-
</span>
474-
) : null}
475-
<Button
476-
variant="secondary"
477-
text="Refresh stats"
478-
loading={isRefreshingStats}
479-
onClick={refreshStats}
480-
className="h-8 rounded-lg px-3"
481-
/>
482-
</div>
483-
)}
484-
</div>
485-
<AnimatedSizeContainer height>
486-
<div>
487-
{activeFilters.length > 0 && (
488-
<div className="pt-3">
489-
<Filter.List
490-
filters={[
491-
...filters,
492-
{
493-
key: "payoutId",
494-
icon: MoneyBill2,
495-
label: "Payout",
496-
options: [],
497-
},
498-
]}
499-
activeFilters={activeFilters}
500-
onSelect={onSelect}
501-
onRemove={onRemove}
502-
onRemoveAll={onRemoveAll}
503-
/>
504-
</div>
505-
)}
506-
</div>
507-
</AnimatedSizeContainer>
445+
<BountySubmissionFilters
446+
bounty={bounty}
447+
bountyInfo={bountyInfo}
448+
submissionsLength={submissions?.length ?? 0}
449+
isRefreshingStats={isRefreshingStats}
450+
refreshStats={refreshStats}
451+
/>
508452
</div>
509453
{submissions?.length !== 0 || isLoading ? (
510454
<Table {...tableProps} table={table} />
@@ -524,3 +468,85 @@ export function BountySubmissionsTable() {
524468
</>
525469
);
526470
}
471+
472+
function BountySubmissionFilters({
473+
bounty,
474+
bountyInfo,
475+
submissionsLength,
476+
isRefreshingStats,
477+
refreshStats,
478+
}: {
479+
bounty: ReturnType<typeof useBounty>["bounty"];
480+
bountyInfo: ReturnType<typeof resolveBountyDetails>;
481+
submissionsLength: number;
482+
isRefreshingStats: boolean;
483+
refreshStats: () => void;
484+
}) {
485+
const {
486+
filters,
487+
activeFilters,
488+
onSelect,
489+
onRemove,
490+
onRemoveAll,
491+
setSearch,
492+
setSelectedFilter,
493+
} = useBountySubmissionFilters({ bounty: bounty ?? undefined });
494+
495+
return (
496+
<>
497+
<div className="flex w-full items-center justify-between gap-4">
498+
<Filter.Select
499+
className="w-full md:w-fit"
500+
filters={filters}
501+
activeFilters={activeFilters}
502+
onSelect={onSelect}
503+
onRemove={onRemove}
504+
onSearchChange={setSearch}
505+
onSelectedFilterChange={setSelectedFilter}
506+
/>
507+
{bountyInfo?.hasSocialMetrics && submissionsLength > 0 && (
508+
<div className="flex shrink-0 items-center gap-3">
509+
{bounty?.socialMetricsLastSyncedAt ? (
510+
<span className="whitespace-nowrap text-xs font-medium text-neutral-500">
511+
Last sync{" "}
512+
{timeAgo(bounty.socialMetricsLastSyncedAt, {
513+
withAgo: true,
514+
})}
515+
</span>
516+
) : null}
517+
<Button
518+
variant="secondary"
519+
text="Refresh stats"
520+
loading={isRefreshingStats}
521+
onClick={refreshStats}
522+
className="h-8 rounded-lg px-3"
523+
/>
524+
</div>
525+
)}
526+
</div>
527+
<AnimatedSizeContainer height>
528+
<div>
529+
{activeFilters.length > 0 && (
530+
<div className="pt-3">
531+
<Filter.List
532+
filters={[
533+
...filters,
534+
{
535+
key: "payoutId",
536+
icon: MoneyBill2,
537+
label: "Payout",
538+
options: [],
539+
},
540+
]}
541+
activeFilters={activeFilters}
542+
onSelect={onSelect}
543+
onRemove={onRemove}
544+
onRemoveAll={onRemoveAll}
545+
/>
546+
</div>
547+
)}
548+
</div>
549+
</AnimatedSizeContainer>
550+
</>
551+
);
552+
}

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/campaigns-table.tsx

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,10 @@ export function CampaignsTable() {
4444
const router = useRouter();
4545
const { id: workspaceId, slug } = useWorkspace();
4646
const { pagination, setPagination } = usePagination();
47-
const { getQueryString } = useRouterStuff();
48-
49-
const {
50-
filters,
51-
activeFilters,
52-
onSelect,
53-
onRemove,
54-
onRemoveAll,
55-
isFiltered,
56-
} = useCampaignsFilters();
47+
const { getQueryString, searchParamsObj } = useRouterStuff();
48+
const isFiltered = Object.keys(searchParamsObj).some(
49+
(key) => !["sortBy", "sortOrder", "page"].includes(key),
50+
);
5751

5852
const {
5953
data: campaigns,
@@ -150,33 +144,7 @@ export function CampaignsTable() {
150144
return (
151145
<div className="flex flex-col gap-4">
152146
<div>
153-
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
154-
<Filter.Select
155-
className="w-full md:w-fit"
156-
filters={filters}
157-
activeFilters={activeFilters}
158-
onSelect={onSelect}
159-
onRemove={onRemove}
160-
/>
161-
<SearchBoxPersisted
162-
placeholder="Search by name"
163-
inputClassName="md:w-[19rem]"
164-
/>
165-
</div>
166-
<AnimatedSizeContainer height>
167-
<div>
168-
{activeFilters.length > 0 && (
169-
<div className="pt-3">
170-
<Filter.List
171-
filters={filters}
172-
activeFilters={activeFilters}
173-
onRemove={onRemove}
174-
onRemoveAll={onRemoveAll}
175-
/>
176-
</div>
177-
)}
178-
</div>
179-
</AnimatedSizeContainer>
147+
<CampaignFilters />
180148
</div>
181149

182150
{campaigns?.length !== 0 ? (
@@ -207,6 +175,43 @@ export function CampaignsTable() {
207175
);
208176
}
209177

178+
function CampaignFilters() {
179+
const { filters, activeFilters, onSelect, onRemove, onRemoveAll } =
180+
useCampaignsFilters();
181+
182+
return (
183+
<>
184+
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
185+
<Filter.Select
186+
className="w-full md:w-fit"
187+
filters={filters}
188+
activeFilters={activeFilters}
189+
onSelect={onSelect}
190+
onRemove={onRemove}
191+
/>
192+
<SearchBoxPersisted
193+
placeholder="Search by name"
194+
inputClassName="md:w-[19rem]"
195+
/>
196+
</div>
197+
<AnimatedSizeContainer height>
198+
<div>
199+
{activeFilters.length > 0 && (
200+
<div className="pt-3">
201+
<Filter.List
202+
filters={filters}
203+
activeFilters={activeFilters}
204+
onRemove={onRemove}
205+
onRemoveAll={onRemoveAll}
206+
/>
207+
</div>
208+
)}
209+
</div>
210+
</AnimatedSizeContainer>
211+
</>
212+
);
213+
}
214+
210215
function RowMenuButton({
211216
row: { original: campaign },
212217
}: {

0 commit comments

Comments
 (0)