feat: provider-strict configs, slot-based templates, broadcast targets, email bots, command templates
Major architectural improvements: - Provider-type enforcement: configs validated against provider type at assignment - TemplateConfig migrated to slot-based pattern (TemplateSlot child table) - Broadcast targets: TargetReceiver child table for multi-receiver dispatch - EmailBot: first-class email sender entity with SMTP config, test connection - CommandTemplateConfig: generic slot-based command response templates - Provider capability registry: dynamic slot/event/command definitions per provider - CommandTracker play/pause button matches NotificationTracker style
This commit is contained in:
@@ -227,6 +227,16 @@
|
||||
// tracker_targets already loaded in tracker response
|
||||
}
|
||||
|
||||
function getProviderType(tracker: any): string {
|
||||
const p = providers.find(p => p.id === tracker.provider_id);
|
||||
return p?.type || '';
|
||||
}
|
||||
|
||||
function configsForTracker(tracker: any, configs: (TrackingConfig | TemplateConfig)[]): any[] {
|
||||
const pt = getProviderType(tracker);
|
||||
return pt ? configs.filter((c: any) => c.provider_type === pt) : configs;
|
||||
}
|
||||
|
||||
function getUnlinkedTargets(tracker: any): any[] {
|
||||
const linkedIds = new Set((tracker.tracker_targets || []).map((tt: any) => tt.target_id));
|
||||
return targets.filter(t => !linkedIds.has(t.id));
|
||||
@@ -399,13 +409,13 @@
|
||||
onchange={(e: Event) => updateTargetLink(tracker.id, tt, 'tracking_config_id', Number((e.target as HTMLSelectElement).value) || null)}
|
||||
class="text-xs px-2 py-1 border border-[var(--color-border)] rounded-md bg-[var(--color-background)]">
|
||||
<option value={0}>— {t('trackingConfig.title')} —</option>
|
||||
{#each trackingConfigs as tc}<option value={tc.id}>{tc.name}</option>{/each}
|
||||
{#each configsForTracker(tracker, trackingConfigs) as tc}<option value={tc.id}>{tc.name}</option>{/each}
|
||||
</select>
|
||||
<select value={tt.template_config_id || 0}
|
||||
onchange={(e: Event) => updateTargetLink(tracker.id, tt, 'template_config_id', Number((e.target as HTMLSelectElement).value) || null)}
|
||||
class="text-xs px-2 py-1 border border-[var(--color-border)] rounded-md bg-[var(--color-background)]">
|
||||
<option value={0}>— {t('templateConfig.title')} —</option>
|
||||
{#each templateConfigs as tc}<option value={tc.id}>{tc.name}</option>{/each}
|
||||
{#each configsForTracker(tracker, templateConfigs) as tc}<option value={tc.id}>{tc.name}</option>{/each}
|
||||
</select>
|
||||
<div class="relative">
|
||||
<IconButton icon="mdiDotsVertical" size={14} title={t('common.test')}
|
||||
@@ -433,12 +443,12 @@
|
||||
<select bind:value={newLinkTrackingConfigId[tracker.id]}
|
||||
class="text-xs px-2 py-1 border border-[var(--color-border)] rounded-md bg-[var(--color-background)]">
|
||||
<option value={0}>— {t('trackingConfig.title')} —</option>
|
||||
{#each trackingConfigs as tc}<option value={tc.id}>{tc.name}</option>{/each}
|
||||
{#each configsForTracker(tracker, trackingConfigs) as tc}<option value={tc.id}>{tc.name}</option>{/each}
|
||||
</select>
|
||||
<select bind:value={newLinkTemplateConfigId[tracker.id]}
|
||||
class="text-xs px-2 py-1 border border-[var(--color-border)] rounded-md bg-[var(--color-background)]">
|
||||
<option value={0}>— {t('templateConfig.title')} —</option>
|
||||
{#each templateConfigs as tc}<option value={tc.id}>{tc.name}</option>{/each}
|
||||
{#each configsForTracker(tracker, templateConfigs) as tc}<option value={tc.id}>{tc.name}</option>{/each}
|
||||
</select>
|
||||
<button onclick={() => addTargetLink(tracker.id)}
|
||||
disabled={!newLinkTargetId[tracker.id] || addingTarget[tracker.id]}
|
||||
|
||||
Reference in New Issue
Block a user