feat: chat language display, disabled EntitySelect items, dev scripts
Chat language: - Added language_code field to TelegramChat model + migration - Saved from message.from.language_code on webhook/polling - Displayed as badge on bot chat cards and target receiver items - Resolved from DB in target API response (works for existing receivers) - Shown in chat picker dropdown (desc includes language) EntitySelect improvements: - Tracker-target link selector shows all targets, already-linked ones appear disabled with "Already linked" hint - Receiver chat picker shows already-added chats as disabled Dev scripts: - scripts/restart-backend.sh and restart-frontend.sh - Updated .claude/docs/dev-servers.md to reference scripts
This commit is contained in:
@@ -176,6 +176,7 @@
|
||||
"linkedTargets": "targets",
|
||||
"noLinkedTargets": "No targets linked. Add a target below.",
|
||||
"addTarget": "Add target",
|
||||
"alreadyLinked": "Already linked",
|
||||
"testBasic": "Send test message",
|
||||
"testPeriodic": "Test periodic summary",
|
||||
"testScheduled": "Test scheduled assets",
|
||||
|
||||
@@ -176,6 +176,7 @@
|
||||
"linkedTargets": "получатели",
|
||||
"noLinkedTargets": "Нет привязанных получателей. Добавьте получателя ниже.",
|
||||
"addTarget": "Добавить получателя",
|
||||
"alreadyLinked": "Уже привязан",
|
||||
"testBasic": "Отправить тестовое сообщение",
|
||||
"testPeriodic": "Тест периодической сводки",
|
||||
"testScheduled": "Тест запланированных фото",
|
||||
|
||||
@@ -306,6 +306,7 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium">{chat.title || chat.username || 'Unknown'}</span>
|
||||
<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{chatTypeLabel(chat.type)}</span>
|
||||
{#if chat.language_code}<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{chat.language_code.toUpperCase()}</span>{/if}
|
||||
<span class="text-xs text-[var(--color-muted-foreground)] font-mono">{chat.chat_id}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
|
||||
@@ -429,7 +429,7 @@
|
||||
{tracker}
|
||||
{trackingConfigs}
|
||||
{templateConfigs}
|
||||
unlinkedTargets={getUnlinkedTargets(tracker)}
|
||||
unlinkedTargets={targets}
|
||||
newLinkTargetId={newLinkTargetId[tracker.id] || 0}
|
||||
newLinkTrackingConfigId={newLinkTrackingConfigId[tracker.id] || 0}
|
||||
newLinkTemplateConfigId={newLinkTemplateConfigId[tracker.id] || 0}
|
||||
|
||||
@@ -57,11 +57,14 @@
|
||||
|
||||
const trackingConfigItems = $derived(toItems(trackingConfigs));
|
||||
const templateConfigItems = $derived(toItems(templateConfigs));
|
||||
const linkedTargetIds = $derived(new Set((tracker.tracker_targets || []).map((tt: any) => tt.target_id)));
|
||||
const targetItems = $derived<EntityItem[]>(unlinkedTargets.map(tgt => ({
|
||||
value: tgt.id,
|
||||
label: tgt.name,
|
||||
icon: tgt.icon || (tgt.type === 'telegram' ? 'mdiSend' : 'mdiWebhook'),
|
||||
desc: tgt.type,
|
||||
disabled: linkedTargetIds.has(tgt.id),
|
||||
disabledHint: linkedTargetIds.has(tgt.id) ? t('notificationTracker.alreadyLinked') : undefined,
|
||||
})));
|
||||
</script>
|
||||
|
||||
|
||||
@@ -311,7 +311,19 @@
|
||||
receiverSubmitting = true;
|
||||
receiverHeadersError = '';
|
||||
try {
|
||||
const config = { ...receiverForm };
|
||||
const config: Record<string, any> = { ...receiverForm };
|
||||
// Enrich Telegram receiver with chat metadata
|
||||
if (config.chat_id && addingReceiverForTarget) {
|
||||
const target = allTargets.find(t => t.id === addingReceiverForTarget);
|
||||
const botId = target?.config?.bot_id || target?.config?.telegram_bot_id;
|
||||
if (botId && receiverBotChats[botId]) {
|
||||
const chat = receiverBotChats[botId].find((c: any) => String(c.chat_id) === String(config.chat_id));
|
||||
if (chat) {
|
||||
config.chat_name = chat.title || chat.username || '';
|
||||
if (chat.language_code) config.language_code = chat.language_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Parse headers JSON for webhook
|
||||
if ('headers' in config && typeof config.headers === 'string') {
|
||||
if (config.headers) {
|
||||
@@ -535,6 +547,9 @@
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<MdiIcon name={TYPE_ICONS[target.type] || 'mdiTarget'} size={14} />
|
||||
<span class="text-sm truncate">{receiverLabel(target, recv)}</span>
|
||||
{#if (recv as any).language_code || recv.config?.language_code}
|
||||
<span class="text-xs px-1 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{((recv as any).language_code || recv.config.language_code).toUpperCase()}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<IconButton icon="mdiSend" title={t('targets.test')}
|
||||
@@ -563,11 +578,11 @@
|
||||
{#if target.type === 'telegram'}
|
||||
{@const botId = target.config?.bot_id}
|
||||
{@const existingKeys = new Set((target.receivers || []).map((r: TargetReceiver) => r.receiver_key))}
|
||||
{@const chatItems = (receiverBotChats[botId] || []).map((c: TelegramChat) => ({
|
||||
{@const chatItems = (receiverBotChats[botId] || []).map((c: any) => ({
|
||||
value: c.chat_id,
|
||||
label: c.title || c.username || c.chat_id,
|
||||
icon: c.type === 'private' ? 'mdiAccount' : c.type === 'channel' ? 'mdiBullhorn' : 'mdiAccountGroup',
|
||||
desc: `${c.type} · ${c.chat_id}`,
|
||||
desc: `${c.type}${c.language_code ? ' · ' + c.language_code.toUpperCase() : ''} · ${c.chat_id}`,
|
||||
disabled: existingKeys.has(c.chat_id),
|
||||
disabledHint: existingKeys.has(c.chat_id) ? t('targets.alreadyAdded') : undefined,
|
||||
}))}
|
||||
|
||||
Reference in New Issue
Block a user