= {
- extend_subscription: 'admin.bulkActions.actions.extend',
- add_days: 'admin.bulkActions.actions.extend',
- cancel_subscription: 'admin.bulkActions.actions.cancel',
- activate_subscription: 'admin.bulkActions.actions.activate',
- change_tariff: 'admin.bulkActions.actions.changeTariff',
- add_traffic: 'admin.bulkActions.actions.addTraffic',
- 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',
- delete_subscription: 'admin.bulkActions.actions.deleteSubscription',
- delete_user: 'admin.bulkActions.actions.deleteUser',
- };
-
- const handleSubmit = () => {
- const params: BulkActionParams = {};
- switch (modal.action) {
- case 'extend_subscription':
- params.days = days;
- break;
- case 'change_tariff':
- params.tariff_id = tariffId;
- break;
- case 'add_traffic':
- params.traffic_gb = trafficGb;
- break;
- case 'add_balance':
- params.amount_kopeks = Math.round(balanceRub * 100);
- break;
- case 'assign_promo_group':
- params.promo_group_id = promoGroupId;
- break;
- case 'grant_subscription':
- params.tariff_id = grantTariffId;
- params.days = grantDays;
- break;
- case 'set_devices':
- params.device_limit = deviceLimit;
- break;
- case 'delete_subscription':
- params.force_delete_active_paid = forceDeleteActivePaid;
- break;
- case 'delete_user':
- params.delete_from_panel = deleteFromPanel;
- break;
- }
- onExecute(params);
- };
-
- const handleBackdropClick = () => {
- if (!modal.loading) {
- onClose();
- }
- };
-
- const renderInputs = () => {
- switch (modal.action) {
- case 'extend_subscription':
- return (
-
-
- setDays(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 'change_tariff':
- return (
-
-
- ({ value: String(tt.id), label: tt.name }))}
- onChange={(v) => setTariffId(Number(v))}
- />
-
- );
- case 'add_traffic':
- return (
-
-
- setTrafficGb(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 'add_balance':
- return (
-
-
- setBalanceRub(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 'assign_promo_group':
- return (
-
-
- ({ value: String(pg.id), label: pg.name })),
- ]}
- onChange={(v) => setPromoGroupId(v === 'null' ? null : Number(v))}
- />
-
- );
- 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 (
-
-
-
- ({ value: String(tt.id), label: tt.name }))}
- onChange={(v) => setGrantTariffId(Number(v))}
- />
-
-
-
- setGrantDays(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"
- />
-
- {/* Warning about users with existing subscriptions */}
-
-
- {t('admin.bulkActions.grantSubscription.warning')}
-
-
-
- );
- case 'delete_subscription': {
- const totalSelected = selectedSubscriptionIds.length;
-
- return (
-
-
-
- {t('admin.bulkActions.deleteSubscription.warning')}
-
-
- {t('admin.bulkActions.deleteSubscription.hint')}
-
-
- {activePaidCount > 0 && (
- <>
-
-
- {t('admin.bulkActions.deleteSubscription.activePaidWarning', {
- count: activePaidCount,
- total: totalSelected,
- })}
-
-
-
- >
- )}
-
- );
- }
- case 'delete_user':
- return (
-
-
-
- {t('admin.bulkActions.deleteUser.warning')}
-
-
- {t('admin.bulkActions.deleteUser.hint')}
-
-
-
-
- );
- default:
- return null;
- }
- };
-
- return createPortal(
-
- {/* Backdrop — no close on click during loading */}
-
-
- {/* Modal — role/aria-modal live on the focus-trapped element so AT
- announces and Tab cycles correctly. */}
-
- {/* Header */}
-
-
- {t(actionLabelKeys[modal.action])}
-
- {!modal.loading && (
-
- )}
-
-
- {/* Progress view during execution */}
- {modal.loading && modal.progress ? (
-
-
-
- {t('admin.bulkActions.progress.doNotClose')}
-
-
- ) : modal.result ? (
- /* Result view */
-
- {/* Summary line */}
-
-
- {t('admin.bulkActions.complete')}
-
- {/* Colored summary */}
-
-
- {t('admin.bulkActions.progress.summarySuccess', {
- count: modal.result.success_count,
- })}
-
- {modal.result.error_count > 0 && (
- <>
-
- /
-
-
- {t('admin.bulkActions.progress.summaryErrors', {
- count: modal.result.error_count,
- })}
-
- >
- )}
-
-
-
-
- {modal.result.success_count}
-
-
- {t('admin.bulkActions.successCount', { count: modal.result.success_count })}
-
-
- {modal.result.error_count > 0 && (
-
-
- {modal.result.error_count}
-
-
- {t('admin.bulkActions.errorCount', { count: modal.result.error_count })}
-
-
- )}
-
-
-
- {/* Collapsible error details */}
-
-
-
-
- ) : (
- <>
- {/* Affected count */}
-
-
- {t('admin.bulkActions.selectedCount', { count: selectedCount })}
-
-
-
- {/* Action-specific inputs */}
-
{renderInputs()}
-
- {/* Buttons */}
-
-
-
-
- >
- )}
-
-
,
- document.body,
- );
-}
+// (ActionModal moved into ./bulkActions/ActionModal.tsx)
// ============ Floating Action Bar ============