From b5a05626157a186cf7ce6086bda19a2065d19a90 Mon Sep 17 00:00:00 2001 From: Fringg Date: Sat, 7 Feb 2026 11:29:41 +0300 Subject: [PATCH] Revert "Merge pull request #185 from BEDOLAGA-DEV/feat/traffic-filters-devices" This reverts commit e824945b733e3321bb2a785da52580508f00b64e, reversing changes made to 81fcf54b1571970bf14175773bcdeb3aa706acfd. --- src/api/adminTraffic.ts | 20 +- src/locales/en.json | 13 +- src/locales/fa.json | 13 +- src/locales/ru.json | 13 +- src/locales/zh.json | 13 +- src/pages/AdminTrafficUsage.tsx | 577 +++++--------------------------- 6 files changed, 87 insertions(+), 562 deletions(-) diff --git a/src/api/adminTraffic.ts b/src/api/adminTraffic.ts index 0d39a35..e00cdeb 100644 --- a/src/api/adminTraffic.ts +++ b/src/api/adminTraffic.ts @@ -15,7 +15,6 @@ export interface UserTrafficItem { subscription_status: string | null; traffic_limit_gb: number; device_limit: number; - connected_devices: number; node_traffic: Record; total_bytes: number; } @@ -28,7 +27,6 @@ export interface TrafficUsageResponse { limit: number; period_days: number; available_tariffs: string[]; - available_statuses: string[]; } export interface ExportCsvResponse { @@ -44,10 +42,6 @@ export type TrafficParams = { sort_by?: string; sort_desc?: boolean; tariffs?: string; - nodes?: string; - statuses?: string; - start_date?: string; - end_date?: string; }; const CACHE_TTL = 5 * 60 * 1000; // 5 minutes @@ -63,10 +57,6 @@ function buildCacheKey(params: TrafficParams): string { sb: params.sort_by ?? 'total_bytes', sd: params.sort_desc ?? true, t: params.tariffs ?? '', - n: params.nodes ?? '', - st: params.statuses ?? '', - sd2: params.start_date ?? '', - ed: params.end_date ?? '', }); } @@ -105,15 +95,7 @@ export const adminTrafficApi = { trafficCache.clear(); }, - exportCsv: async (data: { - period: number; - start_date?: string; - end_date?: string; - tariffs?: string; - nodes?: string; - statuses?: string; - search?: string; - }): Promise => { + exportCsv: async (data: { period: number }): Promise => { const response = await apiClient.post('/cabinet/admin/traffic/export-csv', data); return response.data; }, diff --git a/src/locales/en.json b/src/locales/en.json index c5d3152..de83ae0 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -804,18 +804,7 @@ "loading": "Loading traffic data...", "noTariff": "\u2014", "search": "Search by name or username", - "allTariffs": "All tariffs", - "nodes": "Nodes", - "allNodes": "All nodes", - "status": "Status", - "allStatuses": "All", - "statusActive": "Active", - "statusTrial": "Trial", - "statusExpired": "Expired", - "statusDisabled": "Disabled", - "customDates": "Dates", - "dateFrom": "From", - "dateTo": "To" + "allTariffs": "All tariffs" }, "emailTemplates": { "title": "Email Templates", diff --git a/src/locales/fa.json b/src/locales/fa.json index aace454..2bf6548 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -685,18 +685,7 @@ "loading": "در حال بارگذاری داده‌های ترافیک...", "noTariff": "\u2014", "search": "جستجو بر اساس نام یا نام کاربری", - "allTariffs": "همه تعرفه‌ها", - "nodes": "نودها", - "allNodes": "همه نودها", - "status": "وضعیت", - "allStatuses": "همه", - "statusActive": "فعال", - "statusTrial": "آزمایشی", - "statusExpired": "منقضی", - "statusDisabled": "غیرفعال", - "customDates": "تاریخ", - "dateFrom": "از", - "dateTo": "تا" + "allTariffs": "همه تعرفه‌ها" }, "emailTemplates": { "title": "قالب‌های ایمیل", diff --git a/src/locales/ru.json b/src/locales/ru.json index 7b04e20..0ae6c1a 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -825,18 +825,7 @@ "loading": "Загрузка данных о трафике...", "noTariff": "\u2014", "search": "Поиск по имени или юзернейму", - "allTariffs": "Все тарифы", - "nodes": "Ноды", - "allNodes": "Все ноды", - "status": "Статус", - "allStatuses": "Все", - "statusActive": "Активная", - "statusTrial": "Пробная", - "statusExpired": "Истекла", - "statusDisabled": "Отключена", - "customDates": "Даты", - "dateFrom": "От", - "dateTo": "До" + "allTariffs": "Все тарифы" }, "emailTemplates": { "title": "Email-шаблоны", diff --git a/src/locales/zh.json b/src/locales/zh.json index e2bc9e5..551abbd 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -685,18 +685,7 @@ "loading": "正在加载流量数据...", "noTariff": "\u2014", "search": "按名称或用户名搜索", - "allTariffs": "所有套餐", - "nodes": "节点", - "allNodes": "所有节点", - "status": "状态", - "allStatuses": "全部", - "statusActive": "活跃", - "statusTrial": "试用", - "statusExpired": "已过期", - "statusDisabled": "已禁用", - "customDates": "日期", - "dateFrom": "从", - "dateTo": "到" + "allTariffs": "所有套餐" }, "paymentMethods": { "title": "支付方法", diff --git a/src/pages/AdminTrafficUsage.tsx b/src/pages/AdminTrafficUsage.tsx index 27e3f03..4d1179a 100644 --- a/src/pages/AdminTrafficUsage.tsx +++ b/src/pages/AdminTrafficUsage.tsx @@ -52,13 +52,6 @@ const toBackendSortField = (columnId: string): string => { return columnId; }; -const formatDateForInput = (date: Date): string => { - const y = date.getFullYear(); - const m = String(date.getMonth() + 1).padStart(2, '0'); - const d = String(date.getDate()).padStart(2, '0'); - return `${y}-${m}-${d}`; -}; - // ============ Icons ============ const SearchIcon = () => ( @@ -141,32 +134,6 @@ const ChevronDownIcon = () => ( ); -const CalendarIcon = () => ( - - - -); - -const ServerIcon = () => ( - - - -); - -const XIcon = () => ( - - - -); - // ============ Progress Bar ============ function ProgressBar({ loading }: { loading: boolean }) { @@ -214,47 +181,6 @@ function ProgressBar({ loading }: { loading: boolean }) { ); } -// ============ Checkbox helper ============ - -function Checkbox({ checked }: { checked: boolean }) { - return ( - - {checked && ( - - - - )} - - ); -} - -// ============ Status dot helper ============ - -const STATUS_COLORS: Record = { - active: 'bg-green-500', - trial: 'bg-yellow-500', - expired: 'bg-red-500', - disabled: 'bg-gray-500', -}; - -function StatusDot({ status }: { status: string }) { - return ( - - ); -} - // ============ Components ============ const PERIODS = [1, 3, 7, 14, 30] as const; @@ -263,96 +189,31 @@ function PeriodSelector({ value, onChange, label, - dateMode, - onToggleDateMode, }: { value: number; onChange: (v: number) => void; label: string; - dateMode: boolean; - onToggleDateMode: () => void; }) { const { t } = useTranslation(); return (
{label} - {!dateMode && ( -
- {PERIODS.map((p) => ( - - ))} -
- )} - -
- ); -} - -function DateRangePicker({ - startDate, - endDate, - onStartChange, - onEndChange, - onClose, -}: { - startDate: string; - endDate: string; - onStartChange: (v: string) => void; - onEndChange: (v: string) => void; - onClose: () => void; -}) { - const { t } = useTranslation(); - const today = formatDateForInput(new Date()); - const minDate = formatDateForInput(new Date(Date.now() - 31 * 24 * 60 * 60 * 1000)); - - return ( -
- - - onStartChange(e.target.value)} - min={minDate} - max={endDate || today} - className="rounded-lg border border-dark-700 bg-dark-800 px-2 py-1 text-xs text-dark-200 focus:border-dark-600 focus:outline-none" - /> - - onEndChange(e.target.value)} - min={startDate || minDate} - max={today} - className="rounded-lg border border-dark-700 bg-dark-800 px-2 py-1 text-xs text-dark-200 focus:border-dark-600 focus:outline-none" - /> - +
+ {PERIODS.map((p) => ( + + ))} +
); } @@ -423,211 +284,62 @@ function TariffFilter({ allSelected ? 'text-accent-400' : 'text-dark-300' }`} > - + + {allSelected && ( + + + + )} + {t('admin.trafficUsage.allTariffs')}
- {available.map((tariff) => ( - - ))} -
-
- )} - - ); -} - -function NodeFilter({ - available, - selected, - onChange, -}: { - available: TrafficNodeInfo[]; - selected: Set; - onChange: (next: Set) => void; -}) { - const { t } = useTranslation(); - const [open, setOpen] = useState(false); - const ref = useRef(null); - - useEffect(() => { - const handler = (e: MouseEvent) => { - if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); - }; - document.addEventListener('mousedown', handler); - return () => document.removeEventListener('mousedown', handler); - }, []); - - if (available.length === 0) return null; - - const allSelected = selected.size === 0; - const activeCount = selected.size; - - const toggle = (nodeUuid: string) => { - const next = new Set(selected); - if (next.has(nodeUuid)) { - next.delete(nodeUuid); - } else { - next.add(nodeUuid); - } - onChange(next); - }; - - const selectAll = () => onChange(new Set()); - - return ( -
- - - {open && ( -
- - -
- -
- {available.map((node) => ( - - ))} -
-
- )} -
- ); -} - -function StatusFilter({ - available, - selected, - onChange, -}: { - available: string[]; - selected: Set; - onChange: (next: Set) => void; -}) { - const { t } = useTranslation(); - const [open, setOpen] = useState(false); - const ref = useRef(null); - - useEffect(() => { - const handler = (e: MouseEvent) => { - if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); - }; - document.addEventListener('mousedown', handler); - return () => document.removeEventListener('mousedown', handler); - }, []); - - if (available.length === 0) return null; - - const allSelected = selected.size === 0; - const activeCount = selected.size; - - const statusLabels: Record = { - active: t('admin.trafficUsage.statusActive'), - trial: t('admin.trafficUsage.statusTrial'), - expired: t('admin.trafficUsage.statusExpired'), - disabled: t('admin.trafficUsage.statusDisabled'), - }; - - const toggle = (status: string) => { - const next = new Set(selected); - if (next.has(status)) { - next.delete(status); - } else { - next.add(status); - } - onChange(next); - }; - - const selectAll = () => onChange(new Set()); - - return ( -
- - - {open && ( -
- - -
- -
- {available.map((status) => ( - - ))} + {available.map((tariff) => { + const checked = selected.has(tariff); + return ( + + ); + })}
)} @@ -644,20 +356,13 @@ export default function AdminTrafficUsage() { const [items, setItems] = useState([]); const [nodes, setNodes] = useState([]); - const [allNodes, setAllNodes] = useState([]); const [availableTariffs, setAvailableTariffs] = useState([]); - const [availableStatuses, setAvailableStatuses] = useState([]); const [loading, setLoading] = useState(false); const [initialLoading, setInitialLoading] = useState(true); const [period, setPeriod] = useState(30); const [searchInput, setSearchInput] = useState(''); const [committedSearch, setCommittedSearch] = useState(''); const [selectedTariffs, setSelectedTariffs] = useState>(new Set()); - const [selectedNodes, setSelectedNodes] = useState>(new Set()); - const [selectedStatuses, setSelectedStatuses] = useState>(new Set()); - const [dateMode, setDateMode] = useState(false); - const [customStart, setCustomStart] = useState(''); - const [customEnd, setCustomEnd] = useState(''); const [offset, setOffset] = useState(0); const [total, setTotal] = useState(0); const [exporting, setExporting] = useState(false); @@ -671,36 +376,18 @@ export default function AdminTrafficUsage() { const sortBy = sorting[0] ? toBackendSortField(sorting[0].id) : 'total_bytes'; const sortDesc = sorting[0]?.desc ?? true; const tariffsParam = selectedTariffs.size > 0 ? [...selectedTariffs].join(',') : undefined; - const nodesParam = selectedNodes.size > 0 ? [...selectedNodes].join(',') : undefined; - const statusesParam = selectedStatuses.size > 0 ? [...selectedStatuses].join(',') : undefined; const buildParams = useCallback( (): TrafficParams => ({ - period: dateMode ? undefined : period, + period, limit, offset, search: committedSearch || undefined, sort_by: sortBy, sort_desc: sortDesc, tariffs: tariffsParam, - nodes: nodesParam, - statuses: statusesParam, - start_date: dateMode && customStart ? customStart : undefined, - end_date: dateMode && customEnd ? customEnd : undefined, }), - [ - period, - offset, - committedSearch, - sortBy, - sortDesc, - tariffsParam, - nodesParam, - statusesParam, - dateMode, - customStart, - customEnd, - ], + [period, offset, committedSearch, sortBy, sortDesc, tariffsParam], ); const applyData = useCallback( @@ -709,13 +396,11 @@ export default function AdminTrafficUsage() { nodes: TrafficNodeInfo[]; total: number; available_tariffs: string[]; - available_statuses: string[]; }) => { setItems(data.items); setNodes(data.nodes); setTotal(data.total); setAvailableTariffs(data.available_tariffs); - setAvailableStatuses(data.available_statuses); }, [], ); @@ -724,9 +409,6 @@ export default function AdminTrafficUsage() { async (skipCache = false) => { const params = buildParams(); - // In date mode, require both dates - if (dateMode && (!customStart || !customEnd)) return; - // Check cache first — apply instantly without any loading state if (!skipCache) { const cached = adminTrafficApi.getCached(params); @@ -748,7 +430,7 @@ export default function AdminTrafficUsage() { setInitialLoading(false); } }, - [buildParams, applyData, dateMode, customStart, customEnd], + [buildParams, applyData], ); // Load on param change @@ -756,36 +438,8 @@ export default function AdminTrafficUsage() { loadData(); }, [loadData]); - // Track all available nodes (before node filtering) — fetch once on mount with default params + // Prefetch adjacent periods in background useEffect(() => { - const fetchAllNodes = async () => { - const params: TrafficParams = { - period: 30, - limit: 1, - offset: 0, - sort_by: 'total_bytes', - sort_desc: true, - }; - try { - const data = await adminTrafficApi.getTrafficUsage(params); - setAllNodes(data.nodes); - } catch { - // ignore - } - }; - fetchAllNodes(); - }, []); - - // Update allNodes when we get unfiltered response - useEffect(() => { - if (selectedNodes.size === 0 && nodes.length > 0) { - setAllNodes(nodes); - } - }, [nodes, selectedNodes.size]); - - // Prefetch adjacent periods in background (only in period mode) - useEffect(() => { - if (dateMode) return; const prefetchPeriods = PERIODS.filter((p) => p !== period); const timer = setTimeout(() => { prefetchPeriods.forEach((p) => { @@ -802,7 +456,8 @@ export default function AdminTrafficUsage() { }); }, 500); return () => clearTimeout(timer); - }, [period, dateMode]); + // Only prefetch once on mount + when period changes + }, [period]); useEffect(() => { if (toast) { @@ -820,15 +475,7 @@ export default function AdminTrafficUsage() { const handleExport = async () => { try { setExporting(true); - await adminTrafficApi.exportCsv({ - period, - start_date: dateMode && customStart ? customStart : undefined, - end_date: dateMode && customEnd ? customEnd : undefined, - tariffs: tariffsParam, - nodes: nodesParam, - statuses: statusesParam, - search: committedSearch || undefined, - }); + await adminTrafficApi.exportCsv({ period }); setToast({ message: t('admin.trafficUsage.exportSuccess'), type: 'success' }); } catch { setToast({ message: t('admin.trafficUsage.exportError'), type: 'error' }); @@ -853,41 +500,10 @@ export default function AdminTrafficUsage() { setOffset(0); }; - const handleNodeChange = (next: Set) => { - setSelectedNodes(next); - setOffset(0); - }; - - const handleStatusChange = (next: Set) => { - setSelectedStatuses(next); - setOffset(0); - }; - const handleRefresh = () => { loadData(true); }; - const handleToggleDateMode = () => { - if (!dateMode) { - const today = new Date(); - const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); - setCustomStart(formatDateForInput(weekAgo)); - setCustomEnd(formatDateForInput(today)); - } - setDateMode(!dateMode); - setOffset(0); - }; - - const handleCustomStartChange = (v: string) => { - setCustomStart(v); - setOffset(0); - }; - - const handleCustomEndChange = (v: string) => { - setCustomEnd(v); - setOffset(0); - }; - const columns = useMemo[]>(() => { const cols: ColumnDef[] = [ { @@ -928,26 +544,15 @@ export default function AdminTrafficUsage() { ), }, { - id: 'device_limit', - accessorFn: (row) => row.device_limit, + accessorKey: 'device_limit', header: t('admin.trafficUsage.devices'), enableSorting: true, size: 80, minSize: 60, meta: { align: 'center' as const }, - cell: ({ row }) => { - const item = row.original; - const connected = item.connected_devices; - const limit = item.device_limit; - return ( - - 0 ? 'text-accent-400' : 'text-dark-500'}> - {connected} - - /{limit} - - ); - }, + cell: ({ getValue }) => ( + {getValue() as number} + ), }, { accessorKey: 'traffic_limit_gb', @@ -1062,34 +667,16 @@ export default function AdminTrafficUsage() { {/* Controls */}
- {dateMode ? ( - - ) : ( - - )} + - -