mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
fix: multi-tariff filter now works server-side + trial-only checkbox
- Send comma-separated tariff_ids to backend (was only single ID) — server now filters with IN() operator, pagination correct - Remove client-side tariff filtering (server handles it) - Add "Только триал" checkbox filter: amber checkbox, filters users with trial subscriptions client-side - Remove "trial" from status dropdown (was not working via subscription_status server filter) - i18n: trialOnly key (ru + en)
This commit is contained in:
@@ -2086,6 +2086,7 @@
|
|||||||
"allStatuses": "All statuses",
|
"allStatuses": "All statuses",
|
||||||
"allTariffs": "All tariffs",
|
"allTariffs": "All tariffs",
|
||||||
"allGroups": "All groups",
|
"allGroups": "All groups",
|
||||||
|
"trialOnly": "Trial only",
|
||||||
"tariffsSelected": "{{count}} tariffs",
|
"tariffsSelected": "{{count}} tariffs",
|
||||||
"selectAll": "Select all",
|
"selectAll": "Select all",
|
||||||
"deselectAll": "Deselect all"
|
"deselectAll": "Deselect all"
|
||||||
|
|||||||
@@ -3834,6 +3834,7 @@
|
|||||||
"allStatuses": "Все статусы",
|
"allStatuses": "Все статусы",
|
||||||
"allTariffs": "Все тарифы",
|
"allTariffs": "Все тарифы",
|
||||||
"allGroups": "Все группы",
|
"allGroups": "Все группы",
|
||||||
|
"trialOnly": "Только триал",
|
||||||
"tariffsSelected": "{{count}} тарифов",
|
"tariffsSelected": "{{count}} тарифов",
|
||||||
"selectAll": "Выбрать все",
|
"selectAll": "Выбрать все",
|
||||||
"deselectAll": "Снять все"
|
"deselectAll": "Снять все"
|
||||||
|
|||||||
@@ -1403,6 +1403,7 @@ export default function AdminBulkActions() {
|
|||||||
const [searchInput, setSearchInput] = useState('');
|
const [searchInput, setSearchInput] = useState('');
|
||||||
const [committedSearch, setCommittedSearch] = useState('');
|
const [committedSearch, setCommittedSearch] = useState('');
|
||||||
const [statusFilter, setStatusFilter] = useState<SubscriptionStatusFilter>('');
|
const [statusFilter, setStatusFilter] = useState<SubscriptionStatusFilter>('');
|
||||||
|
const [trialOnly, setTrialOnly] = useState(false);
|
||||||
const [tariffFilter, setTariffFilter] = useState<number[]>([]);
|
const [tariffFilter, setTariffFilter] = useState<number[]>([]);
|
||||||
const [promoGroupFilter, setPromoGroupFilter] = useState('');
|
const [promoGroupFilter, setPromoGroupFilter] = useState('');
|
||||||
|
|
||||||
@@ -1444,7 +1445,7 @@ export default function AdminBulkActions() {
|
|||||||
};
|
};
|
||||||
if (committedSearch) params.search = committedSearch;
|
if (committedSearch) params.search = committedSearch;
|
||||||
if (statusFilter) params.subscription_status = statusFilter;
|
if (statusFilter) params.subscription_status = statusFilter;
|
||||||
if (tariffFilter.length === 1) params.tariff_id = tariffFilter[0];
|
if (tariffFilter.length > 0) params.tariff_id = tariffFilter.join(',');
|
||||||
if (promoGroupFilter) params.promo_group_id = Number(promoGroupFilter);
|
if (promoGroupFilter) params.promo_group_id = Number(promoGroupFilter);
|
||||||
|
|
||||||
const data = await adminUsersApi.getUsers(
|
const data = await adminUsersApi.getUsers(
|
||||||
@@ -1980,12 +1981,17 @@ export default function AdminBulkActions() {
|
|||||||
// When multiple tariffs are selected, filter users client-side
|
// When multiple tariffs are selected, filter users client-side
|
||||||
// (server only supports single tariff_id filter)
|
// (server only supports single tariff_id filter)
|
||||||
const filteredUsers = useMemo(() => {
|
const filteredUsers = useMemo(() => {
|
||||||
if (tariffFilter.length <= 1) return users;
|
let result = users;
|
||||||
return users.filter((u) => {
|
|
||||||
const subs = u.subscriptions ?? [];
|
// Trial-only filter: show users with trial subscription
|
||||||
return subs.some((s) => s.tariff_id !== null && tariffFilter.includes(s.tariff_id));
|
if (trialOnly) {
|
||||||
});
|
result = result.filter(
|
||||||
}, [users, tariffFilter]);
|
(u) => u.subscription_is_trial || (u.subscriptions ?? []).some((s) => s.status === 'trial'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [users, trialOnly]);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: filteredUsers,
|
data: filteredUsers,
|
||||||
@@ -2005,7 +2011,6 @@ export default function AdminBulkActions() {
|
|||||||
{ value: '', label: t('admin.bulkActions.filters.allStatuses') },
|
{ value: '', label: t('admin.bulkActions.filters.allStatuses') },
|
||||||
{ value: 'active', label: t('admin.bulkActions.statuses.active') },
|
{ value: 'active', label: t('admin.bulkActions.statuses.active') },
|
||||||
{ value: 'expired', label: t('admin.bulkActions.statuses.expired') },
|
{ value: 'expired', label: t('admin.bulkActions.statuses.expired') },
|
||||||
{ value: 'trial', label: t('admin.bulkActions.statuses.trial') },
|
|
||||||
{ value: 'limited', label: t('admin.bulkActions.statuses.limited') },
|
{ value: 'limited', label: t('admin.bulkActions.statuses.limited') },
|
||||||
{ value: 'disabled', label: t('admin.bulkActions.statuses.disabled') },
|
{ value: 'disabled', label: t('admin.bulkActions.statuses.disabled') },
|
||||||
];
|
];
|
||||||
@@ -2074,7 +2079,7 @@ export default function AdminBulkActions() {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{/* Filter dropdowns */}
|
{/* Filter dropdowns + trial toggle */}
|
||||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
||||||
<DropdownSelect
|
<DropdownSelect
|
||||||
value={statusFilter}
|
value={statusFilter}
|
||||||
@@ -2093,6 +2098,31 @@ export default function AdminBulkActions() {
|
|||||||
onChange={handlePromoGroupFilterChange}
|
onChange={handlePromoGroupFilterChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Trial filter checkbox */}
|
||||||
|
<label className="inline-flex cursor-pointer items-center gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setTrialOnly((prev) => !prev);
|
||||||
|
setOffset(0);
|
||||||
|
clearAllSelections();
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
'flex h-5 w-5 items-center justify-center rounded-md border-2 transition-all duration-150',
|
||||||
|
trialOnly
|
||||||
|
? 'border-amber-500 bg-amber-500 shadow-[0_0_8px_rgba(245,158,11,0.4)]'
|
||||||
|
: 'border-dark-500 bg-dark-700/60 hover:border-amber-500/50 hover:bg-dark-600/60',
|
||||||
|
)}
|
||||||
|
aria-pressed={trialOnly}
|
||||||
|
>
|
||||||
|
{trialOnly && <CheckIcon />}
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
className={cn('text-sm', trialOnly ? 'font-medium text-amber-400' : 'text-dark-400')}
|
||||||
|
>
|
||||||
|
{t('admin.bulkActions.filters.trialOnly')}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
|
|||||||
Reference in New Issue
Block a user