From cb8e74738727510ee986e8ac4a36e6232c1468cc Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Mon, 5 Jan 2026 08:17:56 +0000 Subject: [PATCH] Phase 2, 2.1 and 2.2 complete --- docs/plans/FINAL-PRELAUNCH.md | 21 +- .../src/components/common/ComponentCard.tsx | 4 +- .../dashboard/AccountInfoWidget.tsx | 208 +++++++ .../dashboard/CreditsUsageWidget.tsx | 134 ++++ .../dashboard/SitesOverviewWidget.tsx | 171 ++++++ .../dashboard/WorkflowPipelineWidget.tsx | 32 +- .../onboarding/OnboardingWizard.tsx | 122 ++-- .../onboarding/steps/Step1Welcome.tsx | 139 +++-- .../onboarding/steps/Step2AddSite.tsx | 257 +++++--- .../steps/Step3ConnectIntegration.tsx | 291 +++++---- .../onboarding/steps/Step4AddKeywords.tsx | 577 ++++++++++++++++-- .../onboarding/steps/Step5Complete.tsx | 341 +++++++---- frontend/src/pages/Dashboard/Home.tsx | 53 +- frontend/src/pages/Setup/SetupWizard.tsx | 36 +- 14 files changed, 1834 insertions(+), 552 deletions(-) create mode 100644 frontend/src/components/dashboard/AccountInfoWidget.tsx create mode 100644 frontend/src/components/dashboard/CreditsUsageWidget.tsx create mode 100644 frontend/src/components/dashboard/SitesOverviewWidget.tsx diff --git a/docs/plans/FINAL-PRELAUNCH.md b/docs/plans/FINAL-PRELAUNCH.md index 2be42860..3fa972fc 100644 --- a/docs/plans/FINAL-PRELAUNCH.md +++ b/docs/plans/FINAL-PRELAUNCH.md @@ -28,7 +28,7 @@ --- # PHASE 1: App UI Quick Fixes -## 1.1 - Credits Display Fix +## 1.1 - Credits Display Fix ✅ **Location**: App header component @@ -38,7 +38,7 @@ --- -## 1.2 - Sites Card Redesign +## 1.2 - Sites Card Redesign ✅ **Location**: Sites listing/grid component @@ -50,7 +50,7 @@ --- -## 1.3 - Page Loading Standardization +## 1.3 - Page Loading Standardization ✅ **Current problem**: Each page has its own loading spinner/text implementation @@ -64,7 +64,7 @@ --- -## 1.4 - Global Layout Spacing +## 1.4 - Global Layout Spacing ✅ **Required**: - Define exact padding in global app layout ONLY: @@ -79,7 +79,7 @@ --- -## 1.5 - Button Standardization +## 1.5 - Button Standardization ✅ **Current problems**: 1. Icon-on-top bug: Some buttons render icon above text instead of inline @@ -111,7 +111,7 @@ grep -rn " = ({ {(title || desc || headerContent) && (
-

+

{title}

{desc && ( -

+

{desc}

)} diff --git a/frontend/src/components/dashboard/AccountInfoWidget.tsx b/frontend/src/components/dashboard/AccountInfoWidget.tsx new file mode 100644 index 00000000..c1bf6bcb --- /dev/null +++ b/frontend/src/components/dashboard/AccountInfoWidget.tsx @@ -0,0 +1,208 @@ +/** + * Account Info Widget + * Displays account-related billing information: + * - Credits consumed/remaining this billing period + * - Reset day (when credits refresh) + * - Last payment date + * - Next payment due date + * - Current plan/package type + */ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import ComponentCard from '../common/ComponentCard'; +import Button from '../ui/button/Button'; +import Badge from '../ui/badge/Badge'; +import { CreditBalance, Subscription, Plan } from '../../services/billing.api'; +import { + CalendarIcon, + CreditCardIcon, + UserCircleIcon, + ArrowRightIcon, +} from '../../icons'; + +interface AccountInfoWidgetProps { + balance: CreditBalance | null; + subscription?: Subscription | null; + plan?: Plan | null; + userPlan?: any; // Plan from user.account.plan + loading?: boolean; +} + +// Helper to format dates +function formatDate(dateStr: string | undefined): string { + if (!dateStr) return '—'; + try { + const date = new Date(dateStr); + return date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric' + }); + } catch { + return '—'; + } +} + +// Helper to get days until date +function getDaysUntil(dateStr: string | undefined): number | null { + if (!dateStr) return null; + try { + const date = new Date(dateStr); + const now = new Date(); + const diff = date.getTime() - now.getTime(); + return Math.ceil(diff / (1000 * 60 * 60 * 24)); + } catch { + return null; + } +} + +export default function AccountInfoWidget({ + balance, + subscription, + plan, + userPlan, + loading = false, +}: AccountInfoWidgetProps) { + const navigate = useNavigate(); + + if (loading) { + return ( + +
+ {[1, 2, 3, 4].map((i) => ( +
+ ))} +
+ + ); + } + + const periodEnd = subscription?.current_period_end; + const daysUntilReset = getDaysUntil(periodEnd); + + // Resolve plan - prefer explicit plan, then subscription plan, then userPlan + const currentPlan = plan || (subscription?.plan && typeof subscription.plan === 'object' ? subscription.plan : null) || userPlan; + + return ( + navigate('/account/settings')} + endIcon={} + > + Manage + + } + > +
+ {/* Current Plan */} +
+
+
+ +
+
+

+ Current Plan +

+

+ {currentPlan?.name || 'Free Plan'} +

+
+
+ {subscription?.status && ( + + {subscription.status} + + )} +
+ + {/* Info Grid */} +
+ {/* Credits This Period */} +
+

+ Used This Period +

+

+ {balance?.credits_used_this_month?.toLocaleString() || '0'} +

+

+ of {balance?.plan_credits_per_month?.toLocaleString() || '0'} credits +

+
+ + {/* Credits Remaining */} +
+

+ Remaining +

+

+ {balance?.credits_remaining?.toLocaleString() || '0'} +

+

+ credits available +

+
+ + {/* Reset Date */} +
+
+ +

+ Credits Reset +

+
+

+ {formatDate(periodEnd)} +

+ {daysUntilReset !== null && daysUntilReset > 0 && ( +

+ in {daysUntilReset} day{daysUntilReset !== 1 ? 's' : ''} +

+ )} +
+ + {/* Next Payment */} +
+
+ +

+ Next Payment +

+
+

+ {formatDate(periodEnd)} +

+ {currentPlan?.price && ( +

+ ${currentPlan.price}/mo +

+ )} +
+
+ + {/* Upgrade CTA (if on free or lower plan) */} + {(!currentPlan || currentPlan.slug === 'free' || currentPlan.slug === 'starter') && ( + + )} +
+
+ ); +} diff --git a/frontend/src/components/dashboard/CreditsUsageWidget.tsx b/frontend/src/components/dashboard/CreditsUsageWidget.tsx new file mode 100644 index 00000000..8cedcd82 --- /dev/null +++ b/frontend/src/components/dashboard/CreditsUsageWidget.tsx @@ -0,0 +1,134 @@ +/** + * Credits Usage Widget + * Displays remaining credits, AI runs count, and visual progress indicator + */ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import ComponentCard from '../common/ComponentCard'; +import Button from '../ui/button/Button'; +import { CreditBalance } from '../../services/billing.api'; +import { BoltIcon, PlusIcon } from '../../icons'; + +interface CreditsUsageWidgetProps { + balance: CreditBalance | null; + aiOperations?: { + total: number; + period: string; + }; + loading?: boolean; +} + +export default function CreditsUsageWidget({ + balance, + aiOperations, + loading = false, +}: CreditsUsageWidgetProps) { + const navigate = useNavigate(); + + if (loading || !balance) { + return ( + +
+
+
+
+ + ); + } + + const usagePercentage = balance.plan_credits_per_month > 0 + ? (balance.credits_used_this_month / balance.plan_credits_per_month) * 100 + : 0; + + const remainingPercentage = 100 - usagePercentage; + + // Determine color based on remaining credits + const getProgressColor = () => { + if (remainingPercentage > 50) return 'bg-success-500'; + if (remainingPercentage > 25) return 'bg-warning-500'; + return 'bg-error-500'; + }; + + return ( + navigate('/account/credits')} + startIcon={} + > + Buy More + + } + > +
+ {/* Main Credit Display */} +
+
+

+ Available Credits +

+

+ {balance.credits.toLocaleString()} +

+
+
50 + ? 'bg-success-100 dark:bg-success-900/30 text-success-600 dark:text-success-400' + : remainingPercentage > 25 + ? 'bg-warning-100 dark:bg-warning-900/30 text-warning-600 dark:text-warning-400' + : 'bg-error-100 dark:bg-error-900/30 text-error-600 dark:text-error-400' + }`}> + +
+
+ + {/* Usage Progress Bar */} +
+
+ Monthly Usage + + {balance.credits_used_this_month.toLocaleString()} / {balance.plan_credits_per_month.toLocaleString()} + +
+
+
+
+
+ {usagePercentage.toFixed(1)}% used + {balance.credits_remaining.toLocaleString()} remaining +
+
+ + {/* AI Operations Summary */} + {aiOperations && ( +
+
+
+

+ AI Operations ({aiOperations.period}) +

+

+ {aiOperations.total.toLocaleString()} +

+
+ +
+
+ )} +
+ + ); +} diff --git a/frontend/src/components/dashboard/SitesOverviewWidget.tsx b/frontend/src/components/dashboard/SitesOverviewWidget.tsx new file mode 100644 index 00000000..03ff9e75 --- /dev/null +++ b/frontend/src/components/dashboard/SitesOverviewWidget.tsx @@ -0,0 +1,171 @@ +/** + * Sites Overview Widget + * Compact display of sites with status, stats, and quick action buttons + */ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import ComponentCard from '../common/ComponentCard'; +import Button from '../ui/button/Button'; +import IconButton from '../ui/button/IconButton'; +import Badge from '../ui/badge/Badge'; +import { Site } from '../../services/api'; +import { + SettingsIcon, + EyeIcon, + FileIcon, + PlusIcon, + CheckCircleIcon, +} from '../../icons'; + +interface SitesOverviewWidgetProps { + sites: Site[]; + loading?: boolean; + onAddSite?: () => void; + maxSites?: number; +} + +export default function SitesOverviewWidget({ + sites, + loading = false, + onAddSite, + maxSites = 0, +}: SitesOverviewWidgetProps) { + const navigate = useNavigate(); + const canAddMoreSites = maxSites === 0 || sites.length < maxSites; + + if (loading) { + return ( + +
+ {[1, 2].map((i) => ( +
+ ))} +
+ + ); + } + + return ( + } + > + Add Site + + ) + } + > + {sites.length === 0 ? ( +
+

+ No sites configured yet +

+ {onAddSite && ( + + )} +
+ ) : ( +
+ {sites.slice(0, 5).map((site) => ( +
+
+ {/* Status Indicator */} +
+ + {/* Site Info */} +
+
+

+ {site.name} +

+ + {site.is_active ? 'Active' : 'Inactive'} + +
+ {site.domain && ( +

+ {site.domain} +

+ )} +
+ + {/* Quick Stats */} +
+ {site.active_sectors_count !== undefined && ( + + {site.active_sectors_count} sectors + + )} + {(site as any).page_count !== undefined && ( + + {(site as any).page_count} pages + + )} +
+
+ + {/* Action Buttons */} +
+ navigate(`/sites/${site.id}`)} + icon={} + aria-label="View Dashboard" + /> + navigate(`/sites/${site.id}/content`)} + icon={} + aria-label="View Content" + /> + navigate(`/sites/${site.id}/settings`)} + icon={} + aria-label="Site Settings" + /> +
+
+ ))} + + {sites.length > 5 && ( + + )} +
+ )} + + ); +} diff --git a/frontend/src/components/dashboard/WorkflowPipelineWidget.tsx b/frontend/src/components/dashboard/WorkflowPipelineWidget.tsx index f251a31a..4e669651 100644 --- a/frontend/src/components/dashboard/WorkflowPipelineWidget.tsx +++ b/frontend/src/components/dashboard/WorkflowPipelineWidget.tsx @@ -55,12 +55,12 @@ const stages = [ { key: 'published', label: 'Published', icon: PaperPlaneIcon, href: '/writer/published', gradient: 'from-success-500 to-success-600' }, ] as const; -// Small filled arrow triangle component +// Filled arrow triangle component - positioned at end of flex item function ArrowTip() { return ( -
- - +
+ +
); @@ -71,26 +71,32 @@ export default function WorkflowPipelineWidget({ data, loading }: WorkflowPipeli
{/* Header */}
-

- Workflow Pipeline -

+
+

+ Workflow Pipeline +

+

+ Content creation progress +

+
{data.completionPercentage}%
{/* Pipeline Flow - Single Balanced Row */} -
+
{stages.map((stage, index) => { const Icon = stage.icon; const count = data[stage.key as keyof PipelineData]; const isTransparent = 'transparent' in stage && stage.transparent; + const isLast = index === stages.length - 1; return ( -
+
- {index < stages.length - 1 && } + {!isLast && ( +
+ +
+ )}
); })} diff --git a/frontend/src/components/onboarding/OnboardingWizard.tsx b/frontend/src/components/onboarding/OnboardingWizard.tsx index 8d3ea2a7..5635b823 100644 --- a/frontend/src/components/onboarding/OnboardingWizard.tsx +++ b/frontend/src/components/onboarding/OnboardingWizard.tsx @@ -52,7 +52,7 @@ export interface WizardData { const STEPS: WizardStep[] = [ { id: 1, title: 'Welcome', description: 'Get started with IGNY8' }, { id: 2, title: 'Add Site', description: 'Create your first site' }, - { id: 3, title: 'Connect', description: 'Install WordPress plugin', isOptional: true }, + { id: 3, title: 'Connect', description: 'Connect your site', isOptional: true }, { id: 4, title: 'Keywords', description: 'Add target keywords', isOptional: true }, { id: 5, title: 'Complete', description: 'You\'re all set!' }, ]; @@ -146,6 +146,8 @@ export default function OnboardingWizard({ onComplete, onSkip }: OnboardingWizar ); case 2: @@ -156,6 +158,8 @@ export default function OnboardingWizard({ onComplete, onSkip }: OnboardingWizar onNext={handleNext} onBack={handleBack} setIsLoading={setIsLoading} + currentStep={currentStep} + totalSteps={STEPS.length} /> ); case 3: @@ -166,6 +170,8 @@ export default function OnboardingWizard({ onComplete, onSkip }: OnboardingWizar onNext={handleNext} onBack={handleBack} onSkip={handleSkipStep} + currentStep={currentStep} + totalSteps={STEPS.length} /> ); case 4: @@ -176,6 +182,8 @@ export default function OnboardingWizard({ onComplete, onSkip }: OnboardingWizar onNext={handleNext} onBack={handleBack} onSkip={handleSkipStep} + currentStep={currentStep} + totalSteps={STEPS.length} /> ); case 5: @@ -184,6 +192,8 @@ export default function OnboardingWizard({ onComplete, onSkip }: OnboardingWizar data={wizardData} onComplete={handleComplete} isLoading={isLoading} + currentStep={currentStep} + totalSteps={STEPS.length} /> ); default: @@ -192,86 +202,58 @@ export default function OnboardingWizard({ onComplete, onSkip }: OnboardingWizar }; return ( -
- - {/* Header */} -
-
-
-
- -
-
-

- Getting Started -

-

- Step {currentStep} of {STEPS.length} -

-
-
- } - /> -
- - {/* Progress Bar */} -
-
- {STEPS.map((step) => ( +
+ {/* Progress Steps - Clean horizontal stepper */} +
+
+ {STEPS.map((step, index) => ( + +
-
- {step.id < currentStep ? ( - - ) : ( - step.id - )} -
- {step.id < STEPS.length && ( -
+ {step.id < currentStep ? ( + + ) : ( + step.id )}
- ))} -
-
- {STEPS.map((step) => ( {step.title} - ))} -
-
+
+ {index < STEPS.length - 1 && ( +
+ )} + + ))}
- +
+ + {/* Step Content Card */} + {/* Step Content */} -
+
{renderStepContent()}
diff --git a/frontend/src/components/onboarding/steps/Step1Welcome.tsx b/frontend/src/components/onboarding/steps/Step1Welcome.tsx index c6be7622..b415bda4 100644 --- a/frontend/src/components/onboarding/steps/Step1Welcome.tsx +++ b/frontend/src/components/onboarding/steps/Step1Welcome.tsx @@ -1,6 +1,6 @@ /** * Step 1: Welcome - * Introduction screen for new users + * Redesigned intro with cleaner cards explaining what each step accomplishes */ import React from 'react'; import Button from '../../ui/button/Button'; @@ -9,99 +9,126 @@ import { BoltIcon, FileTextIcon, PlugInIcon, - PieChartIcon, + CheckCircleIcon, } from '../../../icons'; interface Step1WelcomeProps { onNext: () => void; onSkip: () => void; + currentStep: number; + totalSteps: number; } -const FEATURES = [ +const WIZARD_STEPS = [ { - icon: , - title: 'AI Content Creation', - description: 'Generate high-quality articles with AI assistance', + step: 1, + icon: , + title: 'Add Your Site', + description: 'Set up your first site with industry preferences', + outcome: 'A configured site ready for content generation', + color: 'brand', }, { - icon: , - title: 'WordPress Integration', - description: 'Publish directly to your WordPress site', + step: 2, + icon: , + title: 'Connect Site', + description: 'Install our plugin to enable direct publishing', + outcome: 'One-click publishing to your site', + color: 'success', + optional: true, }, { - icon: , - title: 'Automated Pipeline', - description: 'Set it and forget it content scheduling', - }, - { - icon: , - title: 'Smart Analytics', - description: 'Track content performance and optimize', + step: 3, + icon: , + title: 'Add Keywords', + description: 'Define target keywords for AI content', + outcome: 'Keywords ready for clustering and ideas', + color: 'warning', + optional: true, }, ]; -export default function Step1Welcome({ onNext, onSkip }: Step1WelcomeProps) { +export default function Step1Welcome({ onNext, onSkip, currentStep, totalSteps }: Step1WelcomeProps) { return ( -
+
{/* Hero Section */} -
-
- -
-

+
+

Welcome to IGNY8

-

- Your complete AI-powered content creation and publishing platform. - Let's get you set up in just a few minutes. +

+ Let's set up your AI-powered content pipeline in just a few steps. + You'll be creating SEO-optimized content in minutes.

- {/* Features Grid */} -
- {FEATURES.map((feature, index) => ( -
-
- {feature.icon} + {/* What You'll Accomplish - Horizontal Cards */} +
+

+ What we'll set up together +

+
+ {WIZARD_STEPS.map((item) => ( +
+
+
+ {item.icon} +
+ {item.optional && ( + + Optional + + )} +
+

+ {item.title} +

+

+ {item.description} +

+
+ + {item.outcome} +
-

- {feature.title} -

-

- {feature.description} -

-
- ))} + ))} +
- {/* What's Next */} -
-

- What we'll do together: -

-
    -
  • ✓ Create your first site with optimized defaults
  • -
  • ✓ Connect your WordPress installation
  • -
  • ✓ Add keywords to start your content pipeline
  • -
+ {/* Time Estimate */} +
+

+ ⏱️ Estimated time: 3-5 minutes +

{/* Actions */} -
+
+ + Step {currentStep} of {totalSteps} + diff --git a/frontend/src/components/onboarding/steps/Step2AddSite.tsx b/frontend/src/components/onboarding/steps/Step2AddSite.tsx index 70587721..b9ea10f8 100644 --- a/frontend/src/components/onboarding/steps/Step2AddSite.tsx +++ b/frontend/src/components/onboarding/steps/Step2AddSite.tsx @@ -14,6 +14,8 @@ import { PageIcon, GridIcon, CheckCircleIcon, + LockIcon, + PlusIcon, } from '../../../icons'; import { fetchIndustries, @@ -32,6 +34,8 @@ interface Step2AddSiteProps { onNext: () => void; onBack: () => void; setIsLoading: (loading: boolean) => void; + currentStep: number; + totalSteps: number; } export default function Step2AddSite({ @@ -39,7 +43,9 @@ export default function Step2AddSite({ updateData, onNext, onBack, - setIsLoading + setIsLoading, + currentStep, + totalSteps, }: Step2AddSiteProps) { const toast = useToast(); @@ -49,6 +55,9 @@ export default function Step2AddSite({ const [isCreating, setIsCreating] = useState(false); const [error, setError] = useState(null); + // Check if site is already created (locked state) + const isSiteCreated = !!data.createdSiteId; + // Load industries on mount useEffect(() => { const loadIndustries = async () => { @@ -56,6 +65,12 @@ export default function Step2AddSite({ setLoadingIndustries(true); const response = await fetchIndustries(); setIndustries(response.industries || []); + + // Restore selected industry if data has industrySlug + if (data.industrySlug && response.industries) { + const industry = response.industries.find((i: Industry) => i.slug === data.industrySlug); + if (industry) setSelectedIndustry(industry); + } } catch (err: any) { if (err?.status !== 429) { console.error('Failed to load industries:', err); @@ -65,7 +80,7 @@ export default function Step2AddSite({ } }; loadIndustries(); - }, []); + }, [data.industrySlug]); // Get available sectors for selected industry const availableSectors = useMemo(() => { @@ -74,6 +89,7 @@ export default function Step2AddSite({ }, [selectedIndustry]); const handleIndustrySelect = (industry: Industry) => { + if (isSiteCreated) return; // Don't allow changes if site is created setSelectedIndustry(industry); updateData({ industryId: industry.id || null, @@ -83,6 +99,7 @@ export default function Step2AddSite({ }; const handleSectorToggle = (sectorSlug: string) => { + if (isSiteCreated) return; // Don't allow changes if site is created const newSectors = data.selectedSectors.includes(sectorSlug) ? data.selectedSectors.filter(s => s !== sectorSlug) : [...data.selectedSectors, sectorSlug]; @@ -139,10 +156,33 @@ export default function Step2AddSite({ // Update wizard data with created site updateData({ createdSiteId: newSite.id }); - toast.success(`Site "${data.siteName.trim()}" created successfully!`); + toast.success(`Site "${data.siteName.trim()}" added successfully!`); onNext(); } catch (err: any) { - setError(err.message || 'Failed to create site'); + // Parse error message to show user-friendly text + let errorMessage = 'Failed to add site. Please try again.'; + + if (err.message) { + const msg = err.message.toLowerCase(); + if (msg.includes('name') && (msg.includes('exist') || msg.includes('unique') || msg.includes('duplicate'))) { + errorMessage = 'A site with this name already exists. Please choose a different name.'; + } else if (msg.includes('domain') && (msg.includes('exist') || msg.includes('unique') || msg.includes('duplicate'))) { + errorMessage = 'A site with this domain already exists. Please use a different URL.'; + } else if (msg.includes('industry')) { + errorMessage = 'Please select a valid industry.'; + } else if (msg.includes('sector')) { + errorMessage = 'Please select at least one valid sector.'; + } else if (msg.includes('permission') || msg.includes('unauthorized')) { + errorMessage = 'You do not have permission to create sites. Please contact support.'; + } else if (msg.includes('limit') || msg.includes('quota') || msg.includes('maximum')) { + errorMessage = 'You have reached the maximum number of sites allowed on your plan.'; + } else if (!msg.includes('internal') && !msg.includes('500') && msg.length < 200) { + // Use the original message if it's not an internal error and is reasonable length + errorMessage = err.message; + } + } + + setError(errorMessage); } finally { setIsCreating(false); setIsLoading(false); @@ -151,11 +191,11 @@ export default function Step2AddSite({ return (
-
-

+
+

Add Your First Site

-

+

We'll set up your site with optimized defaults for automated content publishing.

@@ -168,77 +208,103 @@ export default function Step2AddSite({ /> )} - {/* Site Name */} -
- - ) => updateData({ siteName: e.target.value })} - placeholder="My Awesome Blog" - className="w-full px-4 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-transparent" + {/* Site Added Lock Notice */} + {isSiteCreated && ( + -
+ )} - {/* Website URL */} -
- -
- + {/* Form Fields - 3 in a row */} +
+ {/* Site Name */} +
+ ) => updateData({ domain: e.target.value })} - placeholder="https://mysite.com" - className="w-full pl-10 pr-4 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-transparent" + value={data.siteName} + onChange={(e: React.ChangeEvent) => updateData({ siteName: e.target.value })} + placeholder="My Awesome Blog" + disabled={isSiteCreated} + className={`w-full px-4 py-3 text-base border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-transparent ${isSiteCreated ? 'opacity-60 cursor-not-allowed' : ''}`} />
-
- {/* Industry Selection */} -
- - {loadingIndustries ? ( -
Loading industries...
- ) : ( - ({ value: i.slug, label: i.name }))} - value={selectedIndustry?.slug || ''} - onChange={(value) => { - const industry = industries.find(i => i.slug === value); - if (industry) handleIndustrySelect(industry); - }} - placeholder="Select an industry" - /> - )} + {/* Website URL */} +
+ +
+ + ) => updateData({ domain: e.target.value })} + placeholder="https://mysite.com" + disabled={isSiteCreated} + className={`w-full pl-11 pr-4 py-3 text-base border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-transparent ${isSiteCreated ? 'opacity-60 cursor-not-allowed' : ''}`} + /> +
+
+ + {/* Industry Selection */} +
+ + {loadingIndustries ? ( +
Loading...
+ ) : ( + ({ value: i.slug, label: i.name }))} + value={selectedIndustry?.slug || ''} + onChange={(value) => { + const industry = industries.find(i => i.slug === value); + if (industry) handleIndustrySelect(industry); + }} + placeholder="Select industry" + disabled={isSiteCreated} + className="text-base [&_button]:py-3 [&_button]:text-base" + /> + )} +
{/* Sector Selection */} {selectedIndustry && availableSectors.length > 0 && (
-