From 91525b89991f0c7229582bc7272206e523dc34ae Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Thu, 25 Dec 2025 22:58:21 +0000 Subject: [PATCH] finalizing app adn fixes --- PRE-LAUNCH-AUDIT.md | 867 +++++++++++++++++ docs/UX-GUIDELINES.md | 156 +++ frontend/src/App.tsx | 35 +- frontend/src/components/auth/AdminRoute.tsx | 32 + frontend/src/components/common/PageHeader.tsx | 5 + frontend/src/layout/AppSidebar.tsx | 85 +- .../src/pages/Automation/AutomationPage.tsx | 18 +- frontend/src/pages/Dashboard/Home.tsx | 32 +- frontend/src/pages/Planner/Clusters.tsx | 7 +- frontend/src/pages/Planner/Ideas.tsx | 9 +- frontend/src/pages/Planner/Keywords.tsx | 7 +- frontend/src/pages/Writer/Content.tsx | 11 +- frontend/src/pages/Writer/Images.tsx | 10 +- frontend/src/pages/Writer/Published.tsx | 10 +- frontend/src/pages/Writer/Review.tsx | 8 +- frontend/src/pages/Writer/Tasks.tsx | 11 +- .../src/pages/account/AccountSettingsPage.tsx | 902 ++++++++++++++---- .../src/pages/account/ContentSettingsPage.tsx | 616 ++++++++++++ .../src/pages/account/PlansAndBillingPage.tsx | 232 +---- 19 files changed, 2498 insertions(+), 555 deletions(-) create mode 100644 PRE-LAUNCH-AUDIT.md create mode 100644 docs/UX-GUIDELINES.md create mode 100644 frontend/src/components/auth/AdminRoute.tsx create mode 100644 frontend/src/pages/account/ContentSettingsPage.tsx diff --git a/PRE-LAUNCH-AUDIT.md b/PRE-LAUNCH-AUDIT.md new file mode 100644 index 00000000..c3dea2d6 --- /dev/null +++ b/PRE-LAUNCH-AUDIT.md @@ -0,0 +1,867 @@ +# Pre-Launch System Audit + +**Date:** December 25, 2025 +**Purpose:** Identify functional workflow gaps and improvements for pre-launch QA +**Scope:** Non-cosmetic, professional, workflow-based issues only + +--- + +## Table of Contents +1. [Dashboard](#1-dashboard) +2. [SETUP Modules](#2-setup-modules) +3. [WORKFLOW Modules](#3-workflow-modules) +4. [ACCOUNT Modules](#4-account-modules) +5. [HELP Module](#5-help-module) +6. [Sidebar & Navigation](#6-sidebar--navigation) +7. [Summary & Prioritization](#7-summary--prioritization) + +--- + +## 1. Dashboard + +**Route:** `/` +**Files:** `pages/Dashboard/Home.tsx`, `components/dashboard/*` + +### Current Functionality +- Workflow Progress: 6-step pipeline visualization (Sites → Keywords → Clusters → Ideas → Content → Published) +- Quick Actions: 5 navigation shortcuts +- Key Metrics: 4 cards (Keywords, Articles, Images, Completion %) +- Credit Usage: Monthly allowance and usage bar +- Workflow Modules Guide: 8 info cards explaining modules +- Onboarding: Site creation wizard for new users + +### Critical Gaps + +| Issue | Impact | Details | +|-------|--------|---------| +| **No aggregated API endpoint** | Performance | Makes 6+ sequential API calls with 120ms delays to avoid rate limiting | +| **Published content count incorrect** | Data accuracy | Cannot distinguish published vs draft content | +| **Usage Summary is hardcoded** | Misleading | Shows fake "547 credits / $0.34" data | +| **Recent Activity is hardcoded** | Misleading | Static mock activity that never updates | +| **No real-time updates** | Stale data | Only refreshes on manual action | + +### Missing Professional Features + +| Feature | Why Important | +|---------|---------------| +| **Needs Attention section** | Users don't know what requires action | +| **Recent content activity** | No real list of recently created/published content | +| **Error/warning alerts** | No indication of failed syncs, low credits, config issues | +| **Pipeline queue depth** | No visibility into items waiting at each stage | +| **Automation status** | No run status, last run time, or items processed | +| **Site health/sync status** | No WordPress sync health indicator | + +### Workflow Issues + +1. **Quick Actions don't adapt to user state** - Shows same 5 actions regardless of workflow stage +2. **Workflow Completion % is misleading** - Formula doesn't reflect real content-from-keywords ratio +3. **Modules Guide not dismissible** - 8 large cards always shown, no way to hide +4. **Chart widget code exists but unused** - Dead code, no trend visualization + +### Recommendations + +**Priority 1 - Must Fix:** +- [ ] Create `/v1/dashboard/summary/` aggregated endpoint +- [ ] Fix published content count (separate published vs draft) +- [ ] Replace hardcoded usage data with real billing data +- [ ] Remove or implement real recent activity + +**Priority 2 - High Value:** +- [ ] Add "Needs Attention" section (pending reviews, failed syncs, low credits) +- [ ] Add actionable pending task count per pipeline stage +- [ ] Add automation run status display +- [ ] Add WordPress sync health indicator + +**Priority 3 - UX Polish:** +- [ ] Make Quick Actions contextual based on workflow state +- [ ] Add loading skeleton +- [ ] Make Workflow Modules Guide dismissible +- [ ] Fix or remove trend chart code + +--- + +## 2. SETUP Modules + +### 2.1 Add Keywords + +**Route:** `/setup/add-keywords` +**Files:** `pages/Setup/AddKeywords.tsx` +**Tabs:** None (single page) + +#### Current Functionality +- Displays global seed keywords filtered by active site's industry/sector +- Browse pre-populated keyword database (admin-imported CSV) +- Add selected keywords to Planner workflow +- Tracks already-added keywords +- Bulk selection and bulk add +- Filters: Search, Country, Difficulty +- Admin CSV upload capability + +#### Functional Gaps + +| Issue | Impact | +|-------|--------| +| **Sector requirement unclear** | Buttons disabled with no tooltip explaining why | +| **No keyword research integration** | Can only browse pre-imported seeds, no external discovery | +| **No manual keyword entry** | Cannot add custom keywords not in seed database | +| **No keyword details/preview** | No SERP features, trends, or related keywords visible | +| **No "already added" filter** | Cannot filter to show only not-yet-added keywords | + +#### Workflow Issues +- No "Next Step" CTA after adding keywords → users don't know to go to Planner +- No keyword count summary (X in workflow, Y available) + +#### Recommendations +- [ ] Add "Next: Plan Your Content →" button after keywords added +- [ ] Add "Show not-yet-added only" filter +- [ ] Add manual keyword entry form +- [ ] Add tooltip explaining disabled state when no sector selected +- [ ] Add keyword count summary + +--- + +### 2.2 Content Settings + +**Route:** `/account/content-settings` +**Files:** `pages/account/ContentSettingsPage.tsx` +**Tabs:** Content Generation, Publishing, Image Settings + +#### Current Functionality +- **Content Generation:** Append to prompt, default tone, default length +- **Publishing:** Auto-publish toggle, keep updated toggle +- **Image Settings:** Quality, style, sizes, format (DALL-E 2/3/Runware) + +#### Critical Gaps + +| Issue | Impact | Severity | +|-------|--------|----------| +| **Content Generation NOT PERSISTED** | Settings appear to save but don't - TODO comments in code | 🔴 Critical | +| **Publishing NOT PERSISTED** | Same issue - no backend API implemented | 🔴 Critical | +| **Only Image Settings work** | Only tab with actual API integration | 🔴 Critical | +| **No per-site settings** | Global only, but multi-site users need site-specific | 🟠 High | + +#### Workflow Issues +- **False confidence**: Users see "Settings saved successfully" but nothing is saved +- No indication that Content Generation/Publishing tabs are non-functional +- Disconnected from Thinker prompts (which also affect content generation) + +#### Recommendations +- [ ] 🔴 **CRITICAL:** Implement backend endpoints for Content Generation settings +- [ ] 🔴 **CRITICAL:** Implement backend endpoints for Publishing settings +- [ ] Either hide non-functional tabs or mark as "Coming Soon" +- [ ] Add relationship explanation to Thinker prompts +- [ ] Consider per-site overrides + +--- + +### 2.3 Sites + +**Route:** `/sites` +**Files:** `pages/Sites/List.tsx`, `pages/Sites/SiteSettings.tsx`, `pages/Sites/SiteDashboard.tsx` +**Tabs:** Site Settings has 3 tabs (General, Integrations, Content Types) + +#### Current Functionality +- List all sites with filtering (search, type, hosting, status) +- Create sites via WorkflowGuide (requires industry + sectors) +- Activate/deactivate sites +- Navigate to site dashboard, content, settings +- Settings: Name, URL, SEO, WordPress integration, content type mapping + +#### Functional Gaps + +| Issue | Impact | +|-------|--------| +| **Dashboard stats are mock data** | `getSiteDashboardStats` returns hardcoded zeros | +| **No inline editing** | Must navigate to settings to change site name | +| **No site cloning** | Cannot duplicate site configuration | +| **Duplicate pages** | List.tsx and Manage.tsx overlap functionality | +| **No bulk operations** | Cannot bulk activate/delete sites | + +#### Workflow Issues +- Complex site creation requires industry/sector upfront (users may not know yet) +- Integration tab default after creation confuses non-technical users +- No setup progress indicator showing what's configured vs pending + +#### Recommendations +- [ ] Add site setup checklist/progress indicator on cards +- [ ] Allow site creation without immediate WordPress setup +- [ ] Add inline editing for site name from list +- [ ] Remove or merge Manage.tsx if redundant +- [ ] Implement real site statistics endpoint +- [ ] Add "Skip integration for now" option + +--- + +### 2.4 Thinker (Admin Only) + +**Route:** `/thinker/prompts` +**Files:** `pages/Thinker/Prompts.tsx`, `pages/Thinker/AuthorProfiles.tsx`, `pages/Thinker/Strategies.tsx`, `pages/Thinker/ImageTesting.tsx` +**Tabs:** Prompts, Author Profiles, Strategies (Coming Soon), Image Testing (Coming Soon) + +#### Current Functionality +- **Prompts:** View/edit AI prompt templates (Clustering, Ideas, Content, Images, etc.) +- **Author Profiles:** CRUD for author profiles (name, description, tone, language) +- **Strategies:** Coming Soon placeholder +- **Image Testing:** Coming Soon placeholder + +#### Functional Gaps + +| Issue | Impact | +|-------|--------| +| **50% Coming Soon** | Strategies + Image Testing are placeholders | 🟠 High | +| **No prompt testing** | Cannot preview prompt output before saving | +| **No prompt versioning** | No history or rollback capability | +| **No per-site prompts** | All prompts global, no site-specific variations | +| **Author Profiles not connected** | Unclear how/where they're used in workflow | +| **No variable reference** | Placeholders like `[IGNY8_KEYWORDS]` undocumented | + +#### Workflow Issues +- Admin-only with no explanation of relationship to Content Settings (user-accessible) +- No prompt categories in UI (hardcoded grouping) +- Strategies/Image Testing pages offer no value + +#### Recommendations +- [ ] Either complete or hide Strategies and Image Testing +- [ ] Add prompt testing capability (preview with sample data) +- [ ] Add prompt version history +- [ ] Document relationship between Content Settings and Thinker Prompts +- [ ] Show where Author Profiles are used +- [ ] Add prompt variable reference documentation + +--- + +### SETUP Cross-Module Issues + +| Issue | Impact | +|-------|--------| +| **No guided flow** | After setup tasks, no clear path to start content workflow | +| **Scattered settings** | Content settings split across 3 locations | +| **No onboarding checklist** | No unified "Setup Complete" indicator | + +#### Setup Completion Checklist Needed: +- [ ] Site created +- [ ] Industry/Sectors selected +- [ ] Integration configured (or skipped) +- [ ] Keywords added +- [ ] Content settings configured + +--- + +## 3. WORKFLOW Modules + +### 3.1 Planner + +**Route:** `/planner/keywords` +**Files:** `pages/Planner/Keywords.tsx`, `pages/Planner/Clusters.tsx`, `pages/Planner/ClusterView.tsx`, `pages/Planner/Ideas.tsx`, `pages/Planner/KeywordOpportunities.tsx` +**Tabs:** Keywords, Clusters, Ideas (in-page navigation) + +#### Current Functionality +- **Keywords:** CRUD, bulk status updates, auto-cluster AI, filters +- **Clusters:** CRUD, bulk operations, auto-generate ideas AI +- **Ideas:** CRUD, bulk queue to writer, filters +- **Flow:** Keywords → Auto-Cluster → Clusters → Auto-Generate Ideas → Ideas → Queue to Writer + +#### Functional Gaps + +| Issue | Impact | Severity | +|-------|--------|----------| +| **KeywordOpportunities hidden** | Page exists at `/planner/keyword-opportunities` but NOT in navigation tabs | 🟠 High | +| **No "Add to Existing Cluster"** | Auto-cluster creates new clusters only, can't add to existing | 🟡 Medium | +| **No cluster merge/split** | Cannot combine or split clusters | 🟡 Medium | +| **No cluster progress indicator** | Can't see which clusters already have ideas generated | 🟠 High | +| **Ideas missing queued count** | No indicator of how many are pending vs processed | 🟡 Medium | + +#### Workflow Issues +- Seed keyword system (KeywordOpportunities) is orphaned from main workflow +- Cluster → Ideas transition unclear (which clusters need ideas?) +- No return path from Ideas to source cluster + +#### Recommendations +- [ ] **Add KeywordOpportunities to Planner tabs** (Critical for onboarding) +- [ ] Add "Assign to Cluster" bulk action for existing clusters +- [ ] Add Ideas Count badge on Clusters table +- [ ] Make cluster name clickable on Ideas page + +--- + +### 3.2 Writer + +**Route:** `/writer/tasks` +**Files:** `pages/Writer/Tasks.tsx`, `pages/Writer/Drafts.tsx`, `pages/Writer/ContentView.tsx`, `pages/Writer/Images.tsx`, `pages/Writer/Review.tsx`, `pages/Writer/Published.tsx` +**Tabs:** Queue, Drafts, Images, Review, Published + +#### Current Functionality +- **Tasks:** CRUD, generate content (row action only), generate images bulk +- **Drafts:** List drafts, view details, status updates +- **Images:** Grouped by content, image generation +- **Review:** Status=review filter, publish to WordPress +- **Published:** Status=published, WordPress sync status + +#### Functional Gaps + +| Issue | Impact | Severity | +|-------|--------|----------| +| **No bulk content generation** | Must click each row individually, `generate_content` removed from bulk | 🔴 Critical | +| **No content editing** | ContentView is read-only, no inline editing | 🔴 Critical | +| **No manual task creation** | Must go through Ideas, can't create task directly | 🟠 High | +| **No content regeneration** | Can't regenerate with different params, must delete & re-queue | 🟠 High | +| **Review → Published manual only** | No simple "Mark as Published" for non-WordPress | 🟡 Medium | + +#### Workflow Issues +- Status progression confusion (Draft → Review → Published requires different pages) +- ContentView missing actions bar (must return to list for actions) +- Images detached from content workflow +- No `send_to_linker` action (only `send_to_optimizer`) + +#### Recommendations +- [ ] 🔴 **CRITICAL:** Add bulk content generation with rate limiting +- [ ] 🔴 **CRITICAL:** Implement inline content editing in ContentView +- [ ] Add content action bar (Edit, Regenerate, Add Images, Optimize, Link, Publish) +- [ ] Add `send_to_linker` row action +- [ ] Add manual task creation capability + +--- + +### 3.3 Automation + +**Route:** `/automation` +**Files:** `pages/Automation/Dashboard.tsx` +**Tabs:** None (single page) + +#### Current Functionality +- Pipeline overview (7 stages: Keywords→Clusters→Ideas→Tasks→Content→Image Prompts→Images) +- Schedule configuration (frequency, time, enable/disable) +- Run controls (Run Now, Pause, Resume) +- Real-time progress polling +- Metrics display and activity log + +#### Functional Gaps + +| Issue | Impact | +|-------|--------| +| **No stage-by-stage control** | All-or-nothing, can't run individual stages | +| **No review gate config** | Stage 7 "Review Gate" has no UI to configure rules | +| **No error recovery** | If stage fails, must rerun entire pipeline | +| **No batch size config** | Can't throttle items per run | +| **No dry run/preview** | Can't see what WOULD process before running | +| **Activity log not filterable** | Can't filter by stage, status, or date | + +#### Workflow Issues +- Credit estimation unclear (labeled "content pieces" but shows credits?) +- Run history depth unknown, no pagination +- No indication of manual vs automated items + +#### Recommendations +- [ ] Add stage toggles (enable/disable individual stages) +- [ ] Add preview mode (show items that will process) +- [ ] Add retry for failed items (per-item or per-stage) +- [ ] Add activity log filters +- [ ] Clarify credit vs content labeling + +--- + +### 3.4 Linker + +**Route:** `/linker/content` +**Files:** `pages/Linker/Content.tsx`, `pages/Linker/Dashboard.tsx` (not routed) +**Tabs:** Content only (Dashboard exists but hidden) + +#### Current Functionality +- Content list with link count and version +- Single-item "Add Links" button +- Batch linking capability (code exists, UI unclear) +- Recent results display (last 3) + +#### Functional Gaps + +| Issue | Impact | +|-------|--------| +| **Dashboard not exposed** | Dashboard.tsx exists but not in navigation | +| **No content filtering** | Can't filter by status, cluster, or link count | +| **No bulk selection UI** | `handleBatchLink` exists but no checkboxes | +| **No link preview/management** | Shows count but can't view/edit actual links | +| **No "Needs Linking" filter** | Can't find content with 0 links easily | + +#### Workflow Issues +- Completely separate from Writer, requires manual navigation +- Link results only show "last 3" in session, no persistent history +- No cluster-based linking + +#### Recommendations +- [ ] Add content filters (status, cluster, "needs linking") +- [ ] Add bulk selection checkboxes +- [ ] Add link details modal (show/manage individual links) +- [ ] Add "Link All New Content" action +- [ ] Integrate into Writer ContentView + +--- + +### 3.5 Optimizer + +**Route:** `/optimizer/content` +**Files:** `pages/Optimizer/Content.tsx`, `pages/Optimizer/AnalysisPreview.tsx` (orphaned), `pages/Optimizer/Dashboard.tsx` (not routed) +**Tabs:** Content only (Dashboard exists but hidden) + +#### Current Functionality +- Content list with optimization scores +- Entry point selection (auto, writer, wordpress, external, manual) +- Single-item and batch optimize +- Score display (overall, SEO, readability, engagement) + +#### Functional Gaps + +| Issue | Impact | +|-------|--------| +| **Dashboard not exposed** | Dashboard.tsx exists but not in navigation | +| **AnalysisPreview orphaned** | Route exists but no UI link to it | +| **Limited filtering** | Only source/search, no score range filter | +| **No optimization history** | No before/after comparison | +| **No optimization settings** | Can't configure what aspects to optimize | +| **No suggested actions** | Scores show but no recommendations | + +#### Workflow Issues +- Must navigate to Optimizer separately (disconnected from Writer) +- Analysis vs Optimize confusion (analyze never used) +- No re-optimization control + +#### Recommendations +- [ ] Add Analysis Preview link ("Preview Scores" action) +- [ ] Add score-based filters ("Score < 70", "Needs SEO") +- [ ] Add optimization recommendations +- [ ] Add "Optimize All Below Threshold" bulk action +- [ ] Integrate into Writer (auto-analyze during draft) + +--- + +### WORKFLOW Cross-Module Issues + +| Issue | Impact | +|-------|--------| +| **No Planner → Writer visibility** | After queuing, must manually switch modules | +| **No Writer → Linker integration** | No "Add Links" button in content view | +| **No Writer → Optimizer integration** | Optimize exists but no score preview | +| **No cross-module notifications** | User doesn't know when AI tasks complete | +| **No breadcrumb navigation** | Can't see full workflow path (Cluster → Idea → Task → Content) | + +#### Cross-Module Recommendations +- [ ] Add global notification system for completed tasks +- [ ] Add breadcrumb navigation showing workflow path +- [ ] Add "Next Step" suggestions after each action +- [ ] Unify content detail view with ALL actions available +- [ ] Add workflow progress indicator + +--- + +## 4. ACCOUNT Modules + +### 4.1 Account Settings + +**Route:** `/account/settings` +**Files:** `pages/account/AccountSettingsPage.tsx` +**Tabs:** Account, Profile, Team + +#### Current Functionality +- **Account Tab:** Organization name, billing email, full billing address, tax ID/VAT +- **Profile Tab:** First/last name, email, phone, timezone, language, notifications, security +- **Team Tab:** List team members, invite via email, remove members, role display + +#### Functional Gaps + +| Issue | Impact | Severity | +|-------|--------|----------| +| **Profile NOT connected to API** | Form saves nowhere - fake save with timeout | 🔴 Critical | +| **No role assignment on invite** | Only email/name collected, no role dropdown | 🟠 High | +| **No role editing for members** | Cannot change Member to Admin or vice versa | 🟠 High | +| **Change Password does nothing** | Static button with no functionality | 🔴 Critical | +| **No email verification** | Can change email without verification | 🟠 High | +| **No 2FA option** | Security section minimal | 🟡 Medium | +| **No account deletion** | Cannot close account | 🟡 Medium | +| **No session management** | Cannot view/revoke active sessions | 🟡 Medium | + +#### Workflow Issues +- Orphaned `TeamManagement.tsx` file exists (395 lines, not routed) +- Inconsistent role system (shows Admin/Member but backend returns `is_admin` boolean) +- No pending invitation status or resend capability +- No team member limit enforcement display + +#### Recommendations +- [ ] 🔴 **CRITICAL:** Implement profile API and connect Profile tab +- [ ] 🔴 **CRITICAL:** Implement password change functionality +- [ ] Add role selection to team invitation +- [ ] Add invitation management (resend, cancel pending) +- [ ] Show team member count vs plan limit +- [ ] Delete orphaned `TeamManagement.tsx` + +--- + +### 4.2 Plans & Billing + +**Route:** `/account/plans` +**Files:** `pages/account/PlansAndBillingPage.tsx`, `pages/Billing/CreditPurchase.tsx` +**Tabs:** Current Plan, Upgrade Plan, History + +#### Current Functionality +- **Current Plan:** Plan name, status, credits, balance, renewal date, features +- **Upgrade:** Pricing table, plan comparison, change policy +- **History:** Invoices with PDF download, payments, payment methods + +#### Functional Gaps + +| Issue | Impact | +|-------|--------| +| **No proration preview** | Doesn't show prorated amount before upgrade | +| **Credit purchase not linked** | `/billing/credits` exists separately but not linked | +| **Cancellation is immediate** | No reason collection, no retention offers | +| **No payment failure retry** | If payment fails, no retry UI | +| **No downgrade proration display** | Policy exists but no calculation shown | + +#### Workflow Issues +- Throttling errors surface directly to users ("Request was throttled. Retrying...") +- Cancel flow has no confirmation dialog +- Payment method supports bank_transfer, manual, stripe, paypal but UI only shows some +- No billing cycle visualization (renewal date not prominent) + +#### Recommendations +- [ ] Add proration preview before plan changes +- [ ] Add confirmation dialog for cancellation +- [ ] Link credit purchase from this page +- [ ] Add cancellation reason collection +- [ ] Clean up throttling messages (use spinner) + +--- + +### 4.3 Usage + +**Route:** `/account/usage` +**Files:** `pages/account/UsageAnalyticsPage.tsx`, `pages/account/UsageLimits.tsx`, `pages/account/CreditActivity.tsx` +**Tabs:** Your Limits & Usage, Credit History, API Activity + +#### Current Functionality +- **Quick Stats:** Credits left, used this month, monthly limit, usage % +- **Limits:** Hard limits (sites, users, keywords, clusters) + Monthly limits +- **Credit History:** Transaction log with type, amount, description +- **API Activity:** Call statistics, endpoint breakdown + +#### Critical Gaps + +| Issue | Impact | Severity | +|-------|--------|----------| +| **API Activity is HARDCODED** | Shows fake static values (1,234, 567, 342) not real data | 🔴 Critical | +| **Success rate is fake** | Hardcoded 98.5% | 🔴 Critical | +| **No usage alerts** | No notification when approaching limits | 🟠 High | +| **No per-site usage** | Can't see which site consumed what | 🟠 High | +| **No per-user usage** | Can't see team member individual usage | 🟠 High | +| **No usage export** | Cannot download usage report | 🟡 Medium | +| **No usage forecasting** | No "you'll run out in X days" | 🟡 Medium | + +#### Workflow Issues +- No actionable insights (doesn't suggest upgrade when hitting limits) +- Credit history lacks context (no link to what was generated) +- Disconnected from billing (separate page to upgrade) + +#### Recommendations +- [ ] 🔴 **CRITICAL:** Implement actual API activity tracking or hide tab +- [ ] Add usage alerts configuration (email at 80%, 90%, 100%) +- [ ] Add per-site/per-user usage breakdown +- [ ] Add "Upgrade" CTA when limits approaching +- [ ] Add usage export functionality + +--- + +### 4.4 AI Models (Admin Only) + +**Route:** `/settings/integration` +**Files:** `pages/Settings/IntegrationPage.tsx` +**Sections:** OpenAI Integration, Runware Integration, Image Generation, Site Integrations + +#### Current Functionality +- **OpenAI:** Enable/disable, model selection, connection testing, validation +- **Runware:** Enable/disable, model selection, connection testing +- **Image Generation:** Service selection, model selection, image settings +- **Testing:** Generate test images with preview +- **Site Integrations:** Connected site management + +#### Functional Gaps + +| Issue | Impact | Severity | +|-------|--------|----------| +| **Fictional GPT model names** | Shows GPT-5.1, GPT-5.2 which don't exist | 🔴 Critical | +| **No fallback configuration** | If OpenAI fails, no automatic Runware fallback | 🟠 High | +| **No cost tracking** | Can't see integration costs | 🟡 Medium | +| **No integration health history** | Only current status, no uptime history | 🟡 Medium | +| **No API key rotation** | Can't rotate keys without disabling | 🟡 Medium | +| **No audit log** | No record of when settings changed | 🟠 High | + +#### Workflow Issues +- Admin-only but affects all users (no scope clarification) +- Page mixes LLM models, image generation, AND site integrations +- Complex modal nesting (settings, details, form all separate) +- No preview of cost impact when changing models + +#### Recommendations +- [ ] 🔴 **CRITICAL:** Fix GPT model names to actual models (gpt-4-turbo, gpt-4, gpt-3.5-turbo) +- [ ] Add integration change audit logging +- [ ] Add cost estimation when changing models +- [ ] Consider separating site integrations to own page +- [ ] Add fallback configuration + +--- + +### ACCOUNT Cross-Module Issues + +| Issue | Impact | +|-------|--------| +| **Multiple credit balance sources** | Plans, Usage, billingStore all fetch independently | +| **Fragmented billing pages** | PlansAndBillingPage, CreditPurchase, legacy routes | +| **Legacy routes still exist** | `/billing/overview`, `/team`, `/profile` all redirect | +| **No audit log across modules** | No record of who changed what when | +| **No notification preferences** | Cannot configure billing/usage email alerts | + +--- + +## 5. HELP Module + +### 5.1 Help & Docs + +**Route:** `/help` +**Files:** `pages/Help/HelpCenter.tsx`, `pages/Help/Documentation.tsx` (placeholder), `pages/Help/SystemTesting.tsx` (placeholder), `pages/Help/FunctionTesting.tsx` (placeholder) +**Routes:** `/help`, `/help/docs`, `/help/system-testing`, `/help/function-testing` + +#### Current Functionality +- Table of Contents with jump-to-section navigation +- Getting Started: Quick Start Guide, Workflow Overview +- Planner Module: Keywords, Clusters, Ideas documentation +- Writer Module: Tasks, Content, Images documentation +- Automation Setup overview +- FAQ section (~20 questions) +- Support CTA buttons (non-functional) + +#### Critical Gaps + +| Issue | Impact | Severity | +|-------|--------|----------| +| **Support dropdown link broken** | Goes to `/profile` which has NO route - 404 | 🔴 Critical | +| **Contact Support button does nothing** | ` )} {!canAddMoreSites && sites.length > 0 && maxSites > 0 && ( @@ -685,7 +682,7 @@ export default function Home() {
{/* Progress Flow - Circular Design with Progress Bar */} - + {/* Percentage and Progress Bar */}
@@ -702,9 +699,6 @@ export default function Home() { color="primary" className="h-4" /> -

- (This shows your progress from keywords through to published content) -

{/* Icon-based Progress Flow */} @@ -717,7 +711,7 @@ export default function Home() {
Site & Sectors
{sites.filter(s => s.active_sectors_count > 0).length}
-
Niches you're targeting - Industry & sectors set up
+
Industry & sectors configured
@@ -728,7 +722,7 @@ export default function Home() {
Keywords
{progress.keywordsCount}
-
Search terms to target - Keywords added from research
+
Added from opportunities
@@ -739,7 +733,7 @@ export default function Home() {
Clusters
{progress.clustersCount}
-
Topic groups - Keywords organized by theme
+
Keywords grouped by topic
@@ -750,7 +744,7 @@ export default function Home() {
Ideas
{progress.ideasCount}
-
Article outlines ready - Ideas and outlines created
+
Content ideas and outlines
@@ -761,7 +755,7 @@ export default function Home() {
Content
{progress.contentCount}
-
Articles created - Written content + images ready
+
Articles ready to publish
@@ -772,7 +766,7 @@ export default function Home() {
Published
{progress.publishedCount}
-
Live on your site - Articles published and active
+
Live on your site
@@ -859,7 +853,7 @@ export default function Home() { } accentColor="blue" trend={0} diff --git a/frontend/src/pages/Planner/Clusters.tsx b/frontend/src/pages/Planner/Clusters.tsx index bb134f30..30009195 100644 --- a/frontend/src/pages/Planner/Clusters.tsx +++ b/frontend/src/pages/Planner/Clusters.tsx @@ -444,15 +444,16 @@ export default function Clusters() { // Planner navigation tabs const plannerTabs = [ - { label: 'Keywords (individual terms)', path: '/planner/keywords', icon: }, - { label: 'Topics (keyword groups)', path: '/planner/clusters', icon: }, + { label: 'Keywords', path: '/planner/keywords', icon: }, + { label: 'Clusters', path: '/planner/clusters', icon: }, { label: 'Ideas', path: '/planner/ideas', icon: }, ]; return ( <> , color: 'purple' }} navigation={} workflowInsights={workflowInsights} diff --git a/frontend/src/pages/Planner/Ideas.tsx b/frontend/src/pages/Planner/Ideas.tsx index 2bd4fcc3..a5bcf25e 100644 --- a/frontend/src/pages/Planner/Ideas.tsx +++ b/frontend/src/pages/Planner/Ideas.tsx @@ -350,15 +350,16 @@ export default function Ideas() { // Planner navigation tabs const plannerTabs = [ - { label: 'Keywords (individual terms)', path: '/planner/keywords', icon: }, - { label: 'Topics (keyword groups)', path: '/planner/clusters', icon: }, + { label: 'Keywords', path: '/planner/keywords', icon: }, + { label: 'Clusters', path: '/planner/clusters', icon: }, { label: 'Ideas', path: '/planner/ideas', icon: }, ]; return ( <> , color: 'orange' }} navigation={} workflowInsights={workflowInsights} @@ -474,7 +475,7 @@ export default function Ideas() { { title: 'Clusters', value: clusters.length.toLocaleString(), - subtitle: 'topic groups', + subtitle: 'keyword groups', icon: , accentColor: 'purple', href: '/planner/clusters', diff --git a/frontend/src/pages/Planner/Keywords.tsx b/frontend/src/pages/Planner/Keywords.tsx index 2f7a025d..81cb3f3b 100644 --- a/frontend/src/pages/Planner/Keywords.tsx +++ b/frontend/src/pages/Planner/Keywords.tsx @@ -611,15 +611,16 @@ export default function Keywords() { // Planner navigation tabs const plannerTabs = [ - { label: 'Keywords (individual terms)', path: '/planner/keywords', icon: }, - { label: 'Topics (keyword groups)', path: '/planner/clusters', icon: }, + { label: 'Keywords', path: '/planner/keywords', icon: }, + { label: 'Clusters', path: '/planner/clusters', icon: }, { label: 'Ideas', path: '/planner/ideas', icon: }, ]; return ( <> , color: 'green' }} navigation={} workflowInsights={workflowInsights} diff --git a/frontend/src/pages/Writer/Content.tsx b/frontend/src/pages/Writer/Content.tsx index 58dcd51e..008e3e24 100644 --- a/frontend/src/pages/Writer/Content.tsx +++ b/frontend/src/pages/Writer/Content.tsx @@ -280,17 +280,18 @@ export default function Content() { // Writer navigation tabs const writerTabs = [ - { label: 'Ready to Write', path: '/writer/tasks', icon: }, - { label: 'Finished Drafts', path: '/writer/content', icon: }, - { label: 'Article Images', path: '/writer/images', icon: }, - { label: 'Review Before Publishing', path: '/writer/review', icon: }, + { label: 'Queue', path: '/writer/tasks', icon: }, + { label: 'Drafts', path: '/writer/content', icon: }, + { label: 'Images', path: '/writer/images', icon: }, + { label: 'Review', path: '/writer/review', icon: }, { label: 'Published', path: '/writer/published', icon: }, ]; return ( <> , color: 'purple' }} navigation={} workflowInsights={workflowInsights} diff --git a/frontend/src/pages/Writer/Images.tsx b/frontend/src/pages/Writer/Images.tsx index a1d75441..f3177b61 100644 --- a/frontend/src/pages/Writer/Images.tsx +++ b/frontend/src/pages/Writer/Images.tsx @@ -449,17 +449,17 @@ export default function Images() { // Writer navigation tabs const writerTabs = [ - { label: 'Ready to Write', path: '/writer/tasks', icon: }, - { label: 'Finished Drafts', path: '/writer/content', icon: }, - { label: 'Article Images', path: '/writer/images', icon: }, - { label: 'Review Before Publishing', path: '/writer/review', icon: }, + { label: 'Queue', path: '/writer/tasks', icon: }, + { label: 'Drafts', path: '/writer/content', icon: }, + { label: 'Images', path: '/writer/images', icon: }, + { label: 'Review', path: '/writer/review', icon: }, { label: 'Published', path: '/writer/published', icon: }, ]; return ( <> , color: 'orange' }} navigation={} /> diff --git a/frontend/src/pages/Writer/Published.tsx b/frontend/src/pages/Writer/Published.tsx index bc36348c..5febbfef 100644 --- a/frontend/src/pages/Writer/Published.tsx +++ b/frontend/src/pages/Writer/Published.tsx @@ -307,17 +307,17 @@ export default function Published() { // Writer navigation tabs const writerTabs = [ - { label: 'Ready to Write', path: '/writer/tasks', icon: }, - { label: 'Finished Drafts', path: '/writer/content', icon: }, - { label: 'Article Images', path: '/writer/images', icon: }, - { label: 'Review Before Publishing', path: '/writer/review', icon: }, + { label: 'Queue', path: '/writer/tasks', icon: }, + { label: 'Drafts', path: '/writer/content', icon: }, + { label: 'Images', path: '/writer/images', icon: }, + { label: 'Review', path: '/writer/review', icon: }, { label: 'Published', path: '/writer/published', icon: }, ]; return ( <> , color: 'green' }} navigation={} /> diff --git a/frontend/src/pages/Writer/Review.tsx b/frontend/src/pages/Writer/Review.tsx index d36a4dd3..7396353e 100644 --- a/frontend/src/pages/Writer/Review.tsx +++ b/frontend/src/pages/Writer/Review.tsx @@ -346,10 +346,10 @@ export default function Review() { // Writer navigation tabs const writerTabs = [ - { label: 'Ready to Write', path: '/writer/tasks', icon: }, - { label: 'Finished Drafts', path: '/writer/content', icon: }, - { label: 'Article Images', path: '/writer/images', icon: }, - { label: 'Review Before Publishing', path: '/writer/review', icon: }, + { label: 'Queue', path: '/writer/tasks', icon: }, + { label: 'Drafts', path: '/writer/content', icon: }, + { label: 'Images', path: '/writer/images', icon: }, + { label: 'Review', path: '/writer/review', icon: }, { label: 'Published', path: '/writer/published', icon: }, ]; diff --git a/frontend/src/pages/Writer/Tasks.tsx b/frontend/src/pages/Writer/Tasks.tsx index 615f7a0b..6b51e7b7 100644 --- a/frontend/src/pages/Writer/Tasks.tsx +++ b/frontend/src/pages/Writer/Tasks.tsx @@ -424,17 +424,18 @@ export default function Tasks() { // Writer navigation tabs const writerTabs = [ - { label: 'Ready to Write', path: '/writer/tasks', icon: }, - { label: 'Finished Drafts', path: '/writer/content', icon: }, - { label: 'Article Images', path: '/writer/images', icon: }, - { label: 'Review Before Publishing', path: '/writer/review', icon: }, + { label: 'Queue', path: '/writer/tasks', icon: }, + { label: 'Drafts', path: '/writer/content', icon: }, + { label: 'Images', path: '/writer/images', icon: }, + { label: 'Review', path: '/writer/review', icon: }, { label: 'Published', path: '/writer/published', icon: }, ]; return ( <> , color: 'indigo' }} navigation={} workflowInsights={workflowInsights} diff --git a/frontend/src/pages/account/AccountSettingsPage.tsx b/frontend/src/pages/account/AccountSettingsPage.tsx index 58b8d210..8d29f2c8 100644 --- a/frontend/src/pages/account/AccountSettingsPage.tsx +++ b/frontend/src/pages/account/AccountSettingsPage.tsx @@ -1,25 +1,41 @@ /** - * Account Settings Page - * Manage account information and billing address + * Account Settings Page - Consolidated Settings + * Tabs: Account, Profile, Team + * Consistent with system page structure (like Plans & Usage pages) */ import { useState, useEffect } from 'react'; -import { Save, Loader2 } from 'lucide-react'; +import { + Save, Loader2, Settings, User, Users, UserPlus, Shield, Lock +} from 'lucide-react'; import { Card } from '../../components/ui/card'; +import Button from '../../components/ui/button/Button'; +import Badge from '../../components/ui/badge/Badge'; +import PageMeta from '../../components/common/PageMeta'; +import { useToast } from '../../components/ui/toast/ToastContainer'; import { getAccountSettings, updateAccountSettings, + getTeamMembers, + inviteTeamMember, + removeTeamMember, type AccountSettings, + type TeamMember, } from '../../services/billing.api'; +type TabType = 'account' | 'profile' | 'team'; + export default function AccountSettingsPage() { - const [settings, setSettings] = useState(null); + const toast = useToast(); + const [activeTab, setActiveTab] = useState('account'); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); - const [formData, setFormData] = useState({ + // Account settings state + const [settings, setSettings] = useState(null); + const [accountForm, setAccountForm] = useState({ name: '', billing_address_line1: '', billing_address_line2: '', @@ -31,252 +47,730 @@ export default function AccountSettingsPage() { billing_email: '', }); + // Profile settings state + const [profileForm, setProfileForm] = useState({ + firstName: '', + lastName: '', + email: '', + phone: '', + timezone: 'America/New_York', + language: 'en', + emailNotifications: true, + marketingEmails: false, + }); + + // Team state + const [members, setMembers] = useState([]); + const [teamLoading, setTeamLoading] = useState(false); + const [showInviteModal, setShowInviteModal] = useState(false); + const [inviting, setInviting] = useState(false); + const [inviteForm, setInviteForm] = useState({ + email: '', + first_name: '', + last_name: '', + }); + useEffect(() => { - loadSettings(); + loadData(); }, []); - const loadSettings = async () => { + const loadData = async () => { try { setLoading(true); - const data = await getAccountSettings(); - setSettings(data); - setFormData({ - name: data.name || '', - billing_address_line1: data.billing_address_line1 || '', - billing_address_line2: data.billing_address_line2 || '', - billing_city: data.billing_city || '', - billing_state: data.billing_state || '', - billing_postal_code: data.billing_postal_code || '', - billing_country: data.billing_country || '', - tax_id: data.tax_id || '', - billing_email: data.billing_email || '', + const accountData = await getAccountSettings(); + setSettings(accountData); + setAccountForm({ + name: accountData.name || '', + billing_address_line1: accountData.billing_address_line1 || '', + billing_address_line2: accountData.billing_address_line2 || '', + billing_city: accountData.billing_city || '', + billing_state: accountData.billing_state || '', + billing_postal_code: accountData.billing_postal_code || '', + billing_country: accountData.billing_country || '', + tax_id: accountData.tax_id || '', + billing_email: accountData.billing_email || '', }); } catch (err: any) { - setError(err.message || 'Failed to load account settings'); - console.error('Account settings load error:', err); + setError(err.message || 'Failed to load settings'); } finally { setLoading(false); } }; - const handleSubmit = async (e: React.FormEvent) => { + const loadTeamMembers = async () => { + try { + setTeamLoading(true); + const data = await getTeamMembers(); + setMembers(data.results || []); + } catch (error: any) { + toast.error(`Failed to load team members: ${error.message}`); + } finally { + setTeamLoading(false); + } + }; + + // Load team members when team tab is selected + useEffect(() => { + if (activeTab === 'team' && members.length === 0) { + loadTeamMembers(); + } + }, [activeTab]); + + const handleAccountSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { setSaving(true); setError(''); setSuccess(''); - - await updateAccountSettings(formData); + await updateAccountSettings(accountForm); setSuccess('Account settings updated successfully'); - await loadSettings(); + toast.success('Account settings saved'); + await loadData(); } catch (err: any) { setError(err.message || 'Failed to update account settings'); + toast.error(err.message || 'Failed to save settings'); } finally { setSaving(false); } }; - const handleChange = (e: React.ChangeEvent) => { - setFormData(prev => ({ + const handleProfileSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + setSaving(true); + // TODO: Connect to profile API when available + await new Promise(resolve => setTimeout(resolve, 500)); + toast.success('Profile settings saved'); + } catch (err: any) { + toast.error(err.message || 'Failed to save profile'); + } finally { + setSaving(false); + } + }; + + const handleInvite = async () => { + if (!inviteForm.email) { + toast.error('Email is required'); + return; + } + + try { + setInviting(true); + const result = await inviteTeamMember(inviteForm); + toast.success(result.message || 'Team member invited successfully'); + setShowInviteModal(false); + setInviteForm({ email: '', first_name: '', last_name: '' }); + await loadTeamMembers(); + } catch (error: any) { + toast.error(`Failed to invite team member: ${error.message}`); + } finally { + setInviting(false); + } + }; + + const handleRemoveMember = async (userId: number, email: string) => { + if (!confirm(`Are you sure you want to remove ${email} from the team?`)) { + return; + } + + try { + const result = await removeTeamMember(userId); + toast.success(result.message || 'Team member removed successfully'); + await loadTeamMembers(); + } catch (error: any) { + toast.error(`Failed to remove team member: ${error.message}`); + } + }; + + const handleAccountChange = (e: React.ChangeEvent) => { + setAccountForm(prev => ({ ...prev, [e.target.name]: e.target.value })); }; + const tabs = [ + { id: 'account' as TabType, label: 'Account', icon: }, + { id: 'profile' as TabType, label: 'Profile', icon: }, + { id: 'team' as TabType, label: 'Team', icon: }, + ]; + if (loading) { return ( -
- +
+ +
+
+ +
Loading settings...
+
+
); } return ( -
+
+ + + {/* Page Header */}

Account Settings

- Manage your account information and billing details + Manage your account information, profile settings, and team members

- {error && ( -
-

{error}

-
- )} + {/* Tabs */} +
+ +
- {success && ( -
-

{success}

-
- )} - -
- {/* Account Information */} - -

Account Information

-
-
- - -
-
- - -
-
-
- - -
-
- - {/* Billing Address */} - -

Billing Address

-
-
- - -
-
- - -
-
-
- - + {/* Tab Content */} +
+ {/* Account Tab */} + {activeTab === 'account' && ( +
+ {error && ( +
+

{error}

-
- - -
-
- - -
-
-
- - -
-
- - - {/* Tax Information */} - -

Tax Information

-
- - -
-
- - {/* Submit Button */} -
- + + {success && ( +
+

{success}

+
+ )} + + + {/* Account Information */} + +

Account Information

+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + {/* Billing Address */} + +

Billing Address

+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + {/* Tax Information */} + +

Tax Information

+
+ + +
+
+ + {/* Submit Button */} +
+ +
+ +
+ )} + + {/* Profile Tab */} + {activeTab === 'profile' && ( +
+
+ +

About You

+
+
+ + setProfileForm({ ...profileForm, firstName: e.target.value })} + className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-[var(--color-brand-500)] dark:bg-gray-800" + /> +
+
+ + setProfileForm({ ...profileForm, lastName: e.target.value })} + className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-[var(--color-brand-500)] dark:bg-gray-800" + /> +
+
+ + setProfileForm({ ...profileForm, email: e.target.value })} + className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-[var(--color-brand-500)] dark:bg-gray-800" + /> +
+
+ + setProfileForm({ ...profileForm, phone: e.target.value })} + className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-[var(--color-brand-500)] dark:bg-gray-800" + /> +
+
+
+ + +

Preferences

+
+
+ + +
+
+ + +
+
+
+ + +

Notifications

+

+ Choose what emails you want to receive: +

+
+
+
+
Important Updates
+
+ Get notified about important changes to your account +
+
+ setProfileForm({ ...profileForm, emailNotifications: e.target.checked })} + className="w-5 h-5 text-[var(--color-brand-500)] rounded focus:ring-[var(--color-brand-500)]" + /> +
+
+
+
Tips & Product Updates
+
+ Hear about new features and content tips +
+
+ setProfileForm({ ...profileForm, marketingEmails: e.target.checked })} + className="w-5 h-5 text-[var(--color-brand-500)] rounded focus:ring-[var(--color-brand-500)]" + /> +
+
+
+ + +

+ + Security +

+ +
+ + {/* Submit Button */} +
+ +
+
+
+ )} + + {/* Team Tab */} + {activeTab === 'team' && ( +
+
+
+

Team Members

+

+ Manage who can access your account +

+
+ +
+ + {teamLoading ? ( +
+ +
+ ) : ( + +
+ + + + + + + + + + + + + {members.map((member) => ( + + + + + + + + + ))} + {members.length === 0 && ( + + + + )} + +
NameEmailStatusRoleJoinedActions
+ {member.first_name || member.last_name + ? `${member.first_name} ${member.last_name}`.trim() + : '-'} + + {member.email} + + + {member.is_active ? 'Active' : 'Inactive'} + + + {member.is_staff ? 'Admin' : 'Member'} + + {member.date_joined ? new Date(member.date_joined).toLocaleDateString() : 'N/A'} + + +
+ + No team members yet. Invite your first team member! +
+
+
+ )} + + {/* Role Permissions Info */} + +

+ + Role Permissions +

+
+
+
+

Admin

+ High Access +
+
    +
  • ✓ Manage all sites and content
  • +
  • ✓ Invite team members
  • +
  • ✗ Cannot manage billing
  • +
+
+
+
+

Member

+ Standard Access +
+
    +
  • ✓ Create and edit content
  • +
  • ✓ View analytics
  • +
  • ✗ Cannot invite users
  • +
+
+
+
+
+ )} +
+ + {/* Invite Modal */} + {showInviteModal && ( +
+ +

+ Invite Team Member +

+ +
+
+ + setInviteForm(prev => ({ ...prev, email: e.target.value }))} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white" + placeholder="user@example.com" + /> +
+ +
+ + setInviteForm(prev => ({ ...prev, first_name: e.target.value }))} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white" + /> +
+ +
+ + setInviteForm(prev => ({ ...prev, last_name: e.target.value }))} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white" + /> +
+
+ +
+ + +
+
- + )}
); } diff --git a/frontend/src/pages/account/ContentSettingsPage.tsx b/frontend/src/pages/account/ContentSettingsPage.tsx new file mode 100644 index 00000000..362d2fee --- /dev/null +++ b/frontend/src/pages/account/ContentSettingsPage.tsx @@ -0,0 +1,616 @@ +/** + * Content Settings Page - 3 Tabs + * Tabs: Content Generation, Publishing, Image Settings + * Consolidated settings for content creation workflow + */ + +import { useState, useEffect, useCallback } from 'react'; +import { + Save, Loader2, Image as ImageIcon, FileText, Send, Settings +} from 'lucide-react'; +import { Card } from '../../components/ui/card'; +import Button from '../../components/ui/button/Button'; +import { fetchAPI } from '../../services/api'; +import { useToast } from '../../components/ui/toast/ToastContainer'; +import SelectDropdown from '../../components/form/SelectDropdown'; +import Label from '../../components/form/Label'; +import Checkbox from '../../components/form/input/Checkbox'; +import PageMeta from '../../components/common/PageMeta'; + +type TabType = 'content' | 'publishing' | 'images'; + +interface ImageGenerationSettings { + enabled: boolean; + service: 'openai' | 'runware'; + provider: string; + model: string; + runwareModel?: string; + image_type: 'realistic' | 'artistic' | 'cartoon'; + max_in_article_images: number; + image_format: 'webp' | 'jpg' | 'png'; + desktop_enabled: boolean; + mobile_enabled: boolean; + featured_image_size: string; + desktop_image_size: string; +} + +interface PublishingSettings { + autoPublishEnabled: boolean; + autoSyncEnabled: boolean; +} + +interface ContentGenerationSettings { + appendToPrompt: string; + defaultTone: string; + defaultLength: string; +} + +// Map user-friendly quality to internal service/model configuration +const QUALITY_TO_CONFIG: Record = { + standard: { service: 'openai', model: 'dall-e-2' }, + premium: { service: 'openai', model: 'dall-e-3' }, + best: { service: 'runware', model: 'runware:97@1' }, +}; + +// Map internal config back to user-friendly quality +const getQualityFromConfig = (service?: string, model?: string): 'standard' | 'premium' | 'best' => { + if (service === 'runware') return 'best'; + if (model === 'dall-e-3') return 'premium'; + return 'standard'; +}; + +// Get available image sizes based on provider and model +const getImageSizes = (provider: string, model: string) => { + if (provider === 'runware') { + return [ + { value: '1280x832', label: '1280×832 pixels' }, + { value: '1024x1024', label: '1024×1024 pixels' }, + { value: '512x512', label: '512×512 pixels' }, + ]; + } else if (provider === 'openai') { + if (model === 'dall-e-2') { + return [ + { value: '256x256', label: '256×256 pixels' }, + { value: '512x512', label: '512×512 pixels' }, + { value: '1024x1024', label: '1024×1024 pixels' }, + ]; + } else if (model === 'dall-e-3') { + return [ + { value: '1024x1024', label: '1024×1024 pixels' }, + ]; + } + } + return [{ value: '1024x1024', label: '1024×1024 pixels' }]; +}; + +export default function ContentSettingsPage() { + const toast = useToast(); + const [activeTab, setActiveTab] = useState('content'); + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + + // Content Generation Settings + const [contentSettings, setContentSettings] = useState({ + appendToPrompt: '', + defaultTone: 'professional', + defaultLength: 'medium', + }); + + // Publishing Settings + const [publishingSettings, setPublishingSettings] = useState({ + autoPublishEnabled: false, + autoSyncEnabled: false, + }); + + // Image Quality + const [imageQuality, setImageQuality] = useState<'standard' | 'premium' | 'best'>('premium'); + + // Image Generation Settings + const [imageSettings, setImageSettings] = useState({ + enabled: true, + service: 'openai', + provider: 'openai', + model: 'dall-e-3', + image_type: 'realistic', + max_in_article_images: 2, + image_format: 'webp', + desktop_enabled: true, + mobile_enabled: true, + featured_image_size: '1024x1024', + desktop_image_size: '1024x1024', + }); + + // Get current provider/model from quality setting + const getCurrentConfig = useCallback(() => { + const config = QUALITY_TO_CONFIG[imageQuality]; + return { + service: config.service, + model: config.model, + }; + }, [imageQuality]); + + // Get available sizes for current quality + const availableSizes = getImageSizes( + getCurrentConfig().service, + getCurrentConfig().model + ); + + useEffect(() => { + loadSettings(); + }, []); + + // Update image sizes when quality changes + useEffect(() => { + const config = getCurrentConfig(); + const sizes = getImageSizes(config.service, config.model); + const defaultSize = sizes.length > 0 ? sizes[0].value : '1024x1024'; + + const validSizes = sizes.map(s => s.value); + const needsFeaturedUpdate = !validSizes.includes(imageSettings.featured_image_size); + const needsDesktopUpdate = !validSizes.includes(imageSettings.desktop_image_size); + + if (needsFeaturedUpdate || needsDesktopUpdate) { + setImageSettings(prev => ({ + ...prev, + service: config.service, + provider: config.service, + model: config.model, + featured_image_size: needsFeaturedUpdate ? defaultSize : prev.featured_image_size, + desktop_image_size: needsDesktopUpdate ? defaultSize : prev.desktop_image_size, + })); + } else { + setImageSettings(prev => ({ + ...prev, + service: config.service, + provider: config.service, + model: config.model, + })); + } + }, [imageQuality, getCurrentConfig]); + + const loadSettings = async () => { + try { + setLoading(true); + + // Load image generation settings + const imageData = await fetchAPI('/v1/system/settings/integrations/image_generation/'); + if (imageData) { + const quality = getQualityFromConfig(imageData.service || imageData.provider, imageData.model); + setImageQuality(quality); + + setImageSettings({ + enabled: imageData.enabled !== false, + service: imageData.service || imageData.provider || 'openai', + provider: imageData.provider || imageData.service || 'openai', + model: imageData.model || 'dall-e-3', + runwareModel: imageData.runwareModel, + image_type: imageData.image_type || 'realistic', + max_in_article_images: imageData.max_in_article_images || 2, + image_format: imageData.image_format || 'webp', + desktop_enabled: imageData.desktop_enabled !== false, + mobile_enabled: imageData.mobile_enabled !== false, + featured_image_size: imageData.featured_image_size || '1024x1024', + desktop_image_size: imageData.desktop_image_size || '1024x1024', + }); + } + + // TODO: Load content generation settings when API is available + // TODO: Load publishing settings when API is available + + } catch (error: any) { + console.error('Error loading content settings:', error); + } finally { + setLoading(false); + } + }; + + const handleSave = async () => { + try { + setSaving(true); + + if (activeTab === 'images') { + const config = getCurrentConfig(); + const configToSave = { + enabled: imageSettings.enabled, + service: config.service, + provider: config.service, + model: config.model, + runwareModel: config.service === 'runware' ? config.model : undefined, + image_type: imageSettings.image_type, + max_in_article_images: imageSettings.max_in_article_images, + image_format: imageSettings.image_format, + desktop_enabled: imageSettings.desktop_enabled, + mobile_enabled: imageSettings.mobile_enabled, + featured_image_size: imageSettings.featured_image_size, + desktop_image_size: imageSettings.desktop_image_size, + }; + + await fetchAPI('/v1/system/settings/integrations/image_generation/save/', { + method: 'POST', + body: JSON.stringify(configToSave), + }); + } + + // TODO: Save content generation settings when API is available + // TODO: Save publishing settings when API is available + + toast.success('Settings saved successfully'); + } catch (error: any) { + console.error('Error saving settings:', error); + toast.error(`Failed to save settings: ${error.message}`); + } finally { + setSaving(false); + } + }; + + const tabs = [ + { id: 'content' as TabType, label: 'Content Generation', icon: }, + { id: 'publishing' as TabType, label: 'Publishing', icon: }, + { id: 'images' as TabType, label: 'Image Settings', icon: }, + ]; + + if (loading) { + return ( +
+ +
+
+ +
Loading settings...
+
+
+
+ ); + } + + return ( +
+ + + {/* Page Header */} +
+

Content Settings

+

+ Configure how your content and images are generated +

+
+ + {/* Tabs */} +
+ +
+ + {/* Tab Content */} +
+ {/* Content Generation Tab */} + {activeTab === 'content' && ( +
+ +
+
+ +
+
+

Content Generation

+

Customize how your articles are written

+
+
+ +
+
+ +