Frontend Specs
Section 7 — System Configuration (`/admin/system`)
Role access: ADMIN only. VIEWER/EDITOR/CLIENT redirected to /dashboard.
docs/specs/frontend/admin-spec-03a-system.mdOn this page
- 7.1 System Settings Panel
- 1. Purpose
- 2. Visual Layout
- 3. Data Source (tRPC endpoint)
- 4. Zod Schema
- 5. Fetch Frequency
- 6. Data Manipulations
- 7. Rationale
- 8. Interaction Flows
- 9. Error States
- 10. Role-Based Variations
- 7.2 Integration Status
- 1. Purpose
- 2. Visual Layout
- 3. Data Source (tRPC endpoint)
- 4. Zod Schema
- 5. Fetch Frequency
- 6. Data Manipulations
- 7. Rationale
- 8. Interaction Flows
- 9. Error States
- 10. Role-Based Variations
- 7.3 Maintenance Mode Toggle
- 1. Purpose
- 2. Visual Layout
- 3. Data Source (tRPC endpoint)
- 4. Zod Schema
- 5. Fetch Frequency
- 6. Data Manipulations
- 7. Rationale
- 8. Interaction Flows
- 9. Error States
- 10. Role-Based Variations
Role access: ADMIN only.
VIEWER/EDITOR/CLIENTredirected to/dashboard. Layout: Tabbed interface with 3 tabs: Settings, Integrations, Maintenance.1200pxmax-width. Sidebar:carbon(#12161E) background. Notes: All changes saved immediately viaadmin.updateConfigwith optimistic UI + rollback on error. Unsaved changes show a floating save bar.
7.1 System Settings Panel#
1. Purpose#
Central control panel for all global platform configuration. Admins adjust rate limits, API keys, email templates, feature flags, and business rules without redeploying.
2. Visual Layout#
Section A: API & Rate Limits (top-left, 50% width)
- 2-column grid of editable fields:
- OpenAI API key: masked input (
••••••sk-...) + "Show" toggle + "Rotate" button - Max tokens per request: number input (
16384default) - Rate limit (req/min): number input (
60default) - Max concurrent jobs: number input (
50default) - Default model: select (
gpt-4o/gpt-4o-mini/gpt-4-turbo) - Temperature default: slider
0.0–1.0with value label (0.7default)
- OpenAI API key: masked input (
- Each field has a label + helper text below in
muted-text(#4B5C67) - Edited fields show an amber dot indicator left of the label
- "Save Changes" sticky button at bottom of section (disabled until changes detected)
Section B: Business Rules (top-right, 50% width)
- Form fields stacked:
- Trial days: number input (
14default) - Cancellation grace period (days): number input (
7default) - Max clients per plan: number inputs per tier (
Starter: 1,Growth: 3,Pro: 10) - Content approval queue: toggle ON/OFF (for medical clients)
- Auto-approve threshold: number input (
3days, for non-medical clients) - Default timezone: select (UTC, Asia/Kolkata, America/New_York, etc.)
- Currency default: select (
USD,INR,EUR,GBP)
- Trial days: number input (
- Each tier input has a mini plan badge:
Starter,Growth,Proinpillstyle
Section C: Email & Notifications (bottom, full width)
- 2-column layout:
- Left: SMTP settings (host, port, username, password, from address, from name)
- Right: Default notification templates (welcome email subject, cancellation reminder subject, password reset subject)
- All fields editable inline. "Test Send" button per template to send a test email to the admin's own address.
Section D: Feature Flags (bottom, full width)
- Table of toggles:
AI_CONTENT_GENERATION: enabled (default)GBP_AUTO_POSTING: enabled (default)SOCIAL_AUTO_POSTING: enabled (default)CITATION_AUTO_SUBMISSION: enabled (default)NEW_SIGNUP_ENABLED: enabled (default)IMPERSONATION_ENABLED: enabled (default)BETA_FEATURES: disabled (default)
- Each row: flag name + description + toggle switch + last changed timestamp + who changed it
- Toggle switches use Ember Orange (
#EC652B) for ON state,muted-textfor OFF state - Disabled features show a grayed-out description with "Contact engineering to enable" tooltip
3. Data Source (tRPC endpoint)#
// Read config
admin.getSystemConfig.useQuery(undefined, { refetchInterval: 300000 }); // 5min static
// Update single config
admin.updateConfig.useMutation();
// Rotate API key
admin.rotateApiKey.useMutation({ keyName: z.enum(["OPENAI", "STRIPE", "SENDGRID"]) });
4. Zod Schema#
const SystemConfigSchema = z.object({
api: z.object({
openAiKeyMasked: z.string().regex(/^sk-[a-zA-Z0-9]{4}\*{48}$/), // e.g., sk-Abcd••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
maxTokens: z.number().min(1).max(128000).default(16384),
rateLimitPerMinute: z.number().min(1).max(1000).default(60),
maxConcurrentJobs: z.number().min(1).max(500).default(50),
defaultModel: z.enum(["gpt-4o", "gpt-4o-mini", "gpt-4-turbo"]).default("gpt-4o"),
defaultTemperature: z.number().min(0).max(1).default(0.7),
}),
business: z.object({
trialDays: z.number().min(0).max(90).default(14),
cancellationGraceDays: z.number().min(0).max(30).default(7),
maxClientsPerPlan: z.object({
starter: z.number().min(1).default(1),
growth: z.number().min(1).default(3),
pro: z.number().min(1).default(10),
}),
contentApprovalQueueEnabled: z.boolean().default(true),
autoApproveThresholdDays: z.number().min(0).max(30).default(3),
defaultTimezone: z.string().default("UTC"),
defaultCurrency: z.enum(["USD", "INR", "EUR", "GBP"]).default("USD"),
}),
email: z.object({
smtp: z.object({
host: z.string().min(1),
port: z.number().min(1).max(65535).default(587),
username: z.string().email(),
password: z.string().min(1), // masked in UI
fromAddress: z.string().email(),
fromName: z.string().min(1).default("RankFlow AI"),
}),
templates: z.object({
welcomeSubject: z.string().default("Welcome to RankFlow AI 🚀"),
cancellationReminderSubject: z.string().default("Your RankFlow AI subscription ends in {days} days"),
passwordResetSubject: z.string().default("Reset your RankFlow AI password"),
}),
}),
featureFlags: z.array(z.object({
name: z.string(),
description: z.string(),
enabled: z.boolean(),
lastChangedAt: z.date(),
lastChangedBy: z.string(), // user name
})),
});
5. Fetch Frequency#
- Static (
300000ms / 5min): Config changes rarely. 5min polling is sufficient. - On-demand: After any mutation, invalidate
admin.getSystemConfig. - Real-time: Feature flags push via SSE when changed by another admin session.
6. Data Manipulations#
- Masking: API keys displayed as
sk-Abcd••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••— first 8 chars visible, rest masked. Full key only shown on "Show" toggle. - Rotation: Clicking "Rotate" generates new key server-side, returns masked version, updates
lastChangedAt. - Diff tracking: Form state tracks dirty fields. Floating save bar shows count: "3 unsaved changes".
- Validation: Inline Zod validation on blur. Red border + error message below field.
- Tiers: Max clients per plan rendered as 3 mini input groups with colored plan badges.
7. Rationale#
- Single config screen: Prevents config from being scattered across files, environment variables, and DB. One place to check all settings.
- Masked keys: Security — keys never displayed in full by default. Rotation is one-click.
- Feature flags: Enables toggling features without code deploy. Critical for incident response (e.g., disable new signups during overload).
- 5min polling: Config changes are rare and expensive to compute. No need for real-time.
- Optimistic UI: Admin changes a setting → UI updates immediately → mutation fires. If mutation fails, UI reverts with toast explaining why. Prevents "did it save?" anxiety.
8. Interaction Flows#
- Edit field: Type → amber dot appears → save bar slides up with "Save 3 changes" + "Discard" → click Save → mutation → green toast "Saved" → dots disappear.
- Rotate API key: Click "Rotate" → confirmation modal ("This will invalidate the old key immediately. Confirm?") → mutation → new masked key appears → green toast "Key rotated. Update any external integrations." →
lastChangedAtupdates. - Test email: Click "Test Send" next to template → toast "Test email sent to admin@rankflow.ai" → check inbox.
- Toggle feature flag: Click toggle → immediate optimistic UI change → mutation → if failed, toggle reverts with red toast. If succeeded, green toast +
lastChangedAtandlastChangedByupdate. - Discard changes: Click "Discard" in save bar → all fields revert to last saved state → confirmation if >5 changes.
- Impersonation: Settings are read-only during impersonation. All fields disabled, save bar hidden. Red banner shows "Impersonation — Read Only".
9. Error States#
- Loading: Skeleton form with 8 shimmer rows.
- Permission denied: If non-ADMIN navigates here, 403 redirect (handled at route level, not in widget).
- Mutation fail: Network error → toast "Failed to save. Retry?" with retry button. Field keeps amber dot.
- Validation error: Red border on field + text below: "Must be between 1 and 500" or "Invalid email format".
- API key rotation fail: If key provider API down, toast "Rotation failed: OpenAI API unreachable. Key unchanged."
- Test email fail: Toast "SMTP configuration error. Check host and credentials."
- Concurrent edit: If another admin changes the same field while you're editing, banner appears: "This setting was changed by {name} at {time}. Reload to see latest values." with "Reload" button.
10. Role-Based Variations#
- ADMIN: Full read/write. Can rotate keys, toggle flags, save changes.
- Other roles: No access. Redirected to
/dashboard. - Impersonation: Read-only. All inputs disabled, save bar hidden. Can view but not modify. Actions are logged with
impersonating: trueandaction: "VIEW_SYSTEM_CONFIG".
7.2 Integration Status#
1. Purpose#
Live health dashboard for all external service integrations. Admins see at a glance if OpenAI, Google, Stripe, SendGrid, Redis, or any third-party service is up, degraded, or down.
2. Visual Layout#
Grid of service cards: 3 columns desktop, 2 tablet, 1 mobile. Each card:
- Top row: Service icon (Lucide) + service name + status badge
- Status badge:
HEALTHY:bg-emerald-50+text-emerald-700+ green dot + "Healthy"DEGRADED:bg-amber-50+text-amber-700+ amber dot + "Degraded" + tooltip with reasonDOWN:bg-red-50+text-red-700+ red dot + "Down" + tooltip with last error
- Metrics row: 2-3 mini stats:
- For OpenAI: "Last request: 2s ago", "Avg latency: 847ms", "Error rate: 0.1%"
- For Stripe: "Last webhook: 5min ago", "Pending events: 0"
- For SendGrid: "Queue depth: 0", "Last sent: 1min ago"
- For Redis: "Memory: 47MB/128MB", "Connected clients: 12"
- Action row: "Test Connection" button + "View Logs" link (opens Audit & Logs with service filter)
- Card background:
bg-whitewithshadow-sm. Border-left:4pxcolored by status (green/amber/red).
Services monitored:
- OpenAI API
- Google Cloud Platform (GBP, Search Console, Places API)
- Stripe API + Webhooks
- SendGrid / SMTP
- Redis (BullMQ)
- Inngest (event queue)
- Prisma / Database
- Better Auth (session store)
- External social APIs (Meta, X, LinkedIn — if connected)
3. Data Source (tRPC endpoint)#
admin.getIntegrationStatus.useQuery(undefined, { refetchInterval: 10000 }); // 10s real-time
admin.testIntegration.useMutation({ service: z.enum(["OPENAI", "STRIPE", "SENDGRID", "REDIS", "INGEST", "GCP"]) });
4. Zod Schema#
const IntegrationStatusSchema = z.array(z.object({
service: z.enum(["OPENAI", "STRIPE", "SENDGRID", "REDIS", "INGEST", "GCP", "META", "X", "LINKEDIN", "PRISMA", "BETTER_AUTH"]),
status: z.enum(["HEALTHY", "DEGRADED", "DOWN"]),
lastCheckedAt: z.date(),
lastError: z.string().optional(),
lastErrorAt: z.date().optional(),
metrics: z.object({
lastRequestMs: z.number().optional(), // ms since last request
avgLatencyMs: z.number().optional(),
errorRatePercent: z.number().optional(),
queueDepth: z.number().optional(),
memoryUsedMb: z.number().optional(),
memoryTotalMb: z.number().optional(),
connectedClients: z.number().optional(),
pendingEvents: z.number().optional(),
}).optional(),
testEndpoint: z.string().url().optional(), // URL to test
}));
5. Fetch Frequency#
- Real-time (
10000ms / 10s): Integration status is critical for platform health. 10s polling. - On-demand: Click "Test Connection" → fires one-time test mutation, returns live result.
- Push (SSE): If status changes from HEALTHY to DOWN or DEGRADED, push immediate update via SSE channel.
6. Data Manipulations#
- Color coding: Status → card border-left color + badge color.
- Latency formatting:
< 1000ms→ "847ms";>= 1000ms→ "1.2s" (red if > 5000ms). - Error rate:
0%→ hidden;> 0%→ shown in red if > 1%, amber if > 0.1%. - Memory bar:
memoryUsedMb / memoryTotalMb→ thin progress bar inside card (green if < 50%, amber if < 80%, red if >= 80%). - Last checked: "Just now" (< 30s), "2s ago", "15s ago", "1min ago".
- Sorting: Default by status severity (DOWN first, then DEGRADED, then HEALTHY) then alphabetical.
7. Rationale#
- Card grid: Admins need to scan 10+ services quickly. Card grid with color-coded borders lets them spot problems in < 2 seconds.
- 10s polling: Integration status is critical infrastructure. If Stripe is down, signups fail. If OpenAI is down, content generation stops. Need near-real-time awareness.
- In-card metrics: Key metrics (latency, error rate) without needing to drill down. Contextual to each service.
- Test button: When a service shows DEGRADED, admin can immediately test the connection to confirm if it's a transient issue or real outage.
- SSE push for status changes: Polling every 10s catches most issues, but a service going DOWN needs immediate attention. SSE pushes the change instantly.
8. Interaction Flows#
- Card hover: Slight shadow lift (
shadow-md). Click anywhere on card opens detail panel (slide-in from right) with full metrics history (last 24h). - Test Connection: Click button → button shows spinner → result appears inline (green "Connected in 234ms" or red "Connection failed: timeout") → if failed, card border turns red and status updates.
- View Logs: Click link → navigates to
/admin/auditwith service filter pre-selected. - DEGRADED tooltip: Hover over amber badge → tooltip shows specific reason: "High latency: 4.2s average (threshold: 2s)" or "Error rate: 3.2% (threshold: 1%)".
- DOWN tooltip: Hover red badge → shows last error message + "Since {time}".
- Impersonation: Read-only. Test Connection button disabled. Detail panel accessible but no actions.
9. Error States#
- Loading: Skeleton card grid (3x3 cards with shimmer).
- All healthy: Subtle green header "All systems operational — checked {time}".
- One down: Red banner at top of page: "⚠️ {Service} is down. Revenue-impacting." with direct link to logs.
- Multiple down: Red banner + alert sound (optional browser notification) + card grid auto-scrolls to first DOWN card.
- Test fail: Inline red text below button. "Timeout after 10s. Check firewall rules."
- No services: "No integrations configured. Check environment variables." (system misconfiguration).
10. Role-Based Variations#
- ADMIN: Full access. Test, view logs, view detail panel.
- Other roles: No access.
- Impersonation: Read-only. Test buttons disabled. Detail panel viewable.
7.3 Maintenance Mode Toggle#
1. Purpose#
One-click emergency switch to put the entire platform in maintenance mode. Shows a branded maintenance page to all non-admin users. Used for critical deployments, database migrations, or incident response.
2. Visual Layout#
Prominent toggle card at top of page (full width, bg-red-50 border border-red-200):
- Left: Warning icon (triangle,
text-red-600) + heading "Maintenance Mode" intext-2xltext-red-700 - Below heading: "When enabled, all non-admin users see a maintenance page. Current sessions are not terminated but new requests are blocked."
- Right: Large toggle switch (
w-14 h-8scale). ON state: red background, white circle. OFF state:gray-300background. - Below toggle: "Last used: {date} by {name}" or "Never used".
When toggle is ON:
- Card background changes to
bg-red-100withborder-red-300. - Additional fields appear below:
- "Custom message" textarea (default: "We're performing scheduled maintenance. Back in a few minutes.")
- "Estimated return" datetime picker (optional)
- "Allow admins to bypass": toggle ON by default
- Live preview panel on right: shows exactly what users will see (maintenance page preview in an iframe or mock card).
When toggle is OFF:
- Card returns to
bg-whitewithborder-red-200border. - Extra fields hidden.
- "Last used: {date} by {name}" if previously used.
3. Data Source (tRPC endpoint)#
admin.getMaintenanceStatus.useQuery(undefined, { refetchInterval: 30000 }); // 30s frequent
admin.toggleMaintenanceMode.useMutation({
enabled: z.boolean(),
customMessage: z.string().max(500).optional(),
estimatedReturnAt: z.date().optional(),
allowAdminBypass: z.boolean().default(true),
});
4. Zod Schema#
const MaintenanceModeSchema = z.object({
enabled: z.boolean().default(false),
customMessage: z.string().max(500).default("We're performing scheduled maintenance. Back in a few minutes."),
estimatedReturnAt: z.date().optional().nullable(),
allowAdminBypass: z.boolean().default(true),
lastToggledAt: z.date().optional(),
lastToggledBy: z.string().optional(), // user name
toggleHistory: z.array(z.object({
action: z.enum(["ENABLED", "DISABLED"]),
at: z.date(),
by: z.string(),
reason: z.string().optional(),
})).optional(),
});
5. Fetch Frequency#
- Frequent (
30000ms / 30s): Maintenance status is critical for platform availability. 30s polling. - On-demand: After toggle mutation, invalidate and refetch immediately.
- SSE: When maintenance mode is toggled by another admin, push update to all admin sessions.
6. Data Manipulations#
- Toggle animation: Switch animates with
duration-300ease. Circle slides left/right with spring feel. - Preview: Live preview of maintenance page rendered in a card mock-up showing the custom message and estimated return time.
- History: Last 5 toggles shown in a collapsible "History" accordion below the card.
- Reason field: When toggling ON, prompt admin for reason (optional but encouraged): "Why are you enabling maintenance mode?" (deploy, migration, incident, etc.). Logged in audit trail.
7. Rationale#
- Big red toggle: This is a nuclear option. It needs to be prominent, hard to miss, and hard to trigger accidentally. The large red toggle + warning icon + red card makes it visually distinct from all other settings.
- Live preview: Admin sees exactly what users will experience before committing. Reduces anxiety about breaking the user experience.
- Admin bypass: Critical so admins can continue working during maintenance (e.g., verifying the fix before turning off maintenance mode).
- 30s polling: If another admin toggles it, everyone needs to know. Prevents conflicting actions.
- Audit trail: Every toggle is logged with reason, who, when. Essential for post-incident review.
- Custom message: During a known migration, admin can set "Database migration in progress — back by 3:00 PM IST." During an incident, "We're investigating an issue — back soon."
8. Interaction Flows#
- Toggle ON: Click toggle → confirmation modal: "Enable maintenance mode? All non-admin users will be blocked. Admins can still access." + optional reason textarea → click "Enable" → mutation → card turns red → preview updates → toast "Maintenance mode enabled. Users blocked." → SSE broadcast to all clients → all non-admin users see maintenance page immediately.
- Toggle OFF: Click toggle → confirmation: "Disable maintenance mode? Users will regain access immediately." → click "Disable" → mutation → card returns to white → toast "Maintenance mode disabled. Users can access." → SSE broadcast.
- Edit message: Type in textarea → preview updates live in real-time.
- Set estimated return: Pick datetime → preview shows "Expected back: {time}".
- History expand: Click "History" → accordion opens → shows last 5 toggles with timestamps and reasons.
- Impersonation: Toggle is hidden entirely during impersonation. Cannot see or modify maintenance mode. Only visible in non-impersonated admin sessions.
9. Error States#
- Toggle fail: If mutation fails (e.g., DB connection issue), toggle reverts to original state + red toast "Failed to toggle maintenance mode. Check system status."
- Concurrent toggle: If another admin toggled it while you were viewing, banner: "Maintenance mode was {enabled/disabled} by {name} at {time}." → reload to sync.
- Already in maintenance: If navigating to
/admin/systemwhile platform is in maintenance mode, show red banner at top: "⚠️ Platform is in maintenance mode. Users are blocked." - Preview fail: If preview can't render, show "Preview unavailable. Toggle to test."
- No permission: Non-ADMIN sees 403 before reaching this widget.
10. Role-Based Variations#
- ADMIN: Full toggle control, edit message, set return time, view history.
- Other roles: No access to admin routes at all.
- Impersonation: Toggle hidden. Admin cannot accidentally put platform in maintenance mode while impersonating a client. Section is replaced with: "Maintenance mode controls are hidden during impersonation. End session to access."