mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
fix(admin-bulk-actions): plug stale-closure bug in columns useMemo
Header checkbox for 'select all subscriptions' read allVisibleSubscriptionIds, subscriptionSelection, isMultiTariff, toggleAllSubscriptions from a stale closure because those values were declared AFTER the columns useMemo. The warned-about deps were absent, so the header only refreshed by accident when expandedRows or getFilteredSubs changed. Relocate filteredUsers / allVisibleSubscriptionIds / toggleAllSubscriptions above the columns block, list them as deps. Header now reflects current selection without piggybacking on unrelated re-renders. Same commit clears the matching AdminInfoPageEditor warning with an explicit eslint-disable-line comment — the activeLocale omission was already intentional (initial-content lock), just not silenced.
This commit is contained in:
@@ -1778,6 +1778,66 @@ export default function AdminBulkActions() {
|
||||
[tariffFilter, trialOnly],
|
||||
);
|
||||
|
||||
// Selection-related derivations live here so the columns useMemo (below)
|
||||
// can list them as deps without forward-reference warnings.
|
||||
// When multiple tariffs are selected, filter users client-side
|
||||
// (server only supports single tariff_id filter)
|
||||
const filteredUsers = useMemo(() => {
|
||||
let result = users;
|
||||
if (trialOnly) {
|
||||
result = result.filter(
|
||||
(u) => u.subscription_is_trial || (u.subscriptions ?? []).some((s) => s.status === 'trial'),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}, [users, trialOnly]);
|
||||
|
||||
const allVisibleSubscriptionIds = useMemo(() => {
|
||||
const ids: number[] = [];
|
||||
for (const user of filteredUsers) {
|
||||
const subs = user.subscriptions ?? [];
|
||||
const filtered = getFilteredSubs(subs);
|
||||
for (const sub of filtered) {
|
||||
ids.push(sub.id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}, [filteredUsers, getFilteredSubs]);
|
||||
|
||||
const toggleAllSubscriptions = useCallback(() => {
|
||||
const allSelected =
|
||||
allVisibleSubscriptionIds.length > 0 &&
|
||||
allVisibleSubscriptionIds.every((id) => subscriptionSelection[id]);
|
||||
|
||||
if (allSelected) {
|
||||
const next: Record<number, boolean> = {};
|
||||
for (const key of Object.keys(subscriptionSelection)) {
|
||||
const id = Number(key);
|
||||
if (!allVisibleSubscriptionIds.includes(id)) {
|
||||
next[id] = subscriptionSelection[id];
|
||||
}
|
||||
}
|
||||
setSubscriptionSelection(next);
|
||||
} else {
|
||||
const next = { ...subscriptionSelection };
|
||||
for (const id of allVisibleSubscriptionIds) {
|
||||
next[id] = true;
|
||||
}
|
||||
setSubscriptionSelection(next);
|
||||
}
|
||||
|
||||
// Auto-expand rows that have filtered subs
|
||||
const expanded: Record<number, boolean> = { ...expandedRows };
|
||||
for (const user of users) {
|
||||
const subs = user.subscriptions ?? [];
|
||||
const filtered = getFilteredSubs(subs);
|
||||
if (filtered.length > 1) {
|
||||
expanded[user.id] = true;
|
||||
}
|
||||
}
|
||||
setExpandedRows(expanded);
|
||||
}, [allVisibleSubscriptionIds, subscriptionSelection, expandedRows, users, getFilteredSubs]);
|
||||
|
||||
const handleOpenAction = (type: BulkActionType) => {
|
||||
setModal({ open: true, action: type, loading: false, result: null, progress: null });
|
||||
};
|
||||
@@ -2125,72 +2185,19 @@ export default function AdminBulkActions() {
|
||||
},
|
||||
},
|
||||
],
|
||||
[t, formatWithCurrency, expandedRows, toggleExpandRow, getFilteredSubs],
|
||||
[
|
||||
t,
|
||||
formatWithCurrency,
|
||||
expandedRows,
|
||||
toggleExpandRow,
|
||||
getFilteredSubs,
|
||||
allVisibleSubscriptionIds,
|
||||
isMultiTariff,
|
||||
subscriptionSelection,
|
||||
toggleAllSubscriptions,
|
||||
],
|
||||
);
|
||||
|
||||
// When multiple tariffs are selected, filter users client-side
|
||||
// (server only supports single tariff_id filter)
|
||||
const filteredUsers = useMemo(() => {
|
||||
let result = users;
|
||||
|
||||
// Trial-only filter: show users with trial subscription
|
||||
if (trialOnly) {
|
||||
result = result.filter(
|
||||
(u) => u.subscription_is_trial || (u.subscriptions ?? []).some((s) => s.status === 'trial'),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [users, trialOnly]);
|
||||
|
||||
const allVisibleSubscriptionIds = useMemo(() => {
|
||||
const ids: number[] = [];
|
||||
for (const user of filteredUsers) {
|
||||
const subs = user.subscriptions ?? [];
|
||||
const filtered = getFilteredSubs(subs);
|
||||
for (const sub of filtered) {
|
||||
ids.push(sub.id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}, [filteredUsers, getFilteredSubs]);
|
||||
|
||||
const toggleAllSubscriptions = useCallback(() => {
|
||||
const allSelected =
|
||||
allVisibleSubscriptionIds.length > 0 &&
|
||||
allVisibleSubscriptionIds.every((id) => subscriptionSelection[id]);
|
||||
|
||||
if (allSelected) {
|
||||
// Deselect all
|
||||
const next: Record<number, boolean> = {};
|
||||
for (const key of Object.keys(subscriptionSelection)) {
|
||||
const id = Number(key);
|
||||
if (!allVisibleSubscriptionIds.includes(id)) {
|
||||
next[id] = subscriptionSelection[id];
|
||||
}
|
||||
}
|
||||
setSubscriptionSelection(next);
|
||||
} else {
|
||||
// Select all visible
|
||||
const next = { ...subscriptionSelection };
|
||||
for (const id of allVisibleSubscriptionIds) {
|
||||
next[id] = true;
|
||||
}
|
||||
setSubscriptionSelection(next);
|
||||
}
|
||||
|
||||
// Auto-expand rows that have filtered subs
|
||||
const expanded: Record<number, boolean> = { ...expandedRows };
|
||||
for (const user of users) {
|
||||
const subs = user.subscriptions ?? [];
|
||||
const filtered = getFilteredSubs(subs);
|
||||
if (filtered.length > 1) {
|
||||
expanded[user.id] = true;
|
||||
}
|
||||
}
|
||||
setExpandedRows(expanded);
|
||||
}, [allVisibleSubscriptionIds, subscriptionSelection, expandedRows, users, getFilteredSubs]);
|
||||
|
||||
const table = useReactTable({
|
||||
data: filteredUsers,
|
||||
columns,
|
||||
|
||||
@@ -981,7 +981,10 @@ export default function AdminInfoPageEditor() {
|
||||
const initialContent = pageData.content[activeLocale] ?? pageData.content['ru'] ?? '';
|
||||
editor.commands.setContent(initialContent);
|
||||
editorPopulated.current = true;
|
||||
}, [pageData, editor]); // activeLocale intentionally omitted
|
||||
// activeLocale intentionally omitted — initial content is locked at editor
|
||||
// mount; subsequent locale changes are routed through switchLocale().
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pageData, editor]);
|
||||
|
||||
// Auto-generate slug from Russian title
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user