fix: UI polish — overflow, placeholders, dashboard provider card
- Fix bot card header overflow by replacing "Sync with Telegram" text button with icon button, add flex-wrap - Rename sync button label to "Sync Commands" - Remove decorative dashes from selector placeholders (— X — → X) - Show selected provider name/icon in dashboard stat card when global provider filter is active - Add selector placeholder convention to frontend-architecture.md
This commit is contained in:
@@ -330,7 +330,7 @@
|
||||
"rateFind": "Find cooldown",
|
||||
"rateDefault": "Default cooldown",
|
||||
"noCommandsForProvider": "This provider type does not support bot commands.",
|
||||
"syncCommands": "Sync with Telegram",
|
||||
"syncCommands": "Sync Commands",
|
||||
"discoverChats": "Discover chats from Telegram",
|
||||
"clickToCopy": "Click to copy chat ID",
|
||||
"chatsDiscovered": "Chats discovered",
|
||||
@@ -873,7 +873,7 @@
|
||||
"favoritesOnly": "Favorites only",
|
||||
"targetAlbum": "Target Album",
|
||||
"selectAlbum": "Album",
|
||||
"selectAlbumPlaceholder": "— Select album —",
|
||||
"selectAlbumPlaceholder": "Select album",
|
||||
"albumId": "Album ID",
|
||||
"createAlbumIfMissing": "Create album if it doesn't exist",
|
||||
"newAlbumName": "New album name",
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
"rateFind": "Кулдаун поиска файлов",
|
||||
"rateDefault": "Кулдаун по умолчанию",
|
||||
"noCommandsForProvider": "Этот тип провайдера не поддерживает команды бота.",
|
||||
"syncCommands": "Синхронизировать с Telegram",
|
||||
"syncCommands": "Синхр. команды",
|
||||
"discoverChats": "Обнаружить чаты из Telegram",
|
||||
"clickToCopy": "Нажмите, чтобы скопировать ID чата",
|
||||
"chatsDiscovered": "Чаты обнаружены",
|
||||
@@ -873,7 +873,7 @@
|
||||
"favoritesOnly": "Только избранное",
|
||||
"targetAlbum": "Целевой альбом",
|
||||
"selectAlbum": "Альбом",
|
||||
"selectAlbumPlaceholder": "— Выберите альбом —",
|
||||
"selectAlbumPlaceholder": "Выберите альбом",
|
||||
"albumId": "ID альбома",
|
||||
"createAlbumIfMissing": "Создать альбом, если не существует",
|
||||
"newAlbumName": "Название нового альбома",
|
||||
|
||||
@@ -177,8 +177,12 @@
|
||||
|
||||
let displayCommandTrackers = $state(0);
|
||||
|
||||
const filteredProvider = $derived(globalProviderFilter.id ? providers.find(p => p.id === globalProviderFilter.id) : null);
|
||||
|
||||
const statCards = $derived(status ? [
|
||||
{ icon: 'mdiServer', label: 'dashboard.providers', value: displayProviders, color: '#0d9488' },
|
||||
filteredProvider
|
||||
? { icon: providerDefaultIcon(filteredProvider), label: filteredProvider.name, value: filteredProvider.type, color: '#0d9488', isProvider: true }
|
||||
: { icon: 'mdiServer', label: 'dashboard.providers', value: displayProviders, color: '#0d9488' },
|
||||
{ icon: 'mdiRadar', label: 'dashboard.activeTrackers', value: displayActive, suffix: ` / ${displayTotal}`, color: '#6366f1' },
|
||||
{ icon: 'mdiTarget', label: 'dashboard.targets', value: displayTargets, color: '#f59e0b' },
|
||||
...(status.command_trackers !== undefined ? [{ icon: 'mdiConsoleLine', label: 'nav.commandTrackers', value: displayCommandTrackers, color: '#8b5cf6' }] : []),
|
||||
@@ -234,10 +238,14 @@
|
||||
<MdiIcon name={card.icon} size={22} />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm" style="color: var(--color-muted-foreground);">{t(card.label)}</p>
|
||||
<p class="stat-value font-mono" style="animation-delay: {i * 80 + 200}ms;">
|
||||
{card.value}{#if card.suffix}<span class="stat-suffix">{card.suffix}</span>{/if}
|
||||
</p>
|
||||
<p class="text-sm" style="color: var(--color-muted-foreground);">{card.isProvider ? card.label : t(card.label)}</p>
|
||||
{#if card.isProvider}
|
||||
<p class="text-lg font-medium" style="color: {card.color};">{card.value}</p>
|
||||
{:else}
|
||||
<p class="stat-value font-mono" style="animation-delay: {i * 80 + 200}ms;">
|
||||
{card.value}{#if card.suffix}<span class="stat-suffix">{card.suffix}</span>{/if}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -318,9 +318,9 @@
|
||||
<div class="space-y-3 stagger-children">
|
||||
{#each bots as bot}
|
||||
<Card hover entityId={bot.id}>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center justify-between gap-2 flex-wrap">
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span style="color: var(--color-primary);"><MdiIcon name={bot.icon || 'mdiRobot'} size={20} /></span>
|
||||
<p class="font-medium">{bot.name}</p>
|
||||
{#if bot.bot_username}
|
||||
@@ -335,21 +335,17 @@
|
||||
</div>
|
||||
<p class="text-xs text-[var(--color-muted-foreground)] font-mono">{bot.token_preview}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="flex items-center gap-1 flex-shrink-0 flex-wrap">
|
||||
<IconButton icon="mdiPencil" title={t('common.edit')} onclick={() => editBot(bot)} />
|
||||
<button onclick={() => toggleSection(bot.id, 'chats')}
|
||||
class="text-xs text-[var(--color-muted-foreground)] hover:underline px-2 py-1">
|
||||
class="text-xs text-[var(--color-muted-foreground)] hover:underline px-2 py-1 whitespace-nowrap">
|
||||
{t('telegramBot.chats')} {expandedSection[bot.id] === 'chats' ? '▲' : '▼'}
|
||||
</button>
|
||||
<button onclick={() => { toggleSection(bot.id, 'listeners'); if (expandedSection[bot.id] === 'listeners') loadListenerStatus(bot.id); }}
|
||||
class="text-xs text-[var(--color-muted-foreground)] hover:underline px-2 py-1">
|
||||
class="text-xs text-[var(--color-muted-foreground)] hover:underline px-2 py-1 whitespace-nowrap">
|
||||
{t('commandTracker.listeners')} {expandedSection[bot.id] === 'listeners' ? '▲' : '▼'}
|
||||
</button>
|
||||
<button onclick={() => syncCommands(bot.id)} disabled={modeChanging[bot.id]}
|
||||
class="text-xs text-[var(--color-primary)] hover:underline px-2 py-1 flex items-center gap-1">
|
||||
<MdiIcon name="mdiSync" size={14} />
|
||||
{t('telegramBot.syncCommands')}
|
||||
</button>
|
||||
<IconButton icon="mdiSync" title={t('telegramBot.syncCommands')} onclick={() => syncCommands(bot.id)} disabled={modeChanging[bot.id]} />
|
||||
<IconButton icon="mdiDelete" title={t('common.delete')} onclick={() => remove(bot.id)} variant="danger" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -85,12 +85,12 @@
|
||||
<div class="flex items-center gap-2 flex-wrap justify-end">
|
||||
<div class="min-w-[140px]">
|
||||
<EntitySelect items={trackingConfigItems} value={tt.tracking_config_id}
|
||||
placeholder={'— ' + t('trackingConfig.title') + ' —'} size="sm" allowNone noneLabel={'— ' + t('trackingConfig.title') + ' —'}
|
||||
placeholder={t('trackingConfig.title')} size="sm" allowNone noneLabel={t('trackingConfig.title')}
|
||||
onselect={(v) => onupdateLink(tt, 'tracking_config_id', Number(v) || null)} />
|
||||
</div>
|
||||
<div class="min-w-[140px]">
|
||||
<EntitySelect items={templateConfigItems} value={tt.template_config_id}
|
||||
placeholder={'— ' + t('templateConfig.title') + ' —'} size="sm" allowNone noneLabel={'— ' + t('templateConfig.title') + ' —'}
|
||||
placeholder={t('templateConfig.title')} size="sm" allowNone noneLabel={t('templateConfig.title')}
|
||||
onselect={(v) => onupdateLink(tt, 'template_config_id', Number(v) || null)} />
|
||||
</div>
|
||||
<div class="relative">
|
||||
@@ -113,17 +113,17 @@
|
||||
<div class="flex items-center gap-2 mt-2">
|
||||
<div class="flex-1 min-w-[140px]">
|
||||
<EntitySelect items={targetItems} value={newLinkTargetId || null}
|
||||
placeholder={'— ' + t('notificationTracker.addTarget') + ' —'} size="sm"
|
||||
placeholder={t('notificationTracker.addTarget')} size="sm"
|
||||
onselect={(v) => onchangeNewTarget(Number(v) || 0)} />
|
||||
</div>
|
||||
<div class="min-w-[140px]">
|
||||
<EntitySelect items={trackingConfigItems} value={newLinkTrackingConfigId || null}
|
||||
placeholder={'— ' + t('trackingConfig.title') + ' —'} size="sm" allowNone noneLabel={'— ' + t('trackingConfig.title') + ' —'}
|
||||
placeholder={t('trackingConfig.title')} size="sm" allowNone noneLabel={t('trackingConfig.title')}
|
||||
onselect={(v) => onchangeNewTrackingConfig(Number(v) || 0)} />
|
||||
</div>
|
||||
<div class="min-w-[140px]">
|
||||
<EntitySelect items={templateConfigItems} value={newLinkTemplateConfigId || null}
|
||||
placeholder={'— ' + t('templateConfig.title') + ' —'} size="sm" allowNone noneLabel={'— ' + t('templateConfig.title') + ' —'}
|
||||
placeholder={t('templateConfig.title')} size="sm" allowNone noneLabel={t('templateConfig.title')}
|
||||
onselect={(v) => onchangeNewTemplateConfig(Number(v) || 0)} />
|
||||
</div>
|
||||
<button onclick={onaddLink}
|
||||
|
||||
Reference in New Issue
Block a user