/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { useState, useEffect } from 'react' import { useQueryClient } from '@tanstack/react-query' import { Loader2, RefreshCw, Trash2, Power, PowerOff } from 'lucide-react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Separator } from '@/components/ui/separator' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { ConfirmDialog } from '@/components/confirm-dialog' import { StatusBadge } from '@/components/status-badge' import { getMultiKeyStatus, enableMultiKey, disableMultiKey, deleteMultiKey, enableAllMultiKeys, disableAllMultiKeys, deleteDisabledMultiKeys, } from '../../api' import { MULTI_KEY_FILTER_OPTIONS } from '../../constants' import { channelsQueryKeys, formatTimestamp, getMultiKeyStatusConfig, getMultiKeyConfirmMessage, isDestructiveAction, } from '../../lib' import type { KeyStatus, MultiKeyConfirmAction } from '../../types' import { useChannels } from '../channels-provider' import { StatisticsCard } from './multi-key-statistics-card' import { MultiKeyTableRowActions } from './multi-key-table-row-actions' type MultiKeyManageDialogProps = { open: boolean onOpenChange: (open: boolean) => void } export function MultiKeyManageDialog({ open, onOpenChange, }: MultiKeyManageDialogProps) { const { t } = useTranslation() const { currentRow } = useChannels() const queryClient = useQueryClient() // Data state const [isLoading, setIsLoading] = useState(false) const [keys, setKeys] = useState([]) const [currentPage, setCurrentPage] = useState(1) const [pageSize, setPageSize] = useState(10) const [total, setTotal] = useState(0) const [totalPages, setTotalPages] = useState(0) const [enabledCount, setEnabledCount] = useState(0) const [manualDisabledCount, setManualDisabledCount] = useState(0) const [autoDisabledCount, setAutoDisabledCount] = useState(0) // UI state const [statusFilter, setStatusFilter] = useState(null) const [confirmAction, setConfirmAction] = useState(null) const [isPerformingAction, setIsPerformingAction] = useState(false) // Reset and load data when dialog opens useEffect(() => { if (open && currentRow) { setCurrentPage(1) setStatusFilter(null) loadKeyStatus(1, pageSize, null) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, currentRow?.id]) const loadKeyStatus = async ( page: number = currentPage, size: number = pageSize, status: number | null = statusFilter ) => { if (!currentRow) return setIsLoading(true) try { const response = await getMultiKeyStatus( currentRow.id, page, size, status === null ? undefined : status ) if (response.success && response.data) { setKeys(response.data.keys || []) setTotal(response.data.total || 0) setCurrentPage(response.data.page || 1) setPageSize(response.data.page_size || 10) setTotalPages(response.data.total_pages || 0) setEnabledCount(response.data.enabled_count || 0) setManualDisabledCount(response.data.manual_disabled_count || 0) setAutoDisabledCount(response.data.auto_disabled_count || 0) } } catch (error: unknown) { toast.error( error instanceof Error ? error.message : t('Failed to load key status') ) } finally { setIsLoading(false) } } const handleStatusFilterChange = (value: string) => { const newFilter = value === 'all' ? null : parseInt(value) setStatusFilter(newFilter) setCurrentPage(1) loadKeyStatus(1, pageSize, newFilter) } const handlePageChange = (newPage: number) => { setCurrentPage(newPage) loadKeyStatus(newPage, pageSize) } const performAction = async () => { if (!confirmAction || !currentRow) return setIsPerformingAction(true) try { const { type, keyIndex } = confirmAction let response // Execute the appropriate action if (type === 'enable' && keyIndex !== undefined) { response = await enableMultiKey(currentRow.id, keyIndex) } else if (type === 'disable' && keyIndex !== undefined) { response = await disableMultiKey(currentRow.id, keyIndex) } else if (type === 'delete' && keyIndex !== undefined) { response = await deleteMultiKey(currentRow.id, keyIndex) } else if (type === 'enable-all') { response = await enableAllMultiKeys(currentRow.id) } else if (type === 'disable-all') { response = await disableAllMultiKeys(currentRow.id) } else if (type === 'delete-disabled') { response = await deleteDisabledMultiKeys(currentRow.id) } if (response?.success) { toast.success(response.message || t('Operation successful')) queryClient.invalidateQueries({ queryKey: channelsQueryKeys.lists() }) // Reload data - reset to page 1 for bulk actions const isBulkAction = type.includes('all') || type === 'delete-disabled' if (isBulkAction) { setCurrentPage(1) loadKeyStatus(1, pageSize) } else { loadKeyStatus(currentPage, pageSize) } } else { toast.error(response?.message || t('Operation failed')) } } catch (error: unknown) { toast.error( error instanceof Error ? error.message : t('Operation failed') ) } finally { setIsPerformingAction(false) setConfirmAction(null) } } const renderStatusBadge = (status: number) => { const config = getMultiKeyStatusConfig(status) return ( ) } const formatKeyTimestamp = (timestamp?: number) => { if (!timestamp) return '-' return formatTimestamp(timestamp) } if (!currentRow) return null return ( <> {t('Multi-Key Management')} {currentRow.channel_info?.multi_key_mode && ( )} {t('Manage multi-key status and configuration for this channel')}
{/* Statistics */}
{/* Toolbar */}
{manualDisabledCount + autoDisabledCount > 0 && ( )} {enabledCount > 0 && ( )} {autoDisabledCount > 0 && ( )}
{/* Table */}
{isLoading ? (
) : keys.length === 0 ? (
{t('No keys found')}
) : (
{t('Index')} {t('Status')} {t('Disabled Reason')} {t('Disabled Time')} {t('Actions')} {keys.map((key) => ( #{key.index + 1} {renderStatusBadge(key.status)} {key.reason || '-'} {formatKeyTimestamp(key.disabled_time)} ))}
)}
{/* Pagination */} {totalPages > 1 && (
{t('Page {{current}} of {{total}}', { current: currentPage, total: totalPages, })}
)}
{/* Confirmation Dialog */} !open && setConfirmAction(null)} title={t('Confirm Action')} desc={t(getMultiKeyConfirmMessage(confirmAction))} destructive={isDestructiveAction(confirmAction)} isLoading={isPerformingAction} handleConfirm={performAction} /> ) }