Add inbound country flags and node load in location picker
- Inbound.CountryCode (ISO alpha-2), set on publish; flag shown before the location name (SVG via country-flag-icons on the site, emoji in bot) - Node.Capacity (optional, not a limit); available inbounds report load percent of capacity or active config count, sorted least loaded first Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d7743977a1
commit
bb5e59904f
51 files changed
+3160
-284
No files matched your search
@@ -11,8 +11,41 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
|
||||
import { HttpError } from '@/shared/api/client'
|
||||
import type { AvailableInboundDto } from '@/shared/api/types'
|
||||
import { CountryFlag } from '@/shared/ui/country-flag'
|
||||
import { cn } from '@/shared/lib/cn'
|
||||
import { createConfig } from './api'
|
||||
|
||||
// Пороги «светофора» загрузки ноды, % от ёмкости.
|
||||
const LOAD_MEDIUM = 50
|
||||
const LOAD_HIGH = 80
|
||||
|
||||
function NodeLoad({ inbound }: { inbound: AvailableInboundDto }) {
|
||||
const { t } = useTranslation()
|
||||
if (inbound.loadPercent !== null) {
|
||||
const level = inbound.loadPercent >= LOAD_HIGH ? 'high' : inbound.loadPercent >= LOAD_MEDIUM ? 'medium' : 'low'
|
||||
return (
|
||||
<span className="ml-auto flex items-center gap-1.5 pl-3 text-xs text-muted-foreground">
|
||||
<span
|
||||
className={cn(
|
||||
'h-2 w-2 rounded-full',
|
||||
level === 'high' ? 'bg-red-500' : level === 'medium' ? 'bg-amber-500' : 'bg-emerald-500',
|
||||
)}
|
||||
aria-hidden
|
||||
/>
|
||||
{t(`configs.load.${level}`)} · {inbound.loadPercent}%
|
||||
</span>
|
||||
)
|
||||
}
|
||||
if (inbound.nodeConfigCount !== null) {
|
||||
return (
|
||||
<span className="ml-auto pl-3 text-xs text-muted-foreground">
|
||||
{t('configs.nodeConfigs', { count: inbound.nodeConfigCount })}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function CreateConfigDialog({ inbounds }: { inbounds: AvailableInboundDto[] }) {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
@@ -74,7 +107,11 @@ export function CreateConfigDialog({ inbounds }: { inbounds: AvailableInboundDto
|
||||
<SelectContent>
|
||||
{inbounds.map((inbound) => (
|
||||
<SelectItem key={inbound.inboundId} value={inbound.inboundId}>
|
||||
{inbound.displayName} ({inbound.protocol})
|
||||
<span className="flex w-full items-center gap-2">
|
||||
<CountryFlag code={inbound.countryCode} />
|
||||
{inbound.displayName} ({inbound.protocol})
|
||||
<NodeLoad inbound={inbound} />
|
||||
</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
Reference in new issue
Block a user