Audience UI Migration — Technical Documentation
Last updated: March 2026 Track: B (UI Migration) Status: Planned
Table of Contents
- Overview
- Existing Foundation
- Target UI Architecture
- Implementation Plan
- New Types & DTOs
- New API Endpoints & Hooks
- Component Breakdown
- Verification
1. Overview
Build a complete audience management UI in data-admin (React + TypeScript) that replaces the audience creation and settings workflows currently in Sodastream's SoProMasterSearchController. This track is independent of Track A (Database Consolidation) — it can start at any time and benefits from a single-database foundation once Track A is complete.
Scope
| Capability | Current State | Target |
|---|---|---|
| Audience creation from search | Exists (CreateAudienceModal) — basic fields only | Enhanced with campaign selector, dedup settings, audience type |
| Audience settings management | Sodastream only | New Settings tab in data-admin audience details |
| Email profile picker | Sodastream only | Data API proxy → data-admin dropdown |
| Queue import action | Exists (useQueueAudienceImport) | Verify end-to-end, no changes expected |
| Audience list & details | Exists (list, tabs, contacts) | No changes needed |
2. Existing Foundation
2.1 data-admin — Already Implemented
The data-admin app already has significant audience infrastructure:
Pages:
src/pages/Audiences/— Audience list with search and paginationsrc/pages/Audiences/AudienceDetails/— Detail view with tabs (Overview, Contacts, Settings placeholder)
Components:
src/components/audience/CreateAudienceModal.tsx— Modal for creating audiences from search resultssrc/components/audience/— Various audience-specific components
API Hooks (src/api/audience.queries.ts):
useAudienceHeaders— Paginated audience listuseAudienceHeaderDetails— Single audience detailuseCreateAudienceFromSearch— Create audience from filter datauseQueueAudienceImport— Trigger import jobuseTriggerEmailFinding— Start email findinguseGetAudienceImports— Import history
Endpoints (src/api/endpoints.ts):
- ~40+ audience endpoints already defined
- Covers CRUD, imports, email finding, verification, statistics
Types (src/types/audiences/):
AudienceHeaderModel.ts— Main audience typeCreateAudienceFromFilterDataRequest.ts— Creation DTO- Various supporting types
2.2 Sodastream — Features to Replicate
From SoProMasterSearchController, the features data-admin needs:
| Feature | Sodastream Implementation | data-admin Status |
|---|---|---|
| Search with filters | SoProMasterSearchController.Index/Search | ✅ Already exists (Search page) |
| Create audience from search | SaveSearchModal → CreateAudienceDTO | ⚠️ Exists but needs enhancement |
| Campaign selector | CampaignDropdown + SoProQueueAudience.CampaignId | ❌ Not yet — needs Data API proxy |
| Email profile picker | EmailProfileCheckboxList | ❌ Not yet — needs Data API proxy |
| Daily import volume | DailyImportVolume input | ❌ Not in creation modal |
| Prospects per company | ProspectsPerCompanyPerDay input | ❌ Not in creation modal |
| Auto import toggle | IsAutomaticImport checkbox | ❌ Not in creation modal |
| Dedup days | AudienceDuplicateProspectDays | ❌ Not in creation modal |
| Settings edit page | Inline edits in Sodastream | ❌ No settings tab yet |
3. Target UI Architecture
4. Implementation Plan
4.1 Enhance CreateAudienceModal
Current: Basic modal with name and filter summary. Target: Full creation form with all settings needed for import.
New fields to add:
- Campaign selector — Dropdown queried from Data API (proxy to Sodastream campaigns)
- Daily import volume (
ContactsPerDay) — Number input - Prospects per company per day (
ContactsPerCompanyPerDay) — Number input - Email profile picker — Multi-select checkbox, queried by campaign
- Dedup days (
AudienceDuplicateProspectDays) — Number input - Auto email finding toggle (
IsEmailFindingActive) — Checkbox - Audience type / approach (
ApproachId) — Dropdown
4.2 Build Audience Settings Tab
New tab in the audience details page for editing audience settings post-creation.
Layout: Form with sections matching the creation modal fields, pre-populated from AudienceHeader data. Uses useForm (React Hook Form) for state management and validation.
Sections:
- Import Configuration — Daily volume, per-company cap, auto email finding toggle
- Campaign & Approach — Campaign (read-only or selectable), approach selector
- Email Profiles — Multi-select profile picker (loaded by campaign)
- Dedup Settings — Dedup window days, options
- Active Toggles — Audience enabled, campaign audience active, ad sync flags
4.3 Email Profile Proxy Endpoint
Data API needs to proxy email profiles from Sodastream since they live in the Sodastream database.
GET /admin/Audience/GetEmailProfiles/{campaignId}
Backend: ISodaStreamService.GetEmailProfilesAsync(int campaignId) — calls Sodastream DB, returns list of { Id, Name, IsDeleted }.
4.4 Campaign List Proxy Endpoint
Data API needs to provide campaigns for the dropdown. Already partially exists via SodaStreamService.GetAllCampaignsAsync().
GET /admin/Audience/GetCampaigns
5. New Types & DTOs
5.1 Frontend Types (data-admin)
src/types/audiences/UpdateAudienceSettingsRequest.ts
export interface UpdateAudienceSettingsRequest {
audienceId: string;
name?: string;
contactsPerDay?: number;
contactsPerCompanyPerDay?: number;
isEmailFindingActive?: boolean;
isCampaignAudienceActive?: boolean;
isAudienceEnabled?: boolean;
isAdSync?: boolean;
isAdSyncActive?: boolean;
emailProfileIds?: string;
emailProfileNames?: string;
deletedEmailProfileIds?: string;
audienceDuplicateProspectDays?: number;
option?: number;
approachId?: number;
}
src/types/audiences/EmailProfileModel.ts
export interface EmailProfileModel {
id: number;
name: string;
isDeleted: boolean;
}
src/types/audiences/CampaignSelectModel.ts
export interface CampaignSelectModel {
id: number;
name: string;
clientName: string;
}
5.2 Backend DTOs (sopromasterdata)
UpdateAudienceSettingsRequest— see Track A Phase A1 documentation for full field listEmailProfileResponse—{ Id: int, Name: string, IsDeleted: bool }AudienceConfigResponse— see Track A Phase A3 documentation
6. New API Endpoints & Hooks
6.1 Endpoints to Add
src/api/endpoints.ts:
audiences: {
// ... existing endpoints ...
updateAudienceSettings: "/admin/Audience/UpdateAudienceSettings",
getEmailProfiles: "/admin/Audience/GetEmailProfiles", // + /{campaignId}
getCampaigns: "/admin/Audience/GetCampaigns",
}
6.2 New React Query Hooks
src/api/audience.queries.ts:
// Update audience settings
export const useUpdateAudienceSettings = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (request: UpdateAudienceSettingsRequest) => {
const response = await api.put(endpoints.audiences.updateAudienceSettings, request);
return response.data;
},
onSuccess: (_data, variables) => {
queryClient.invalidateQueries({ queryKey: ["audienceHeaderDetails", variables.audienceId] });
},
});
};
// Get email profiles for campaign
export const useGetEmailProfiles = (campaignId: number | null) => {
return useQuery({
queryKey: ["emailProfiles", campaignId],
queryFn: async () => {
const response = await api.get<EmailProfileModel[]>(`${endpoints.audiences.getEmailProfiles}/${campaignId}`);
return response.data;
},
enabled: !!campaignId,
});
};
// Get campaigns for dropdown
export const useGetCampaigns = () => {
return useQuery({
queryKey: ["campaigns"],
queryFn: async () => {
const response = await api.get<CampaignSelectModel[]>(endpoints.audiences.getCampaigns);
return response.data;
},
});
};
7. Component Breakdown
7.1 Enhanced CreateAudienceModal
| Component | Location | Responsibility |
|---|---|---|
CreateAudienceModal | src/components/audience/CreateAudienceModal.tsx | Modify — Add new form sections |
CampaignSelector | src/components/audience/CampaignSelector.tsx | New — Campaign dropdown with search |
EmailProfilePicker | src/components/audience/EmailProfilePicker.tsx | New — Multi-select email profiles |
ImportSettingsForm | src/components/audience/ImportSettingsForm.tsx | New — Daily volume, per-company cap, auto toggle |
7.2 Audience Settings Tab
| Component | Location | Responsibility |
|---|---|---|
AudienceSettingsTab | src/pages/Audiences/AudienceDetails/tabs/AudienceSettingsTab.tsx | New — Settings form container |
ImportConfigSection | src/components/audience/settings/ImportConfigSection.tsx | New — Import settings section |
CampaignConfigSection | src/components/audience/settings/CampaignConfigSection.tsx | New — Campaign & approach section |
EmailProfileSection | src/components/audience/settings/EmailProfileSection.tsx | New — Email profile management |
DedupConfigSection | src/components/audience/settings/DedupConfigSection.tsx | New — Dedup settings section |
ActiveTogglesSection | src/components/audience/settings/ActiveTogglesSection.tsx | New — Toggle switches section |
7.3 Shared Components
Reuse existing UI components from src/components/ui/:
Button— Form actionsBadge— Status indicators- Form components — Input, Select, Checkbox, Toggle
Table— Email profile list- Spinner — Loading states
8. Verification
Create Audience Flow
- Navigate to Search page → Run search with filters
- Click "Create Audience" → Enhanced modal opens
- Fill in all fields: name, campaign, import volume, email profiles, dedup days
- Submit →
POST /admin/Audience/CreateAudienceFromSearchcalled - Verify
AudienceHeadercreated with all settings - Verify
SoProQueueAudiencecreated with mapped fields (if Track A not yet at A3) - Navigate to new audience → All settings visible in Settings tab
Settings Edit Flow
- Navigate to existing audience → Settings tab
- Form pre-populated with current
AudienceHeadervalues - Edit fields → Submit
PUT /admin/Audience/UpdateAudienceSettingscalled- Verify
AudienceHeaderupdated - Verify reverse sync to
SoProQueueAudience(if Track A not yet at A3)
Import Flow
- Create audience with settings → Queue import
- Verify
TaskAudienceBuilderAutomationsDailyuses correct settings - Verify prospects imported matching the configured volume and per-company caps
Edge Cases
- Campaign with no email profiles → Show empty state with message
- Setting
ContactsPerDayto 0 → Warn user that imports will be paused - Audience already linked to campaign → Campaign selector read-only in settings
Repository Map
| Repository | Layer | Key Files | Action |
|---|---|---|---|
| data-admin | Page | src/pages/Audiences/AudienceDetails/ | Add Settings tab |
| data-admin | Component | src/components/audience/CreateAudienceModal.tsx | Enhance with new fields |
| data-admin | Component | src/components/audience/CampaignSelector.tsx | New |
| data-admin | Component | src/components/audience/EmailProfilePicker.tsx | New |
| data-admin | Component | src/components/audience/settings/ | New directory — all settings sections |
| data-admin | API Hooks | src/api/audience.queries.ts | Add useUpdateAudienceSettings, useGetEmailProfiles, useGetCampaigns |
| data-admin | Endpoints | src/api/endpoints.ts | Add 3 new endpoint entries |
| data-admin | Types | src/types/audiences/UpdateAudienceSettingsRequest.ts | New |
| data-admin | Types | src/types/audiences/EmailProfileModel.ts | New |
| data-admin | Types | src/types/audiences/CampaignSelectModel.ts | New |
| sopromasterdata | Controller | API/.../Admin/AudienceController.cs | Add GetEmailProfiles, GetCampaigns |
| sopromasterdata | Service | SodaStreamServices/SodaStreamService.cs | Add GetEmailProfilesAsync() |