mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
Refactor PeriodTariffModal to remove server traffic limits and add custom days and traffic options for flexible tariffs
This commit is contained in:
@@ -9,8 +9,7 @@ import {
|
|||||||
TariffCreateRequest,
|
TariffCreateRequest,
|
||||||
TariffUpdateRequest,
|
TariffUpdateRequest,
|
||||||
PeriodPrice,
|
PeriodPrice,
|
||||||
ServerInfo,
|
ServerInfo
|
||||||
ServerTrafficLimit
|
|
||||||
} from '../api/tariffs'
|
} from '../api/tariffs'
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
@@ -146,9 +145,6 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
tariff?.period_prices?.length ? tariff.period_prices : []
|
tariff?.period_prices?.length ? tariff.period_prices : []
|
||||||
)
|
)
|
||||||
const [selectedSquads, setSelectedSquads] = useState<string[]>(tariff?.allowed_squads || [])
|
const [selectedSquads, setSelectedSquads] = useState<string[]>(tariff?.allowed_squads || [])
|
||||||
const [serverTrafficLimits, setServerTrafficLimits] = useState<Record<string, ServerTrafficLimit>>(
|
|
||||||
tariff?.server_traffic_limits || {}
|
|
||||||
)
|
|
||||||
// Докупка трафика
|
// Докупка трафика
|
||||||
const [trafficTopupEnabled, setTrafficTopupEnabled] = useState(tariff?.traffic_topup_enabled || false)
|
const [trafficTopupEnabled, setTrafficTopupEnabled] = useState(tariff?.traffic_topup_enabled || false)
|
||||||
const [maxTopupTrafficGb, setMaxTopupTrafficGb] = useState(tariff?.max_topup_traffic_gb || 0)
|
const [maxTopupTrafficGb, setMaxTopupTrafficGb] = useState(tariff?.max_topup_traffic_gb || 0)
|
||||||
@@ -159,6 +155,18 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
// Режим сброса трафика
|
// Режим сброса трафика
|
||||||
const [trafficResetMode, setTrafficResetMode] = useState<string | null>(tariff?.traffic_reset_mode || null)
|
const [trafficResetMode, setTrafficResetMode] = useState<string | null>(tariff?.traffic_reset_mode || null)
|
||||||
|
|
||||||
|
// Плавающий тариф - произвольное количество дней
|
||||||
|
const [customDaysEnabled, setCustomDaysEnabled] = useState(tariff?.custom_days_enabled || false)
|
||||||
|
const [pricePerDayKopeks, setPricePerDayKopeks] = useState(tariff?.price_per_day_kopeks || 0)
|
||||||
|
const [minDays, setMinDays] = useState(tariff?.min_days || 1)
|
||||||
|
const [maxDays, setMaxDays] = useState(tariff?.max_days || 365)
|
||||||
|
|
||||||
|
// Плавающий тариф - произвольный трафик
|
||||||
|
const [customTrafficEnabled, setCustomTrafficEnabled] = useState(tariff?.custom_traffic_enabled || false)
|
||||||
|
const [trafficPricePerGbKopeks, setTrafficPricePerGbKopeks] = useState(tariff?.traffic_price_per_gb_kopeks || 0)
|
||||||
|
const [minTrafficGb, setMinTrafficGb] = useState(tariff?.min_traffic_gb || 1)
|
||||||
|
const [maxTrafficGb, setMaxTrafficGb] = useState(tariff?.max_traffic_gb || 1000)
|
||||||
|
|
||||||
// Новый период для добавления
|
// Новый период для добавления
|
||||||
const [newPeriodDays, setNewPeriodDays] = useState(30)
|
const [newPeriodDays, setNewPeriodDays] = useState(30)
|
||||||
const [newPeriodPrice, setNewPeriodPrice] = useState(300)
|
const [newPeriodPrice, setNewPeriodPrice] = useState(300)
|
||||||
@@ -166,13 +174,6 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
const [activeTab, setActiveTab] = useState<'basic' | 'periods' | 'servers' | 'extra'>('basic')
|
const [activeTab, setActiveTab] = useState<'basic' | 'periods' | 'servers' | 'extra'>('basic')
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
const filteredLimits: Record<string, ServerTrafficLimit> = {}
|
|
||||||
for (const uuid of selectedSquads) {
|
|
||||||
if (serverTrafficLimits[uuid] && serverTrafficLimits[uuid].traffic_limit_gb > 0) {
|
|
||||||
filteredLimits[uuid] = serverTrafficLimits[uuid]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: TariffCreateRequest | TariffUpdateRequest = {
|
const data: TariffCreateRequest | TariffUpdateRequest = {
|
||||||
name,
|
name,
|
||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
@@ -183,24 +184,25 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
tier_level: tierLevel,
|
tier_level: tierLevel,
|
||||||
period_prices: periodPrices.filter(p => p.price_kopeks > 0),
|
period_prices: periodPrices.filter(p => p.price_kopeks > 0),
|
||||||
allowed_squads: selectedSquads,
|
allowed_squads: selectedSquads,
|
||||||
server_traffic_limits: Object.keys(filteredLimits).length > 0 ? filteredLimits : {},
|
|
||||||
traffic_topup_enabled: trafficTopupEnabled,
|
traffic_topup_enabled: trafficTopupEnabled,
|
||||||
traffic_topup_packages: trafficTopupPackages,
|
traffic_topup_packages: trafficTopupPackages,
|
||||||
max_topup_traffic_gb: maxTopupTrafficGb,
|
max_topup_traffic_gb: maxTopupTrafficGb,
|
||||||
is_daily: false,
|
is_daily: false,
|
||||||
daily_price_kopeks: 0,
|
daily_price_kopeks: 0,
|
||||||
traffic_reset_mode: trafficResetMode,
|
traffic_reset_mode: trafficResetMode,
|
||||||
|
// Плавающий тариф
|
||||||
|
custom_days_enabled: customDaysEnabled,
|
||||||
|
price_per_day_kopeks: pricePerDayKopeks,
|
||||||
|
min_days: minDays,
|
||||||
|
max_days: maxDays,
|
||||||
|
custom_traffic_enabled: customTrafficEnabled,
|
||||||
|
traffic_price_per_gb_kopeks: trafficPricePerGbKopeks,
|
||||||
|
min_traffic_gb: minTrafficGb,
|
||||||
|
max_traffic_gb: maxTrafficGb,
|
||||||
}
|
}
|
||||||
onSave(data)
|
onSave(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateServerTrafficLimit = (uuid: string, limitGb: number) => {
|
|
||||||
setServerTrafficLimits(prev => ({
|
|
||||||
...prev,
|
|
||||||
[uuid]: { traffic_limit_gb: limitGb }
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleServer = (uuid: string) => {
|
const toggleServer = (uuid: string) => {
|
||||||
setSelectedSquads(prev =>
|
setSelectedSquads(prev =>
|
||||||
prev.includes(uuid)
|
prev.includes(uuid)
|
||||||
@@ -430,27 +432,24 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
{activeTab === 'servers' && (
|
{activeTab === 'servers' && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p className="text-sm text-dark-400 mb-4">
|
<p className="text-sm text-dark-400 mb-4">
|
||||||
Выберите серверы, доступные на этом тарифе. Можно задать индивидуальный лимит трафика для каждого.
|
Выберите серверы, доступные на этом тарифе.
|
||||||
</p>
|
</p>
|
||||||
{servers.length === 0 ? (
|
{servers.length === 0 ? (
|
||||||
<p className="text-dark-500 text-center py-4">Нет доступных серверов</p>
|
<p className="text-dark-500 text-center py-4">Нет доступных серверов</p>
|
||||||
) : (
|
) : (
|
||||||
servers.map(server => {
|
servers.map(server => {
|
||||||
const isSelected = selectedSquads.includes(server.squad_uuid)
|
const isSelected = selectedSquads.includes(server.squad_uuid)
|
||||||
const serverLimit = serverTrafficLimits[server.squad_uuid]?.traffic_limit_gb || 0
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={server.id}
|
key={server.id}
|
||||||
className={`p-3 rounded-lg transition-colors ${
|
onClick={() => toggleServer(server.squad_uuid)}
|
||||||
|
className={`p-3 rounded-lg transition-colors cursor-pointer ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'bg-accent-500/20 border border-accent-500/50'
|
? 'bg-accent-500/20 border border-accent-500/50'
|
||||||
: 'bg-dark-700 hover:bg-dark-600 border border-transparent'
|
: 'bg-dark-700 hover:bg-dark-600 border border-transparent'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex items-center gap-3">
|
||||||
onClick={() => toggleServer(server.squad_uuid)}
|
|
||||||
className="flex items-center gap-3 cursor-pointer"
|
|
||||||
>
|
|
||||||
<div className={`w-5 h-5 rounded flex items-center justify-center ${
|
<div className={`w-5 h-5 rounded flex items-center justify-center ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'bg-accent-500 text-white'
|
? 'bg-accent-500 text-white'
|
||||||
@@ -463,27 +462,6 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
<span className="text-xs text-dark-500">{server.country_code}</span>
|
<span className="text-xs text-dark-500">{server.country_code}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isSelected && (
|
|
||||||
<div className="mt-2 ml-8 flex items-center gap-2">
|
|
||||||
<span className="text-xs text-dark-400">Лимит трафика:</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={serverLimit}
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
onChange={e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
updateServerTrafficLimit(server.squad_uuid, Math.max(0, parseInt(e.target.value) || 0))
|
|
||||||
}}
|
|
||||||
className="w-20 px-2 py-1 bg-dark-600 border border-dark-500 rounded text-sm text-dark-100 focus:outline-none focus:border-accent-500"
|
|
||||||
min={0}
|
|
||||||
placeholder="0"
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-dark-400">ГБ</span>
|
|
||||||
{serverLimit === 0 && (
|
|
||||||
<span className="text-xs text-dark-500">(использовать общий)</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -585,6 +563,124 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Плавающий тариф - произвольное количество дней */}
|
||||||
|
<div className="p-4 bg-dark-700/50 rounded-lg">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-medium text-dark-200">Произвольное количество дней</h4>
|
||||||
|
<p className="text-xs text-dark-500 mt-1">Пользователь сам выбирает срок подписки</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setCustomDaysEnabled(!customDaysEnabled)}
|
||||||
|
className={`w-10 h-6 rounded-full transition-colors relative ${
|
||||||
|
customDaysEnabled ? 'bg-accent-500' : 'bg-dark-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`absolute top-1 w-4 h-4 bg-white rounded-full transition-transform ${
|
||||||
|
customDaysEnabled ? 'left-5' : 'left-1'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{customDaysEnabled && (
|
||||||
|
<div className="space-y-3 pt-2 border-t border-dark-600">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-sm text-dark-400 w-32">Цена за день:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={pricePerDayKopeks / 100}
|
||||||
|
onChange={e => setPricePerDayKopeks(Math.max(0, parseFloat(e.target.value) || 0) * 100)}
|
||||||
|
className="w-24 px-3 py-2 bg-dark-600 border border-dark-500 rounded-lg text-dark-100 focus:outline-none focus:border-accent-500"
|
||||||
|
min={0}
|
||||||
|
step={0.1}
|
||||||
|
/>
|
||||||
|
<span className="text-dark-400">₽</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-sm text-dark-400 w-32">Мин. дней:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={minDays}
|
||||||
|
onChange={e => setMinDays(Math.max(1, parseInt(e.target.value) || 1))}
|
||||||
|
className="w-24 px-3 py-2 bg-dark-600 border border-dark-500 rounded-lg text-dark-100 focus:outline-none focus:border-accent-500"
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-sm text-dark-400 w-32">Макс. дней:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={maxDays}
|
||||||
|
onChange={e => setMaxDays(Math.max(1, parseInt(e.target.value) || 1))}
|
||||||
|
className="w-24 px-3 py-2 bg-dark-600 border border-dark-500 rounded-lg text-dark-100 focus:outline-none focus:border-accent-500"
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Плавающий тариф - произвольный трафик */}
|
||||||
|
<div className="p-4 bg-dark-700/50 rounded-lg">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-medium text-dark-200">Произвольный объём трафика</h4>
|
||||||
|
<p className="text-xs text-dark-500 mt-1">Пользователь сам выбирает объём трафика при покупке</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setCustomTrafficEnabled(!customTrafficEnabled)}
|
||||||
|
className={`w-10 h-6 rounded-full transition-colors relative ${
|
||||||
|
customTrafficEnabled ? 'bg-accent-500' : 'bg-dark-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`absolute top-1 w-4 h-4 bg-white rounded-full transition-transform ${
|
||||||
|
customTrafficEnabled ? 'left-5' : 'left-1'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{customTrafficEnabled && (
|
||||||
|
<div className="space-y-3 pt-2 border-t border-dark-600">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-sm text-dark-400 w-32">Цена за 1 ГБ:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={trafficPricePerGbKopeks / 100}
|
||||||
|
onChange={e => setTrafficPricePerGbKopeks(Math.max(0, parseFloat(e.target.value) || 0) * 100)}
|
||||||
|
className="w-24 px-3 py-2 bg-dark-600 border border-dark-500 rounded-lg text-dark-100 focus:outline-none focus:border-accent-500"
|
||||||
|
min={0}
|
||||||
|
step={0.1}
|
||||||
|
/>
|
||||||
|
<span className="text-dark-400">₽</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-sm text-dark-400 w-32">Мин. ГБ:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={minTrafficGb}
|
||||||
|
onChange={e => setMinTrafficGb(Math.max(1, parseInt(e.target.value) || 1))}
|
||||||
|
className="w-24 px-3 py-2 bg-dark-600 border border-dark-500 rounded-lg text-dark-100 focus:outline-none focus:border-accent-500"
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-sm text-dark-400 w-32">Макс. ГБ:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={maxTrafficGb}
|
||||||
|
onChange={e => setMaxTrafficGb(Math.max(1, parseInt(e.target.value) || 1))}
|
||||||
|
className="w-24 px-3 py-2 bg-dark-600 border border-dark-500 rounded-lg text-dark-100 focus:outline-none focus:border-accent-500"
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Режим сброса трафика */}
|
{/* Режим сброса трафика */}
|
||||||
<div className="p-4 bg-dark-700/50 rounded-lg">
|
<div className="p-4 bg-dark-700/50 rounded-lg">
|
||||||
<h4 className="text-sm font-medium text-dark-200 mb-3">Режим сброса трафика</h4>
|
<h4 className="text-sm font-medium text-dark-200 mb-3">Режим сброса трафика</h4>
|
||||||
@@ -636,7 +732,7 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
disabled={!name || periodPrices.length === 0 || isLoading}
|
disabled={!name || (periodPrices.length === 0 && !customDaysEnabled) || isLoading}
|
||||||
className="px-4 py-2 bg-accent-500 text-white rounded-lg hover:bg-accent-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="px-4 py-2 bg-accent-500 text-white rounded-lg hover:bg-accent-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{isLoading ? 'Сохранение...' : 'Сохранить'}
|
{isLoading ? 'Сохранение...' : 'Сохранить'}
|
||||||
@@ -668,9 +764,6 @@ function DailyTariffModal({ tariff, servers, onSave, onClose, isLoading }: Daily
|
|||||||
const [tierLevel, setTierLevel] = useState(tariff?.tier_level || 1)
|
const [tierLevel, setTierLevel] = useState(tariff?.tier_level || 1)
|
||||||
const [dailyPriceKopeks, setDailyPriceKopeks] = useState(tariff?.daily_price_kopeks || 0)
|
const [dailyPriceKopeks, setDailyPriceKopeks] = useState(tariff?.daily_price_kopeks || 0)
|
||||||
const [selectedSquads, setSelectedSquads] = useState<string[]>(tariff?.allowed_squads || [])
|
const [selectedSquads, setSelectedSquads] = useState<string[]>(tariff?.allowed_squads || [])
|
||||||
const [serverTrafficLimits, setServerTrafficLimits] = useState<Record<string, ServerTrafficLimit>>(
|
|
||||||
tariff?.server_traffic_limits || {}
|
|
||||||
)
|
|
||||||
// Докупка трафика
|
// Докупка трафика
|
||||||
const [trafficTopupEnabled, setTrafficTopupEnabled] = useState(tariff?.traffic_topup_enabled || false)
|
const [trafficTopupEnabled, setTrafficTopupEnabled] = useState(tariff?.traffic_topup_enabled || false)
|
||||||
const [maxTopupTrafficGb, setMaxTopupTrafficGb] = useState(tariff?.max_topup_traffic_gb || 0)
|
const [maxTopupTrafficGb, setMaxTopupTrafficGb] = useState(tariff?.max_topup_traffic_gb || 0)
|
||||||
@@ -684,13 +777,6 @@ function DailyTariffModal({ tariff, servers, onSave, onClose, isLoading }: Daily
|
|||||||
const [activeTab, setActiveTab] = useState<'basic' | 'servers' | 'extra'>('basic')
|
const [activeTab, setActiveTab] = useState<'basic' | 'servers' | 'extra'>('basic')
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
const filteredLimits: Record<string, ServerTrafficLimit> = {}
|
|
||||||
for (const uuid of selectedSquads) {
|
|
||||||
if (serverTrafficLimits[uuid] && serverTrafficLimits[uuid].traffic_limit_gb > 0) {
|
|
||||||
filteredLimits[uuid] = serverTrafficLimits[uuid]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: TariffCreateRequest | TariffUpdateRequest = {
|
const data: TariffCreateRequest | TariffUpdateRequest = {
|
||||||
name,
|
name,
|
||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
@@ -701,7 +787,6 @@ function DailyTariffModal({ tariff, servers, onSave, onClose, isLoading }: Daily
|
|||||||
tier_level: tierLevel,
|
tier_level: tierLevel,
|
||||||
period_prices: [],
|
period_prices: [],
|
||||||
allowed_squads: selectedSquads,
|
allowed_squads: selectedSquads,
|
||||||
server_traffic_limits: Object.keys(filteredLimits).length > 0 ? filteredLimits : {},
|
|
||||||
traffic_topup_enabled: trafficTopupEnabled,
|
traffic_topup_enabled: trafficTopupEnabled,
|
||||||
traffic_topup_packages: trafficTopupPackages,
|
traffic_topup_packages: trafficTopupPackages,
|
||||||
max_topup_traffic_gb: maxTopupTrafficGb,
|
max_topup_traffic_gb: maxTopupTrafficGb,
|
||||||
@@ -712,13 +797,6 @@ function DailyTariffModal({ tariff, servers, onSave, onClose, isLoading }: Daily
|
|||||||
onSave(data)
|
onSave(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateServerTrafficLimit = (uuid: string, limitGb: number) => {
|
|
||||||
setServerTrafficLimits(prev => ({
|
|
||||||
...prev,
|
|
||||||
[uuid]: { traffic_limit_gb: limitGb }
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleServer = (uuid: string) => {
|
const toggleServer = (uuid: string) => {
|
||||||
setSelectedSquads(prev =>
|
setSelectedSquads(prev =>
|
||||||
prev.includes(uuid)
|
prev.includes(uuid)
|
||||||
@@ -870,20 +948,17 @@ function DailyTariffModal({ tariff, servers, onSave, onClose, isLoading }: Daily
|
|||||||
) : (
|
) : (
|
||||||
servers.map(server => {
|
servers.map(server => {
|
||||||
const isSelected = selectedSquads.includes(server.squad_uuid)
|
const isSelected = selectedSquads.includes(server.squad_uuid)
|
||||||
const serverLimit = serverTrafficLimits[server.squad_uuid]?.traffic_limit_gb || 0
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={server.id}
|
key={server.id}
|
||||||
className={`p-3 rounded-lg transition-colors ${
|
onClick={() => toggleServer(server.squad_uuid)}
|
||||||
|
className={`p-3 rounded-lg transition-colors cursor-pointer ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'bg-amber-500/20 border border-amber-500/50'
|
? 'bg-amber-500/20 border border-amber-500/50'
|
||||||
: 'bg-dark-700 hover:bg-dark-600 border border-transparent'
|
: 'bg-dark-700 hover:bg-dark-600 border border-transparent'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex items-center gap-3">
|
||||||
onClick={() => toggleServer(server.squad_uuid)}
|
|
||||||
className="flex items-center gap-3 cursor-pointer"
|
|
||||||
>
|
|
||||||
<div className={`w-5 h-5 rounded flex items-center justify-center ${
|
<div className={`w-5 h-5 rounded flex items-center justify-center ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'bg-amber-500 text-white'
|
? 'bg-amber-500 text-white'
|
||||||
@@ -896,26 +971,6 @@ function DailyTariffModal({ tariff, servers, onSave, onClose, isLoading }: Daily
|
|||||||
<span className="text-xs text-dark-500">{server.country_code}</span>
|
<span className="text-xs text-dark-500">{server.country_code}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isSelected && (
|
|
||||||
<div className="mt-2 ml-8 flex items-center gap-2">
|
|
||||||
<span className="text-xs text-dark-400">Лимит трафика:</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={serverLimit}
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
onChange={e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
updateServerTrafficLimit(server.squad_uuid, Math.max(0, parseInt(e.target.value) || 0))
|
|
||||||
}}
|
|
||||||
className="w-20 px-2 py-1 bg-dark-600 border border-dark-500 rounded text-sm text-dark-100 focus:outline-none focus:border-amber-500"
|
|
||||||
min={0}
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-dark-400">ГБ</span>
|
|
||||||
{serverLimit === 0 && (
|
|
||||||
<span className="text-xs text-dark-500">(использовать общий)</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user