# IGNY8 Phase 4: White-Label & Reporting (04B) ## Automated Reports, Agency Branding, Admin Dashboard, Client View **Document Version:** 1.0 **Date:** 2026-03-23 **Phase:** IGNY8 Phase 4 — Business Layer **Status:** Build Ready **Source of Truth:** Codebase at `/data/app/igny8/` **Audience:** Claude Code, Backend Developers, Frontend Developers, Architects --- ## 1. CURRENT STATE ### No Reporting Infrastructure - Reports created manually by Alorig team via Google Docs/Sheets - No automated metric collection across IGNY8 modules - No PDF generation capability - No scheduled report delivery ### No Agency/Reseller Model - No white-label capability — all reports show IGNY8 branding - No custom branding configuration - No agency account structure ### Phase 4A Foundation Available (04A) - `ManagedServiceSubscription` — client subscription data - `BacklinkServiceOrder` — order tracking with margin - `AhrefsDomainSnapshot` — DR, traffic, referring domains data - `BacklinkIndexingRequest` — indexing status tracking ### Phase 2 Data Sources Available - `Content` (writer app) — articles published, word counts - `URLInspectionRecord` + `GSCMetricsCache` (02C) — indexing status, search performance - `SAGBacklink` + `CampaignKPISnapshot` (02E) — backlink campaign metrics - `SocialPost` + `SocialEngagement` (02H) — social media metrics - `SAGBlueprint` health score (01G) — SAG completion and authority - All IDs are integers (BigAutoField) --- ## 2. WHAT TO BUILD ### Overview An automated report generation engine with 9 configurable sections, PDF output, white-label branding for agencies, an admin services dashboard for Alorig, and a client-facing read-only view for managed service subscribers. ### Report Generation Engine **9 Configurable Report Sections** (each can be included/excluded per client): | # | Section | Data Sources | |---|---------|-------------| | 1 | **Executive Summary** | AI-generated from all other sections — key wins, KPI highlights | | 2 | **Content Performance** | `Content` model — articles published by type, word count, images created | | 3 | **Indexing Status** | `URLInspectionRecord` + `GSCMetricsCache` (02C) — indexed/pending/error trend | | 4 | **Keyword Rankings** | `GSCMetricsCache` (02C) + `AhrefsDomainSnapshot` (04A) — top 10/20/50, movers | | 5 | **Organic Traffic** | `GSCMetricsCache` (02C) — clicks, impressions, CTR, avg position, MoM change | | 6 | **Backlink Campaign** | `SAGBacklink` (02E) + `BacklinkServiceOrder` (04A) + `AhrefsDomainSnapshot` (04A) — links built, DR dist, budget, quality, referring domain growth | | 7 | **Social Media** | `SocialPost` + `SocialEngagement` (02H) — posts by platform, engagement, top performers | | 8 | **SAG Health** | `SAGBlueprint` (01G) — completion %, cluster status, authority score (0-100), content gaps | | 9 | **Next Month Plan** | `SAGCampaign` (02E) + automation queue — upcoming content topics, backlink targets, action items | ### PDF Report Generation | Component | Choice | Rationale | |-----------|--------|-----------| | Template engine | WeasyPrint (HTML → PDF) | CSS-based layout, full typography control | | Charts | matplotlib (server-side rendering) | SVG/PNG charts embedded in HTML | | Storage | S3/media storage | URL saved in `ServiceReport.report_pdf_url` | | Themes | Light + Dark | Configurable per template | **Report Flow:** ``` Celery task triggers → Collect metrics from all sources → Render section HTML per included sections → Generate charts via matplotlib → Merge into base template → WeasyPrint converts HTML → PDF → Upload to storage → Save ServiceReport with PDF URL → Email to recipients if configured ``` ### White-Label Branding (Agency Play) For agencies reselling IGNY8 services: **`AgencyBranding` provides:** - Agency name (replaces "IGNY8" / "Alorig" throughout PDF) - Logo URL (replaces IGNY8 logo in header) - Primary + secondary colors (brand color scheme) - Contact email + phone + website URL - Footer text **When `ServiceReport.is_white_label = True`:** - All "IGNY8" and "Alorig" text replaced with `brand_name` - Logo swapped to `brand_logo_url` - Footer shows agency contact info - Color scheme uses agency brand colors - Report URL domain can be custom (via Caddy subdomain routing) ### Admin Services Dashboard (Alorig Team Only) **Frontend Pages (.tsx + Zustand):** ``` frontend/src/pages/Services/ ├── ServicesDashboard.tsx # Overview: all managed clients, MRR, revenue │ ├── ClientList.tsx # All subscriptions with status, tier, articles count │ ├── RevenueSummary.tsx # MRR total, margin total, trend chart │ └── UpcomingActions.tsx # Overdue: reports, content, renewals ├── ClientDetail.tsx # Single client management │ ├── ClientConfig.tsx # Service config editor (JSON form) │ ├── ContentTracker.tsx # Articles published this month vs target │ ├── BacklinkTracker.tsx # Links ordered vs delivered vs live vs indexed │ ├── AhrefsMetrics.tsx # DR history, referring domains, top pages │ ├── ReportHistory.tsx # Past reports with resend option │ └── BillingHistory.tsx # Invoices, payments, margin per order ├── BacklinkOrders.tsx # All orders across clients │ ├── OrderList.tsx # Filterable by client, status, package │ └── OrderDetail.tsx # Individual order with link-by-link tracking ├── IndexingDashboard.tsx # Backlink indexing status across all clients └── ReportGenerator.tsx # Generate report for any client + period ``` **Sidebar addition** (admin-only, behind `managed_services_enabled` feature flag): ``` ADMIN ├── Sector Templates └── Managed Services (NEW) ``` ### Client-Facing View (Managed Service Subscribers) For managed clients who also have IGNY8 SaaS access: **Frontend Pages (.tsx + Zustand):** ``` frontend/src/pages/ManagedService/ ├── ServiceOverview.tsx # What's included, current month progress ├── ReportViewer.tsx # View/download past reports └── ApprovalQueue.tsx # Approve blueprint, review content (if review_required) ``` **Sidebar addition** (only shows if `ManagedServiceSubscription` exists for account): ``` ACCOUNT ├── Account Settings ├── Plans & Billing ├── Managed Service (NEW — conditional) ├── Usage └── AI Models ``` --- ## 3. DATA MODELS & APIS ### New Models (in `services` app) **`ServiceReport`** (extends `AccountBaseModel`) | Field | Type | Description | |-------|------|-------------| | `site` | ForeignKey(Site) | Report target site | | `managed_subscription` | ForeignKey(ManagedServiceSubscription, null=True) | Linked subscription | | `report_type` | CharField(max_length=20) | `weekly` / `monthly` / `quarterly` | | `period_start` | DateField | Report period start | | `period_end` | DateField | Report period end | | `report_data` | JSONField | All metrics for period (structured by section) | | `report_pdf_url` | URLField(blank=True) | Generated PDF in S3 | | `is_white_label` | BooleanField(default=False) | Use agency branding | | `branding` | ForeignKey(AgencyBranding, null=True) | Agency branding reference | | `sections_included` | JSONField | List of included section keys | | `generated_at` | DateTimeField(auto_now_add=True) | Generation timestamp | | `sent_at` | DateTimeField(null=True) | Email sent timestamp | | `sent_to` | JSONField(default=list) | List of recipient emails | Table: `igny8_service_reports` **`AgencyBranding`** (extends `AccountBaseModel`) | Field | Type | Description | |-------|------|-------------| | `brand_name` | CharField(max_length=200) | Agency name | | `brand_logo_url` | URLField(blank=True) | Logo URL | | `primary_color` | CharField(max_length=7) | Hex color code | | `secondary_color` | CharField(max_length=7) | Hex color code | | `contact_email` | EmailField(blank=True) | Agency contact email | | `contact_phone` | CharField(blank=True) | Agency phone | | `website_url` | URLField(blank=True) | Agency website | | `footer_text` | TextField(blank=True) | Custom footer text | | `is_active` | BooleanField(default=True) | Active flag | Table: `igny8_agency_branding` **`ReportTemplate`** (extends `AccountBaseModel`) | Field | Type | Description | |-------|------|-------------| | `name` | CharField(max_length=200) | Template name | | `template_html` | TextField | Base HTML template | | `template_css` | TextField | Custom CSS | | `is_default` | BooleanField(default=False) | System default | | `branding` | ForeignKey(AgencyBranding, null=True) | Agency-specific template | Table: `igny8_report_templates` ### API Endpoints All under `/api/v1/`: **Service Reports:** | Method | Endpoint | Purpose | |--------|----------|---------| | GET | `/services/reports/` | List (admin + client's own) | | POST | `/services/reports/generate/` | Generate report for site + period | | GET | `/services/reports/{id}/` | Detail + PDF URL | | POST | `/services/reports/{id}/send/` | Email to recipients | | POST | `/services/reports/{id}/regenerate/` | Regenerate PDF | | GET | `/services/reports/preview/` | Preview with data (no save) | **Agency Branding:** | Method | Endpoint | Purpose | |--------|----------|---------| | GET | `/services/branding/` | List brandings | | POST | `/services/branding/` | Create branding | | GET | `/services/branding/{id}/` | Detail | | PUT | `/services/branding/{id}/` | Update | | DELETE | `/services/branding/{id}/` | Delete | **Report Templates:** | Method | Endpoint | Purpose | |--------|----------|---------| | GET | `/services/report-templates/` | List templates | | POST | `/services/report-templates/` | Create custom template | | PUT | `/services/report-templates/{id}/` | Update template | **Dashboard (Admin):** | Method | Endpoint | Purpose | |--------|----------|---------| | GET | `/services/dashboard/` | Revenue, clients, MRR | | GET | `/services/dashboard/overdue/` | Overdue deliverables | | GET | `/services/dashboard/margin/` | Margin tracking | | GET | `/services/dashboard/clients/` | Client list with status summary | ### Celery Tasks | Task | Schedule | Purpose | |------|----------|---------| | `generate_weekly_reports` | Every Monday 6am | Generate for all Pro clients | | `generate_monthly_reports` | 1st of month | Generate for all Lite + Pro clients | | `send_pending_reports` | Daily | Email any generated but unsent reports | | `check_overdue_deliverables` | Daily | Flag clients behind on content/links | | `collect_report_metrics` | Nightly | Pre-aggregate metrics for faster generation | ### Services | Service | Purpose | |---------|---------| | `ReportService` | `generate_report()` — collect metrics, render PDF | | `ReportEmailService` | Send report emails with PDF attachment or link | ### New Python Dependencies - `weasyprint` — HTML → PDF rendering - `matplotlib` — chart generation (server-side SVG/PNG) --- ## 4. IMPLEMENTATION STEPS ### Build Sequence **Week 1: Models + Report Engine** 1. Create `ServiceReport`, `AgencyBranding`, `ReportTemplate` models + migration 2. Create serializers + viewsets 3. Build `ReportService` — metric collection from all 9 data sources 4. Build PDF template (base HTML + CSS) with section rendering 5. Integrate WeasyPrint for HTML → PDF conversion 6. Build chart generation via matplotlib (DR trend, traffic chart, etc.) **Week 2: White-Label + Dashboard** 1. Build `AgencyBranding` CRUD endpoints 2. Implement branding injection in report templates 3. Build admin dashboard API endpoints 4. Build Celery tasks for scheduled report generation **Week 3: Frontend** 1. Build `ServicesDashboard.tsx` + child components 2. Build `ClientDetail.tsx` + child components 3. Build `BacklinkOrders.tsx` + `IndexingDashboard.tsx` 4. Build `ReportGenerator.tsx` 5. Build client-facing `ManagedService/` pages 6. Add sidebar items (admin + client conditional) ### Zustand Stores ```typescript // frontend/src/stores/servicesStore.ts interface ServicesState { subscriptions: ManagedServiceSubscription[] reports: ServiceReport[] orders: BacklinkServiceOrder[] dashboard: DashboardSummary | null loading: boolean fetchSubscriptions: () => Promise fetchReports: (siteId: number) => Promise generateReport: (params: ReportParams) => Promise fetchDashboard: () => Promise } // frontend/src/stores/brandingStore.ts interface BrandingState { brandings: AgencyBranding[] templates: ReportTemplate[] fetchBrandings: () => Promise createBranding: (data: AgencyBrandingInput) => Promise updateBranding: (id: number, data: AgencyBrandingInput) => Promise } ``` --- ## 5. ACCEPTANCE CRITERIA ### Report Generation - [ ] Reports generate with data from all 9 sections when data available - [ ] Sections configurable per client — excluded sections omitted from PDF - [ ] PDF renders correctly with charts, tables, and branding - [ ] WeasyPrint produces valid PDF files uploadable to S3 - [ ] Charts render via matplotlib (DR trend, traffic chart, ranking distribution) - [ ] Generated PDF URL saved in `ServiceReport.report_pdf_url` ### Scheduled Reports - [ ] Weekly reports auto-generate on Monday 6am for all Pro clients - [ ] Monthly reports auto-generate on 1st for all Lite + Pro clients - [ ] `send_pending_reports` emails generated-but-unsent reports - [ ] Pre-aggregation task collects metrics nightly for faster generation ### White-Label Branding - [ ] `AgencyBranding` stores name, logo, colors, contact info - [ ] When `is_white_label = True`, "IGNY8"/"Alorig" replaced with `brand_name` - [ ] Logo, colors, and footer text swapped in PDF output - [ ] Multiple brandings supported per account ### Admin Dashboard - [ ] `ServicesDashboard.tsx` shows all managed clients, MRR, revenue - [ ] `ClientDetail.tsx` shows config, content tracker, backlink tracker, reports, billing - [ ] `BacklinkOrders.tsx` lists all orders filterable by client, status, package - [ ] `IndexingDashboard.tsx` shows indexing status across all clients - [ ] `ReportGenerator.tsx` generates on-demand report for any client + period - [ ] Sidebar shows "Managed Services" link only for admin users ### Client-Facing View - [ ] `ServiceOverview.tsx` shows tier, articles progress, current month status - [ ] `ReportViewer.tsx` lists past reports with download links - [ ] `ApprovalQueue.tsx` shows pending blueprint/content approvals - [ ] Sidebar shows "Managed Service" only when subscription exists --- ## 6. CLAUDE CODE INSTRUCTIONS ### Context Requirements 1. Read 04A (Managed Services) — subscription, order, Ahrefs, indexing models 2. Read 02C (GSC Integration) — GSCMetricsCache, URLInspectionRecord for report sections 3. Read 02E (Linker External) — SAGBacklink, CampaignKPISnapshot for backlink section 4. Read 02H (Socializer) — SocialPost, SocialEngagement for social section 5. Read 01G (SAG Health Monitoring) — health score for SAG section 6. Read existing billing app — understand existing admin sidebar structure ### Execution Order ``` Models + migrations → ReportService → PDF template + WeasyPrint → AgencyBranding CRUD → Celery tasks → Admin dashboard frontend → Client frontend ``` ### Critical Rules 1. **All IDs are integers** — BigAutoField PKs 2. **Model names PLURAL** — Clusters, Keywords, etc. 3. **Table prefix** — `igny8_` for all tables 4. **App** — models go in `services` app (same as 04A) 5. **Frontend** — `.tsx` files, Zustand stores, in `frontend/src/pages/Services/` and `frontend/src/pages/ManagedService/` 6. **Feature flag** — admin dashboard behind `managed_services_enabled` flag 7. **Report data** — always query by site + date range, never expose cross-account data 8. **PDF security** — generated PDFs accessible only to account owner or admin 9. **Celery app** — `igny8_core` for all tasks 10. **New dependencies** — `weasyprint`, `matplotlib` added to `requirements.txt` ### Cross-References | Doc | Relationship | |-----|-------------| | 04A Managed Services | Subscription + order data feeds all reports | | 02C GSC Integration | GSCMetricsCache → sections 3 (Indexing), 4 (Rankings), 5 (Traffic) | | 02E Linker External | SAGBacklink + CampaignKPISnapshot → section 6 (Backlinks) | | 02H Socializer | SocialPost + SocialEngagement → section 7 (Social Media) | | 01G SAG Health Monitoring | Health score → section 8 (SAG Health) | | 04C Pricing Launch | Feature gates control who can access reports + white-label |