feat(admin): add never-subscribed broadcast target with audience counts

Add a broadcast audience for users who registered but never had any subscription or trial (no Subscription rows at all), backed by a new get_user_ids_without_any_subscription DAL helper and a 'never' target in the webapp broadcast route.

Add GET /api/admin/broadcast/audience-counts so the audience dropdown shows the recipient count next to each option, with graceful fallback when counts are unavailable.
This commit is contained in:
3252a8
2026-06-01 23:37:59 +03:00
parent 619a13128c
commit 56796d9f22
9 changed files with 84 additions and 4 deletions
@@ -1,16 +1,27 @@
<script>
import { Textarea } from "$components/ui/index.js";
import { Send } from "$components/ui/icons.js";
import { getContext } from "svelte";
import { getContext, onMount } from "svelte";
import { Label } from "$components/ui/primitives.js";
import { AdminButton, AdminSelect } from "$components/patterns/admin/index.js";
export let at;
const broadcastStore = getContext("broadcastStore");
$: ({ broadcastTarget, broadcastText, broadcastBusy, broadcastResult } = $broadcastStore);
$: ({ broadcastTarget, broadcastText, broadcastBusy, broadcastResult, broadcastCounts } =
$broadcastStore);
const BROADCAST_TARGET_OPTIONS = broadcastStore.BROADCAST_TARGET_OPTIONS;
// Append the resolved audience size to each option once counts are loaded.
$: targetOptions = BROADCAST_TARGET_OPTIONS.map((option) => {
const count = broadcastCounts?.[option.value];
return count == null ? option : { ...option, label: `${option.label} (${count})` };
});
onMount(() => {
broadcastStore.loadCounts();
});
</script>
<div class="admin-card">
@@ -24,7 +35,7 @@
<span>{at("broadcast_label_audience", {}, "Аудитория")}</span>
<AdminSelect
value={broadcastTarget}
items={BROADCAST_TARGET_OPTIONS}
items={targetOptions}
ariaLabel={at("broadcast_label_audience", {}, "Аудитория")}
onValueChange={(value) => broadcastStore.updateField({ broadcastTarget: value })}
/>