Developing a Notification System via Admin Panel
An admin panel for sending push notifications is not just a form with "title" and "text" fields. It's a tool for a marketer or product manager who needs to understand their audience, plan campaigns, test variants, and see results. Let's break down the technical components.
System Architecture
Components that interact:
Admin Panel (mobile application)
↓
Campaign API (backend)
↓
Queue (RabbitMQ / Redis)
↓
Push Worker (send via FCM / APNs / OneSignal)
↓
Webhook Handler (delivery statuses)
↓
Analytics DB (clicks, opens, conversions)
Mobile app — just the management interface. The heavy lifting is server-side.
Campaign Creation
Campaign creation form on mobile — step-by-step wizard:
Step 1: Audience. Select from pre-created segments (by tags, behavior, geography) or create new filter here.
Step 2: Content. Title, text, image (Rich Push), Deep Link, action buttons. Notification preview — how it looks on iOS and Android.
Step 3: Schedule. Send now, at specific time, or Intelligent Delivery (auto-optimal time for each user).
Step 4: A/B Test (optional). Two or three text variants, traffic distribution.
Step 5: Confirmation. Summary screen: N users in audience, expected reach, final preview.
// iOS — multi-step wizard via NavigationController
class CampaignWizardCoordinator {
private var campaign = DraftCampaign()
private let navigationController: UINavigationController
func start() {
showAudienceStep()
}
func showAudienceStep() {
let vc = AudienceSelectionVC(draft: campaign) { [weak self] audience in
self?.campaign.audience = audience
self?.showContentStep()
}
navigationController.pushViewController(vc, animated: true)
}
func showContentStep() {
let vc = NotificationContentVC(draft: campaign) { [weak self] content in
self?.campaign.content = content
self?.showScheduleStep()
}
navigationController.pushViewController(vc, animated: true)
}
}
Notification Preview
Preview — important UX element. Marketers often don't know how notifications look on specific platforms.
// Android — custom View for iOS/Android preview
@Composable
fun NotificationPreview(
title: String,
body: String,
imageUrl: String?,
platform: Platform
) {
when (platform) {
Platform.IOS -> IOSNotificationMockup(title, body, imageUrl)
Platform.ANDROID -> AndroidNotificationMockup(title, body, imageUrl)
}
}
@Composable
fun IOSNotificationMockup(title: String, body: String, imageUrl: String?) {
Card(modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp)) {
Row(modifier = Modifier.padding(12.dp)) {
// App icon
Box(modifier = Modifier.size(40.dp).background(Color.Blue, RoundedCornerShape(8.dp)))
Spacer(Modifier.width(8.dp))
Column {
Text(title, fontWeight = FontWeight.SemiBold, fontSize = 13.sp)
Text(body, fontSize = 13.sp, maxLines = 2)
}
imageUrl?.let { AsyncImage(model = it, modifier = Modifier.size(56.dp)) }
}
}
}
Template Management
Templates — pre-saved notification variants for typical events. In admin panel — template list, create new, edit, duplicate.
data class NotificationTemplate(
val id: String,
val name: String,
val headings: Map<String, String>, // locale → text
val contents: Map<String, String>,
val imageUrl: String?,
val data: Map<String, String>, // deep link params
val buttons: List<NotificationButton>,
val category: TemplateCategory
)
Templates stored on server. Client receives list via API, displays in RecyclerView / LazyColumn, lets you select template when creating campaign.
Real-Time Analytics
During active campaign — real-time dashboard:
Sent: 15,234 / 18,500
Delivered: 14,891 (97.7%)
Opened: 2,341 (15.7%)
Button clicks: 891 (38.1% of opened)
Data via WebSocket or Server-Sent Events:
class CampaignStatsStream {
func subscribe(campaignId: String) -> AsyncStream<CampaignStats> {
AsyncStream { continuation in
let eventSource = EventSource(url: URL(string: "/api/campaigns/\(campaignId)/stats/stream")!)
eventSource.onMessage = { _, _, data in
if let stats = try? JSONDecoder().decode(CampaignStats.self, from: data) {
continuation.yield(stats)
}
}
eventSource.connect()
}
}
}
Access Control
Different roles in admin panel:
| Role | Can Create | Can Send | Sees Analytics |
|---|---|---|---|
| Editor | Yes | No (draft only) | Own campaigns only |
| Marketer | Yes | Yes | All campaigns |
| Admin | Yes | Yes | Everything + template management |
Server validates permissions via middleware. On client — disable/hide buttons by role from JWT, but this is UX only, not security.
Timeline
Mobile admin panel with campaign wizard, template management, real-time analytics, campaign history, and role-based access — 10–16 working days for mobile part (excluding server queue and worker infrastructure).







