diff --git a/src/api/adminBulkActions.ts b/src/api/adminBulkActions.ts index f74e8af..69315ef 100644 --- a/src/api/adminBulkActions.ts +++ b/src/api/adminBulkActions.ts @@ -10,7 +10,8 @@ export type BulkActionType = | 'add_traffic' | 'add_balance' | 'assign_promo_group' - | 'grant_subscription'; + | 'grant_subscription' + | 'set_devices'; export interface BulkActionRequest { action: BulkActionType; @@ -27,6 +28,7 @@ export interface BulkActionParams { amount_kopeks?: number; balance_description?: string; promo_group_id?: number | null; + device_limit?: number; } export interface BulkActionErrorItem { diff --git a/src/api/adminUsers.ts b/src/api/adminUsers.ts index fc223ad..96737d8 100644 --- a/src/api/adminUsers.ts +++ b/src/api/adminUsers.ts @@ -42,6 +42,7 @@ export interface UserListItemSubscription { days_remaining: number; traffic_used_gb: number; traffic_limit_gb: number; + device_limit: number; } export interface UserListItem { diff --git a/src/locales/en.json b/src/locales/en.json index 94f67b4..aaef068 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -2039,7 +2039,8 @@ "addTraffic": "Add traffic", "addBalance": "Add balance", "assignPromoGroup": "Assign promo group", - "grantSubscription": "Grant subscription" + "grantSubscription": "Grant subscription", + "setDevices": "Set devices" }, "params": { "days": "Number of days", @@ -2047,7 +2048,8 @@ "trafficGb": "Traffic amount (GB)", "balanceRub": "Amount (RUB)", "promoGroup": "Promo group", - "removePromoGroup": "Remove promo group" + "removePromoGroup": "Remove promo group", + "deviceLimit": "Device count" }, "grantSubscription": { "warning": "Users who already have a subscription on the selected tariff will be skipped" diff --git a/src/locales/ru.json b/src/locales/ru.json index 791275f..100f22b 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -3787,7 +3787,8 @@ "addTraffic": "Начислить трафик", "addBalance": "Начислить баланс", "assignPromoGroup": "Назначить промогруппу", - "grantSubscription": "Выдать подписку" + "grantSubscription": "Выдать подписку", + "setDevices": "Изменить устройства" }, "params": { "days": "Количество дней", @@ -3795,7 +3796,8 @@ "trafficGb": "Объём трафика (ГБ)", "balanceRub": "Сумма (руб.)", "promoGroup": "Промогруппа", - "removePromoGroup": "Убрать промогруппу" + "removePromoGroup": "Убрать промогруппу", + "deviceLimit": "Кол-во устройств" }, "grantSubscription": { "warning": "Пользователи, у которых уже есть подписка на выбранный тариф, будут пропущены" diff --git a/src/pages/AdminBulkActions.tsx b/src/pages/AdminBulkActions.tsx index 9efbd3f..add861b 100644 --- a/src/pages/AdminBulkActions.tsx +++ b/src/pages/AdminBulkActions.tsx @@ -140,6 +140,7 @@ const SUBSCRIPTION_LEVEL_ACTIONS: Set = new Set([ 'activate_subscription', 'change_tariff', 'add_traffic', + 'set_devices', ]); function isSubscriptionLevelAction(action: BulkActionType): boolean { @@ -264,6 +265,24 @@ function SubscriptionSubRow({ /> + + {/* Devices */} + + + + + {subscription.device_limit} + @@ -738,6 +757,7 @@ function ActionModal({ const [promoGroupId, setPromoGroupId] = useState(promoGroups[0]?.id ?? null); const [grantTariffId, setGrantTariffId] = useState(tariffs[0]?.id ?? 0); const [grantDays, setGrantDays] = useState(30); + const [deviceLimit, setDeviceLimit] = useState(1); useEffect(() => { if (tariffs.length > 0 && tariffId === 0) { @@ -778,6 +798,7 @@ function ActionModal({ add_balance: 'admin.bulkActions.actions.addBalance', assign_promo_group: 'admin.bulkActions.actions.assignPromoGroup', grant_subscription: 'admin.bulkActions.actions.grantSubscription', + set_devices: 'admin.bulkActions.actions.setDevices', }; const handleSubmit = () => { @@ -802,6 +823,9 @@ function ActionModal({ params.tariff_id = grantTariffId; params.days = grantDays; break; + case 'set_devices': + params.device_limit = deviceLimit; + break; } onExecute(params); }; @@ -891,6 +915,22 @@ function ActionModal({ /> ); + case 'set_devices': + return ( +
+ + setDeviceLimit(Number(e.target.value))} + className="w-full rounded-xl border border-dark-700 bg-dark-800 px-3 py-2.5 text-sm text-dark-100 outline-none transition-colors focus:border-accent-500/40" + /> +
+ ); case 'grant_subscription': return (
@@ -1129,6 +1169,26 @@ function FloatingActionBar({ icon: , colorClass: 'text-accent-400 hover:bg-accent-500/10', }, + { + type: 'set_devices', + labelKey: 'admin.bulkActions.actions.setDevices', + icon: ( + + + + ), + colorClass: 'text-accent-400 hover:bg-accent-500/10', + }, { type: 'add_balance', labelKey: 'admin.bulkActions.actions.addBalance', @@ -1780,13 +1840,34 @@ export default function AdminBulkActions() { id: 'tariff', accessorFn: (row) => row.tariff_name, header: t('admin.bulkActions.columns.tariff'), - size: 120, + size: 160, cell: ({ row }) => { const user = row.original; - if (!user.tariff_name) { - return ; + const subs = user.subscriptions ?? []; + const tariffNames = subs + .map((s) => s.tariff_name) + .filter((name): name is string => !!name); + const uniqueNames = [...new Set(tariffNames)]; + + if (uniqueNames.length === 0) { + if (!user.tariff_name) { + return ; + } + return {user.tariff_name}; } - return {user.tariff_name}; + + return ( +
+ {uniqueNames.map((name) => ( + + {name} + + ))} +
+ ); }, }, { @@ -1800,33 +1881,6 @@ export default function AdminBulkActions() { ), }, - { - id: 'days_remaining', - header: t('admin.bulkActions.columns.daysRemaining'), - size: 80, - cell: ({ row }) => { - const user = row.original; - if (!user.subscription_end_date) { - return -; - } - const end = new Date(user.subscription_end_date); - const now = new Date(); - const days = Math.max( - 0, - Math.ceil((end.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)), - ); - return ( - - {days} - - ); - }, - }, { id: 'promo_group', accessorFn: (row) => row.promo_group_name,