feat: Add No Website button to onboarding Step 2 with business description form
This commit is contained in:
@@ -685,3 +685,76 @@ async def get_user_writing_personas(user_id: int = 1):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting user personas: {str(e)}")
|
logger.error(f"Error getting user personas: {str(e)}")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
raise HTTPException(status_code=500, detail="Internal server error")
|
||||||
|
|
||||||
|
# Business Information endpoints
|
||||||
|
async def save_business_info(business_info: 'BusinessInfoRequest'):
|
||||||
|
"""Save business information for users without websites."""
|
||||||
|
try:
|
||||||
|
from models.business_info_request import BusinessInfoRequest
|
||||||
|
from services.business_info_service import business_info_service
|
||||||
|
|
||||||
|
logger.info(f"🔄 Saving business info for user_id: {business_info.user_id}")
|
||||||
|
result = business_info_service.save_business_info(business_info)
|
||||||
|
logger.success(f"✅ Business info saved successfully for user_id: {business_info.user_id}")
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Error saving business info: {str(e)}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to save business info: {str(e)}")
|
||||||
|
|
||||||
|
async def get_business_info(business_info_id: int):
|
||||||
|
"""Get business information by ID."""
|
||||||
|
try:
|
||||||
|
from services.business_info_service import business_info_service
|
||||||
|
|
||||||
|
logger.info(f"🔄 Getting business info for ID: {business_info_id}")
|
||||||
|
result = business_info_service.get_business_info(business_info_id)
|
||||||
|
if result:
|
||||||
|
logger.success(f"✅ Business info retrieved for ID: {business_info_id}")
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
logger.warning(f"⚠️ No business info found for ID: {business_info_id}")
|
||||||
|
raise HTTPException(status_code=404, detail="Business info not found")
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Error getting business info: {str(e)}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to get business info: {str(e)}")
|
||||||
|
|
||||||
|
async def get_business_info_by_user(user_id: int):
|
||||||
|
"""Get business information by user ID."""
|
||||||
|
try:
|
||||||
|
from services.business_info_service import business_info_service
|
||||||
|
|
||||||
|
logger.info(f"🔄 Getting business info for user ID: {user_id}")
|
||||||
|
result = business_info_service.get_business_info_by_user(user_id)
|
||||||
|
if result:
|
||||||
|
logger.success(f"✅ Business info retrieved for user ID: {user_id}")
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
logger.warning(f"⚠️ No business info found for user ID: {user_id}")
|
||||||
|
raise HTTPException(status_code=404, detail="Business info not found")
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Error getting business info: {str(e)}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to get business info: {str(e)}")
|
||||||
|
|
||||||
|
async def update_business_info(business_info_id: int, business_info: 'BusinessInfoRequest'):
|
||||||
|
"""Update business information."""
|
||||||
|
try:
|
||||||
|
from models.business_info_request import BusinessInfoRequest
|
||||||
|
from services.business_info_service import business_info_service
|
||||||
|
|
||||||
|
logger.info(f"🔄 Updating business info for ID: {business_info_id}")
|
||||||
|
result = business_info_service.update_business_info(business_info_id, business_info)
|
||||||
|
if result:
|
||||||
|
logger.success(f"✅ Business info updated for ID: {business_info_id}")
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
logger.warning(f"⚠️ No business info found to update for ID: {business_info_id}")
|
||||||
|
raise HTTPException(status_code=404, detail="Business info not found")
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Error updating business info: {str(e)}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to update business info: {str(e)}")
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ from api.onboarding import (
|
|||||||
get_onboarding_summary,
|
get_onboarding_summary,
|
||||||
get_website_analysis_data,
|
get_website_analysis_data,
|
||||||
get_research_preferences_data,
|
get_research_preferences_data,
|
||||||
|
save_business_info,
|
||||||
|
get_business_info,
|
||||||
|
get_business_info_by_user,
|
||||||
|
update_business_info,
|
||||||
StepCompletionRequest,
|
StepCompletionRequest,
|
||||||
APIKeyRequest
|
APIKeyRequest
|
||||||
)
|
)
|
||||||
@@ -433,6 +437,45 @@ async def research_preferences_data():
|
|||||||
logger.error(f"Error in research_preferences_data: {e}")
|
logger.error(f"Error in research_preferences_data: {e}")
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
# Business Information endpoints
|
||||||
|
@app.post("/api/onboarding/business-info")
|
||||||
|
async def business_info_save(request: 'BusinessInfoRequest'):
|
||||||
|
"""Save business information for users without websites."""
|
||||||
|
try:
|
||||||
|
from models.business_info_request import BusinessInfoRequest
|
||||||
|
return await save_business_info(request)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in business_info_save: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.get("/api/onboarding/business-info/{business_info_id}")
|
||||||
|
async def business_info_get(business_info_id: int):
|
||||||
|
"""Get business information by ID."""
|
||||||
|
try:
|
||||||
|
return await get_business_info(business_info_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in business_info_get: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.get("/api/onboarding/business-info/user/{user_id}")
|
||||||
|
async def business_info_get_by_user(user_id: int):
|
||||||
|
"""Get business information by user ID."""
|
||||||
|
try:
|
||||||
|
return await get_business_info_by_user(user_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in business_info_get_by_user: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.put("/api/onboarding/business-info/{business_info_id}")
|
||||||
|
async def business_info_update(business_info_id: int, request: 'BusinessInfoRequest'):
|
||||||
|
"""Update business information."""
|
||||||
|
try:
|
||||||
|
from models.business_info_request import BusinessInfoRequest
|
||||||
|
return await update_business_info(business_info_id, request)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in business_info_update: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
# Include component logic router
|
# Include component logic router
|
||||||
app.include_router(component_logic_router)
|
app.include_router(component_logic_router)
|
||||||
|
|
||||||
|
|||||||
24
backend/models/business_info_request.py
Normal file
24
backend/models/business_info_request.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"""Business Information Request Models for ALwrity backend."""
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import Optional
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class BusinessInfoRequest(BaseModel):
|
||||||
|
user_id: Optional[int] = None
|
||||||
|
business_description: str = Field(..., min_length=10, max_length=1000, description="Description of the business")
|
||||||
|
industry: Optional[str] = Field(None, max_length=100, description="Industry sector")
|
||||||
|
target_audience: Optional[str] = Field(None, max_length=500, description="Target audience description")
|
||||||
|
business_goals: Optional[str] = Field(None, max_length=1000, description="Business goals and objectives")
|
||||||
|
|
||||||
|
class BusinessInfoResponse(BaseModel):
|
||||||
|
id: int
|
||||||
|
user_id: Optional[int]
|
||||||
|
business_description: str
|
||||||
|
industry: Optional[str]
|
||||||
|
target_audience: Optional[str]
|
||||||
|
business_goals: Optional[str]
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
38
backend/models/user_business_info.py
Normal file
38
backend/models/user_business_info.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
"""User Business Information Model for ALwrity backend."""
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy import Column, Integer, String, Text, DateTime, func
|
||||||
|
from loguru import logger
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
logger.info("🔄 Loading UserBusinessInfo model...")
|
||||||
|
|
||||||
|
class UserBusinessInfo(Base):
|
||||||
|
__tablename__ = 'user_business_info'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
user_id = Column(Integer, index=True, nullable=True)
|
||||||
|
business_description = Column(Text, nullable=False)
|
||||||
|
industry = Column(String(100), nullable=True)
|
||||||
|
target_audience = Column(Text, nullable=True)
|
||||||
|
business_goals = Column(Text, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=func.now())
|
||||||
|
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<UserBusinessInfo(id={self.id}, user_id={self.user_id}, industry='{self.industry}')>"
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"user_id": self.user_id,
|
||||||
|
"business_description": self.business_description,
|
||||||
|
"industry": self.industry,
|
||||||
|
"target_audience": self.target_audience,
|
||||||
|
"business_goals": self.business_goals,
|
||||||
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||||
|
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("✅ UserBusinessInfo model loaded successfully!")
|
||||||
84
backend/services/business_info_service.py
Normal file
84
backend/services/business_info_service.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
"""Business Information Service for ALwrity backend."""
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from models.user_business_info import UserBusinessInfo
|
||||||
|
from models.business_info_request import BusinessInfoRequest, BusinessInfoResponse
|
||||||
|
from services.database import get_db
|
||||||
|
from loguru import logger
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
logger.info("🔄 Loading BusinessInfoService...")
|
||||||
|
|
||||||
|
class BusinessInfoService:
|
||||||
|
def __init__(self):
|
||||||
|
logger.info("🆕 Initializing BusinessInfoService...")
|
||||||
|
|
||||||
|
def save_business_info(self, business_info: BusinessInfoRequest) -> BusinessInfoResponse:
|
||||||
|
db: Session = next(get_db())
|
||||||
|
logger.debug(f"Attempting to save business info for user_id: {business_info.user_id}")
|
||||||
|
|
||||||
|
# Check if business info already exists for this user
|
||||||
|
existing_info = db.query(UserBusinessInfo).filter(UserBusinessInfo.user_id == business_info.user_id).first()
|
||||||
|
|
||||||
|
if existing_info:
|
||||||
|
logger.info(f"Existing business info found for user_id {business_info.user_id}, updating it.")
|
||||||
|
existing_info.business_description = business_info.business_description
|
||||||
|
existing_info.industry = business_info.industry
|
||||||
|
existing_info.target_audience = business_info.target_audience
|
||||||
|
existing_info.business_goals = business_info.business_goals
|
||||||
|
db.commit()
|
||||||
|
db.refresh(existing_info)
|
||||||
|
logger.success(f"Updated business info for user_id {business_info.user_id}, ID: {existing_info.id}")
|
||||||
|
return BusinessInfoResponse(**existing_info.to_dict())
|
||||||
|
else:
|
||||||
|
logger.info(f"No existing business info for user_id {business_info.user_id}, creating new entry.")
|
||||||
|
db_business_info = UserBusinessInfo(
|
||||||
|
user_id=business_info.user_id,
|
||||||
|
business_description=business_info.business_description,
|
||||||
|
industry=business_info.industry,
|
||||||
|
target_audience=business_info.target_audience,
|
||||||
|
business_goals=business_info.business_goals
|
||||||
|
)
|
||||||
|
db.add(db_business_info)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_business_info)
|
||||||
|
logger.success(f"Saved new business info for user_id {business_info.user_id}, ID: {db_business_info.id}")
|
||||||
|
return BusinessInfoResponse(**db_business_info.to_dict())
|
||||||
|
|
||||||
|
def get_business_info(self, business_info_id: int) -> Optional[BusinessInfoResponse]:
|
||||||
|
db: Session = next(get_db())
|
||||||
|
logger.debug(f"Retrieving business info by ID: {business_info_id}")
|
||||||
|
business_info = db.query(UserBusinessInfo).filter(UserBusinessInfo.id == business_info_id).first()
|
||||||
|
if business_info:
|
||||||
|
logger.debug(f"Found business info for ID: {business_info_id}")
|
||||||
|
return BusinessInfoResponse(**business_info.to_dict())
|
||||||
|
logger.warning(f"No business info found for ID: {business_info_id}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_business_info_by_user(self, user_id: int) -> Optional[BusinessInfoResponse]:
|
||||||
|
db: Session = next(get_db())
|
||||||
|
logger.debug(f"Retrieving business info by user ID: {user_id}")
|
||||||
|
business_info = db.query(UserBusinessInfo).filter(UserBusinessInfo.user_id == user_id).first()
|
||||||
|
if business_info:
|
||||||
|
logger.debug(f"Found business info for user ID: {user_id}")
|
||||||
|
return BusinessInfoResponse(**business_info.to_dict())
|
||||||
|
logger.warning(f"No business info found for user ID: {user_id}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_business_info(self, business_info_id: int, business_info: BusinessInfoRequest) -> Optional[BusinessInfoResponse]:
|
||||||
|
db: Session = next(get_db())
|
||||||
|
logger.debug(f"Updating business info for ID: {business_info_id}")
|
||||||
|
db_business_info = db.query(UserBusinessInfo).filter(UserBusinessInfo.id == business_info_id).first()
|
||||||
|
if db_business_info:
|
||||||
|
db_business_info.business_description = business_info.business_description
|
||||||
|
db_business_info.industry = business_info.industry
|
||||||
|
db_business_info.target_audience = business_info.target_audience
|
||||||
|
db_business_info.business_goals = business_info.business_goals
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_business_info)
|
||||||
|
logger.success(f"Updated business info for ID: {business_info_id}")
|
||||||
|
return BusinessInfoResponse(**db_business_info.to_dict())
|
||||||
|
logger.warning(f"No business info found to update for ID: {business_info_id}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
business_info_service = BusinessInfoService()
|
||||||
|
logger.info("✅ BusinessInfoService loaded successfully!")
|
||||||
@@ -19,6 +19,7 @@ from models.enhanced_strategy_models import Base as EnhancedStrategyBase
|
|||||||
from models.monitoring_models import Base as MonitoringBase
|
from models.monitoring_models import Base as MonitoringBase
|
||||||
from models.persona_models import Base as PersonaBase
|
from models.persona_models import Base as PersonaBase
|
||||||
from models.subscription_models import Base as SubscriptionBase
|
from models.subscription_models import Base as SubscriptionBase
|
||||||
|
from models.user_business_info import Base as UserBusinessInfoBase
|
||||||
|
|
||||||
# Database configuration
|
# Database configuration
|
||||||
DATABASE_URL = os.getenv('DATABASE_URL', 'sqlite:///./alwrity.db')
|
DATABASE_URL = os.getenv('DATABASE_URL', 'sqlite:///./alwrity.db')
|
||||||
@@ -72,7 +73,8 @@ def init_database():
|
|||||||
MonitoringBase.metadata.create_all(bind=engine)
|
MonitoringBase.metadata.create_all(bind=engine)
|
||||||
PersonaBase.metadata.create_all(bind=engine)
|
PersonaBase.metadata.create_all(bind=engine)
|
||||||
SubscriptionBase.metadata.create_all(bind=engine)
|
SubscriptionBase.metadata.create_all(bind=engine)
|
||||||
logger.info("Database initialized successfully with all models including subscription system")
|
UserBusinessInfoBase.metadata.create_all(bind=engine)
|
||||||
|
logger.info("Database initialized successfully with all models including subscription system and business info")
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
logger.error(f"Error initializing database: {str(e)}")
|
logger.error(f"Error initializing database: {str(e)}")
|
||||||
raise
|
raise
|
||||||
|
|||||||
49
frontend/src/api/businessInfo.ts
Normal file
49
frontend/src/api/businessInfo.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { apiClient } from './client';
|
||||||
|
|
||||||
|
console.log('🔄 Loading Business Info API client...');
|
||||||
|
|
||||||
|
export interface BusinessInfo {
|
||||||
|
user_id?: number;
|
||||||
|
business_description: string;
|
||||||
|
industry?: string;
|
||||||
|
target_audience?: string;
|
||||||
|
business_goals?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BusinessInfoResponse extends BusinessInfo {
|
||||||
|
id: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const businessInfoApi = {
|
||||||
|
saveBusinessInfo: async (data: BusinessInfo): Promise<BusinessInfoResponse> => {
|
||||||
|
console.log('API: Saving business info', data);
|
||||||
|
const response = await apiClient.post<BusinessInfoResponse>('/onboarding/business-info', data);
|
||||||
|
console.log('API: Business info saved successfully', response.data);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
getBusinessInfo: async (id: number): Promise<BusinessInfoResponse> => {
|
||||||
|
console.log(`API: Getting business info for ID: ${id}`);
|
||||||
|
const response = await apiClient.get<BusinessInfoResponse>(`/onboarding/business-info/${id}`);
|
||||||
|
console.log('API: Business info retrieved successfully', response.data);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
getBusinessInfoByUserId: async (userId: number): Promise<BusinessInfoResponse> => {
|
||||||
|
console.log(`API: Getting business info for user ID: ${userId}`);
|
||||||
|
const response = await apiClient.get<BusinessInfoResponse>(`/onboarding/business-info/user/${userId}`);
|
||||||
|
console.log('API: Business info retrieved successfully by user ID', response.data);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateBusinessInfo: async (id: number, data: BusinessInfo): Promise<BusinessInfoResponse> => {
|
||||||
|
console.log(`API: Updating business info for ID: ${id}`, data);
|
||||||
|
const response = await apiClient.put<BusinessInfoResponse>(`/onboarding/business-info/${id}`, data);
|
||||||
|
console.log('API: Business info updated successfully', response.data);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('✅ Business Info API client loaded successfully!');
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Box, Button, TextField, Typography, Card, CardContent, CircularProgress, Alert } from '@mui/material';
|
||||||
|
import { ArrowBack as ArrowBackIcon, Save as SaveIcon, CheckCircle as CheckCircleIcon } from '@mui/icons-material';
|
||||||
|
import { businessInfoApi, BusinessInfo } from '../../api/businessInfo';
|
||||||
|
|
||||||
|
interface BusinessDescriptionStepProps {
|
||||||
|
onBack: () => void;
|
||||||
|
onContinue: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BusinessDescriptionStep: React.FC<BusinessDescriptionStepProps> = ({ onBack, onContinue }) => {
|
||||||
|
const [formData, setFormData] = useState<BusinessInfo>({
|
||||||
|
business_description: '',
|
||||||
|
industry: '',
|
||||||
|
target_audience: '',
|
||||||
|
business_goals: '',
|
||||||
|
});
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [success, setSuccess] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData(prev => ({ ...prev, [name]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveAndContinue = async () => {
|
||||||
|
setError(null);
|
||||||
|
setSuccess(null);
|
||||||
|
setLoading(true);
|
||||||
|
console.log('🚀 Attempting to save business info:', formData);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Simulate user_id for now, replace with actual user_id from auth context later
|
||||||
|
const userId = 1;
|
||||||
|
const dataToSave = { ...formData, user_id: userId };
|
||||||
|
|
||||||
|
const response = await businessInfoApi.saveBusinessInfo(dataToSave);
|
||||||
|
console.log('✅ Business info saved to DB:', response);
|
||||||
|
setSuccess('Business information saved successfully!');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
onContinue();
|
||||||
|
}, 1500); // Give user time to see success message
|
||||||
|
} catch (err) {
|
||||||
|
console.error('❌ Error saving business info:', err);
|
||||||
|
setError('Failed to save business information. Please try again.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ mt: 4 }}>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
Tell us about your business
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" color="textSecondary" sx={{ mb: 3 }}>
|
||||||
|
Since you don't have a website, please provide a description of your business. This will help ALwrity understand your brand and tailor its services.
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Card sx={{ p: 3, mb: 3 }}>
|
||||||
|
<CardContent>
|
||||||
|
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
||||||
|
{success && <Alert severity="success" sx={{ mb: 2 }} icon={<CheckCircleIcon fontSize="inherit" />}>{success}</Alert>}
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
label="Business Description"
|
||||||
|
name="business_description"
|
||||||
|
value={formData.business_description}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
rows={4}
|
||||||
|
margin="normal"
|
||||||
|
required
|
||||||
|
helperText={`${formData.business_description.length}/1000 characters`}
|
||||||
|
inputProps={{ maxLength: 1000 }}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Industry"
|
||||||
|
name="industry"
|
||||||
|
value={formData.industry}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
helperText={`${formData.industry.length}/100 characters`}
|
||||||
|
inputProps={{ maxLength: 100 }}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Target Audience"
|
||||||
|
name="target_audience"
|
||||||
|
value={formData.target_audience}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
rows={2}
|
||||||
|
margin="normal"
|
||||||
|
helperText={`${(formData.target_audience || '').length}/500 characters`}
|
||||||
|
inputProps={{ maxLength: 500 }}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Business Goals"
|
||||||
|
name="business_goals"
|
||||||
|
value={formData.business_goals}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
rows={3}
|
||||||
|
margin="normal"
|
||||||
|
helperText={`${(formData.business_goals || '').length}/1000 characters`}
|
||||||
|
inputProps={{ maxLength: 1000 }}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 3 }}>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
onClick={onBack}
|
||||||
|
startIcon={<ArrowBackIcon />}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleSaveAndContinue}
|
||||||
|
endIcon={loading ? <CircularProgress size={20} color="inherit" /> : <SaveIcon />}
|
||||||
|
disabled={loading || !formData.business_description}
|
||||||
|
>
|
||||||
|
{loading ? 'Saving...' : 'Save & Continue'}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BusinessDescriptionStep;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import BusinessDescriptionStep from './BusinessDescriptionStep';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@@ -178,6 +179,7 @@ const WebsiteStep: React.FC<WebsiteStepProps> = ({ onContinue, updateHeaderConte
|
|||||||
const [useAnalysisForGenAI, setUseAnalysisForGenAI] = useState(true);
|
const [useAnalysisForGenAI, setUseAnalysisForGenAI] = useState(true);
|
||||||
const [domainName, setDomainName] = useState<string>('');
|
const [domainName, setDomainName] = useState<string>('');
|
||||||
const [hasCheckedExisting, setHasCheckedExisting] = useState(false);
|
const [hasCheckedExisting, setHasCheckedExisting] = useState(false);
|
||||||
|
const [showBusinessForm, setShowBusinessForm] = useState(false);
|
||||||
const [progress, setProgress] = useState<AnalysisProgress[]>([
|
const [progress, setProgress] = useState<AnalysisProgress[]>([
|
||||||
{ step: 1, message: 'Validating website URL', completed: false },
|
{ step: 1, message: 'Validating website URL', completed: false },
|
||||||
{ step: 2, message: 'Crawling website content', completed: false },
|
{ step: 2, message: 'Crawling website content', completed: false },
|
||||||
@@ -926,6 +928,22 @@ const WebsiteStep: React.FC<WebsiteStepProps> = ({ onContinue, updateHeaderConte
|
|||||||
</Zoom>
|
</Zoom>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Conditional rendering for business description form
|
||||||
|
if (showBusinessForm) {
|
||||||
|
return (
|
||||||
|
<BusinessDescriptionStep
|
||||||
|
onBack={() => {
|
||||||
|
console.log('⬅️ Going back to website form...');
|
||||||
|
setShowBusinessForm(false);
|
||||||
|
}}
|
||||||
|
onContinue={() => {
|
||||||
|
console.log('➡️ Business info completed, proceeding to next step...');
|
||||||
|
onContinue();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ maxWidth: 900, mx: 'auto', p: 3 }}>
|
<Box sx={{ maxWidth: 900, mx: 'auto', p: 3 }}>
|
||||||
{/* Enhanced Explanatory Text */}
|
{/* Enhanced Explanatory Text */}
|
||||||
@@ -978,6 +996,22 @@ const WebsiteStep: React.FC<WebsiteStepProps> = ({ onContinue, updateHeaderConte
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* No Website Button */}
|
||||||
|
<Box sx={{ mt: 2, textAlign: 'center', mb: 3 }}>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
console.log('🔄 Switching to business description form...');
|
||||||
|
setShowBusinessForm(true);
|
||||||
|
}}
|
||||||
|
startIcon={<BusinessIcon />}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
Don't have a website?
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<Card sx={{ mb: 3, p: 3 }}>
|
<Card sx={{ mb: 3, p: 3 }}>
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
|
|||||||
5
tatus
Normal file
5
tatus
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[33mcc8f9cd2e[m[33m ([m[1;36mHEAD[m[33m -> [m[1;32mmain[m[33m, [m[1;31morigin/cleanup/remove-cache-files[m[33m, [m[1;32mcleanup/remove-cache-files[m[33m)[m Clean up: Remove all cache files and add comprehensive .gitignore
|
||||||
|
[33mc19fc3f22[m[33m ([m[1;31morigin/main[m[33m, [m[1;31morigin/HEAD[m[33m)[m ALwrity Prompts - AI Integration Plan
|
||||||
|
[33m5efee4235[m Added citation and quality metrics to the content editor.
|
||||||
|
[33m10b50f973[m Alwrity Copilot Integration for LinkedIn Writer
|
||||||
|
[33m64944104a[m merge: LinkedIn Writer PR #223 - resolve conflicts and integrate with existing routers
|
||||||
Reference in New Issue
Block a user