ALwrity version 0.5.4
This commit is contained in:
@@ -942,13 +942,17 @@ class EnhancedStrategyService:
|
||||
# Transform data into frontend-expected format
|
||||
auto_populated_fields = self._transform_onboarding_data_to_fields(processed_data)
|
||||
|
||||
# Add detailed input data points for transparency
|
||||
input_data_points = self._get_detailed_input_data_points(processed_data)
|
||||
|
||||
logger.info(f"Retrieved comprehensive onboarding data for user {user_id}")
|
||||
return {
|
||||
'fields': auto_populated_fields,
|
||||
'sources': self._get_data_sources(processed_data),
|
||||
'quality_scores': processed_data['data_quality_scores'],
|
||||
'confidence_levels': processed_data['confidence_levels'],
|
||||
'data_freshness': processed_data['data_freshness']
|
||||
'data_freshness': processed_data['data_freshness'],
|
||||
'input_data_points': input_data_points # Add detailed input data
|
||||
}
|
||||
|
||||
finally:
|
||||
@@ -2442,4 +2446,87 @@ class EnhancedStrategyService:
|
||||
'quality_scores': {},
|
||||
'confidence_levels': {},
|
||||
'data_freshness': {'status': 'unknown', 'age_days': 'unknown'}
|
||||
}
|
||||
}
|
||||
|
||||
def _get_detailed_input_data_points(self, processed_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Get detailed input data points that were used to generate each field"""
|
||||
input_data_points = {}
|
||||
|
||||
website_data = processed_data.get('website_analysis', {})
|
||||
research_data = processed_data.get('research_preferences', {})
|
||||
api_data = processed_data.get('api_keys_data', {})
|
||||
|
||||
# Business Objectives - from website analysis
|
||||
if website_data:
|
||||
input_data_points['business_objectives'] = {
|
||||
'website_content': website_data.get('content_goals', 'Not available'),
|
||||
'meta_description': website_data.get('meta_description', 'Not available'),
|
||||
'about_page': website_data.get('about_page_content', 'Not available'),
|
||||
'page_title': website_data.get('page_title', 'Not available'),
|
||||
'content_analysis': website_data.get('content_analysis', {})
|
||||
}
|
||||
|
||||
# Target Metrics - from research preferences and industry analysis
|
||||
if research_data:
|
||||
input_data_points['target_metrics'] = {
|
||||
'research_preferences': research_data.get('target_audience', 'Not available'),
|
||||
'industry_benchmarks': research_data.get('industry_benchmarks', 'Not available'),
|
||||
'competitor_analysis': research_data.get('competitor_analysis', 'Not available'),
|
||||
'market_research': research_data.get('market_research', 'Not available')
|
||||
}
|
||||
|
||||
# Content Preferences - from research preferences
|
||||
if research_data:
|
||||
input_data_points['content_preferences'] = {
|
||||
'user_preferences': research_data.get('content_types', 'Not available'),
|
||||
'industry_trends': research_data.get('industry_trends', 'Not available'),
|
||||
'consumption_patterns': research_data.get('consumption_patterns', 'Not available'),
|
||||
'audience_research': research_data.get('audience_research', 'Not available')
|
||||
}
|
||||
|
||||
# Preferred Formats - from website analysis and research
|
||||
if website_data or research_data:
|
||||
input_data_points['preferred_formats'] = {
|
||||
'existing_content': website_data.get('existing_content_types', 'Not available'),
|
||||
'engagement_metrics': website_data.get('engagement_metrics', 'Not available'),
|
||||
'platform_analysis': research_data.get('platform_preferences', 'Not available'),
|
||||
'content_performance': website_data.get('content_performance', 'Not available')
|
||||
}
|
||||
|
||||
# Content Frequency - from research preferences
|
||||
if research_data:
|
||||
input_data_points['content_frequency'] = {
|
||||
'audience_research': research_data.get('content_frequency_preferences', 'Not available'),
|
||||
'industry_standards': research_data.get('industry_frequency', 'Not available'),
|
||||
'competitor_frequency': research_data.get('competitor_frequency', 'Not available'),
|
||||
'optimal_timing': research_data.get('optimal_timing', 'Not available')
|
||||
}
|
||||
|
||||
# Content Budget - from website analysis and industry standards
|
||||
if website_data:
|
||||
input_data_points['content_budget'] = {
|
||||
'website_analysis': website_data.get('budget_indicators', 'Not available'),
|
||||
'industry_standards': website_data.get('industry_budget', 'Not available'),
|
||||
'company_size': website_data.get('company_size', 'Not available'),
|
||||
'market_position': website_data.get('market_position', 'Not available')
|
||||
}
|
||||
|
||||
# Team Size - from website analysis and company profile
|
||||
if website_data:
|
||||
input_data_points['team_size'] = {
|
||||
'company_profile': website_data.get('company_profile', 'Not available'),
|
||||
'content_volume': website_data.get('content_volume', 'Not available'),
|
||||
'industry_standards': website_data.get('industry_team_size', 'Not available'),
|
||||
'budget_constraints': website_data.get('budget_constraints', 'Not available')
|
||||
}
|
||||
|
||||
# Implementation Timeline - from research and industry analysis
|
||||
if research_data:
|
||||
input_data_points['implementation_timeline'] = {
|
||||
'project_scope': research_data.get('project_scope', 'Not available'),
|
||||
'resource_availability': research_data.get('resource_availability', 'Not available'),
|
||||
'industry_timeline': research_data.get('industry_timeline', 'Not available'),
|
||||
'complexity_assessment': research_data.get('complexity_assessment', 'Not available')
|
||||
}
|
||||
|
||||
return input_data_points
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.c9966057.css",
|
||||
"main.js": "/static/js/main.28afa9ad.js",
|
||||
"main.js": "/static/js/main.ba50e996.js",
|
||||
"index.html": "/index.html",
|
||||
"main.c9966057.css.map": "/static/css/main.c9966057.css.map",
|
||||
"main.28afa9ad.js.map": "/static/js/main.28afa9ad.js.map"
|
||||
"main.ba50e996.js.map": "/static/js/main.ba50e996.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.c9966057.css",
|
||||
"static/js/main.28afa9ad.js"
|
||||
"static/js/main.ba50e996.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Alwrity - AI Content Creation Platform"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Alwrity - AI Content Creation Platform</title><script defer="defer" src="/static/js/main.28afa9ad.js"></script><link href="/static/css/main.c9966057.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Alwrity - AI Content Creation Platform"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Alwrity - AI Content Creation Platform</title><script defer="defer" src="/static/js/main.ba50e996.js"></script><link href="/static/css/main.c9966057.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-is.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @remix-run/router v1.13.1
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* React Router v6.20.1
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
@@ -78,6 +78,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
formErrors,
|
||||
autoPopulatedFields,
|
||||
dataSources,
|
||||
inputDataPoints, // Add inputDataPoints from store
|
||||
loading,
|
||||
error,
|
||||
saving,
|
||||
@@ -369,7 +370,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
<Grid container spacing={3}>
|
||||
{/* Category Overview Panel */}
|
||||
<Grid item xs={12} md={4}>
|
||||
<Paper sx={{ p: 3, height: 'fit-content', position: 'sticky', top: 20 }}>
|
||||
<Paper sx={{ p: 3, height: 'fit-content', position: 'sticky', top: 20, background: 'linear-gradient(180deg, #f7f9fc, #eef3fb)' }}>
|
||||
{/* Enhanced Completion Tracker - Integrated into Category List */}
|
||||
<ProgressTracker
|
||||
reviewProgressPercentage={reviewProgressPercentage}
|
||||
@@ -439,7 +440,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
|
||||
{/* Main Content Area */}
|
||||
<Grid item xs={12} md={8}>
|
||||
<Paper sx={{ p: 3, minHeight: '600px' }}>
|
||||
<Paper sx={{ p: 3, minHeight: '600px', background: 'linear-gradient(180deg, #faf7ff, #f1f0ff)' }}>
|
||||
{activeCategory ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
@@ -515,38 +516,46 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
</Dialog>
|
||||
|
||||
{/* Category Fields */}
|
||||
<Grid container spacing={1.5}>
|
||||
{STRATEGIC_INPUT_FIELDS
|
||||
.filter(field => field.category === activeCategory)
|
||||
.map((field) => {
|
||||
// Group number-based fields together
|
||||
const isNumberField = field.type === 'number' ||
|
||||
field.id.includes('budget') ||
|
||||
field.id.includes('size') ||
|
||||
field.id.includes('timeline') ||
|
||||
field.id.includes('metrics');
|
||||
|
||||
// Determine grid size based on field type
|
||||
const gridSize = isNumberField ? 6 : 12;
|
||||
|
||||
return (
|
||||
<Grid item xs={12} md={gridSize} key={field.id}>
|
||||
<StrategicInputField
|
||||
fieldId={field.id}
|
||||
value={formData[field.id]}
|
||||
error={formErrors[field.id]}
|
||||
autoPopulated={!!autoPopulatedFields[field.id]}
|
||||
dataSource={dataSources[field.id]}
|
||||
confidenceLevel={autoPopulatedFields[field.id] ? 0.8 : undefined}
|
||||
dataQuality={autoPopulatedFields[field.id] ? 'High Quality' : undefined}
|
||||
onChange={(value: any) => updateFormField(field.id, value)}
|
||||
onValidate={() => validateFormField(field.id)}
|
||||
onShowTooltip={() => setShowTooltip(field.id)}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
<Box sx={{ mt: 1 }}>
|
||||
<Grid container spacing={2}>
|
||||
{STRATEGIC_INPUT_FIELDS
|
||||
.filter(field => field.category === activeCategory)
|
||||
.map((field, index) => {
|
||||
// Determine grid size based on field type for better layout organization
|
||||
const type = field.type;
|
||||
const isWideField = type === 'json';
|
||||
const isMediumField = type === 'multiselect' || type === 'select' || type === 'text';
|
||||
const isCompactField = type === 'number' || type === 'boolean';
|
||||
const forceFullWidth = field.id === 'content_budget' || field.id === 'team_size';
|
||||
|
||||
const gridMd = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4);
|
||||
const gridLg = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4);
|
||||
const gridSm = 12;
|
||||
|
||||
return (
|
||||
<Grid item xs={12} sm={gridSm} md={gridMd} lg={gridLg} key={field.id}>
|
||||
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.25, delay: index * 0.03 }}>
|
||||
<StrategicInputField
|
||||
fieldId={field.id}
|
||||
value={formData[field.id]}
|
||||
error={formErrors[field.id]}
|
||||
autoPopulated={!!autoPopulatedFields[field.id]}
|
||||
dataSource={dataSources[field.id]}
|
||||
confidenceLevel={autoPopulatedFields[field.id] ? 0.8 : undefined}
|
||||
dataQuality={autoPopulatedFields[field.id] ? 'High Quality' : undefined}
|
||||
onChange={(value: any) => updateFormField(field.id, value)}
|
||||
onValidate={() => validateFormField(field.id)}
|
||||
onShowTooltip={() => setShowTooltip(field.id)}
|
||||
onViewDataSource={() => setShowDataSourceTransparency(true)}
|
||||
accentColorKey={getCategoryColor(activeCategory) as any}
|
||||
isCompact={isCompactField}
|
||||
/>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{/* Category Actions */}
|
||||
<Box sx={{ mt: 3, display: 'flex', gap: 2 }}>
|
||||
@@ -681,6 +690,7 @@ const ContentStrategyBuilder: React.FC = () => {
|
||||
<DataSourceTransparency
|
||||
autoPopulatedFields={autoPopulatedFields}
|
||||
dataSources={dataSources}
|
||||
inputDataPoints={inputDataPoints} // Use real input data points from store
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/* Subtle, modern styles for strategy input cards */
|
||||
|
||||
:root {
|
||||
--csb-card-radius: 12px;
|
||||
--csb-card-border: 1px;
|
||||
--csb-card-shadow: 0 6px 18px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.csb-card {
|
||||
border-radius: var(--csb-card-radius);
|
||||
border: var(--csb-card-border) solid rgba(148, 163, 184, 0.25);
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.9) 0%, rgba(249,250,251,0.9) 100%);
|
||||
box-shadow: var(--csb-card-shadow);
|
||||
transition: box-shadow .2s ease, border-color .2s ease, transform .15s ease;
|
||||
}
|
||||
|
||||
.csb-card:hover {
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
|
||||
border-color: rgba(59,130,246,0.35);
|
||||
}
|
||||
|
||||
.csb-section {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
Typography,
|
||||
Alert,
|
||||
Autocomplete,
|
||||
InputAdornment
|
||||
InputAdornment,
|
||||
Button
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Help as HelpIcon,
|
||||
@@ -37,6 +38,9 @@ interface StrategicInputFieldProps {
|
||||
onChange: (value: any) => void;
|
||||
onValidate: () => boolean;
|
||||
onShowTooltip: () => void;
|
||||
onViewDataSource?: () => void; // Add callback for viewing data source
|
||||
accentColorKey?: 'primary' | 'secondary' | 'success' | 'warning' | 'info' | 'error';
|
||||
isCompact?: boolean;
|
||||
}
|
||||
|
||||
// Define proper types for field configurations
|
||||
@@ -78,10 +82,15 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
|
||||
dataQuality,
|
||||
onChange,
|
||||
onValidate,
|
||||
onShowTooltip
|
||||
onShowTooltip,
|
||||
onViewDataSource,
|
||||
accentColorKey = 'primary',
|
||||
isCompact = false
|
||||
}) => {
|
||||
const { getTooltipData } = useEnhancedStrategyStore();
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
const getAccent = (theme: any) => (theme?.palette?.[accentColorKey] ?? theme?.palette?.primary);
|
||||
|
||||
// Get field configuration from store with proper null checking
|
||||
const tooltipData = getTooltipData(fieldId);
|
||||
@@ -484,16 +493,17 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
|
||||
return (
|
||||
<Box sx={{
|
||||
position: 'relative',
|
||||
mb: 1.5,
|
||||
p: 1.5,
|
||||
borderRadius: 1.5,
|
||||
bgcolor: 'background.paper',
|
||||
mb: isCompact ? 1.25 : 2,
|
||||
p: isCompact ? 1 : 1.5,
|
||||
borderRadius: 2,
|
||||
bgcolor: 'rgba(255,255,255,0.9)',
|
||||
border: '1px solid',
|
||||
borderColor: error ? 'error.main' : autoPopulated ? 'info.main' : 'divider',
|
||||
borderColor: error ? 'error.main' : 'rgba(148, 163, 184, 0.35)',
|
||||
boxShadow: '0 6px 18px rgba(0,0,0,0.06)',
|
||||
transition: 'box-shadow 0.2s ease, border-color 0.2s ease',
|
||||
'&:hover': {
|
||||
borderColor: 'primary.main',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
||||
transition: 'all 0.2s ease'
|
||||
borderColor: (theme) => getAccent(theme).main,
|
||||
boxShadow: (theme) => `0 10px 24px rgba(0,0,0,0.08), 0 0 0 2px ${getAccent(theme).main}22`
|
||||
}
|
||||
}}>
|
||||
{/* Field input - Enhanced styling */}
|
||||
@@ -501,19 +511,28 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
|
||||
'& .MuiTextField-root, & .MuiFormControl-root': {
|
||||
'& .MuiInputBase-root': {
|
||||
borderRadius: 1,
|
||||
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: (theme) => getAccent(theme).main,
|
||||
boxShadow: (theme) => `0 0 0 2px ${getAccent(theme).main}22`
|
||||
},
|
||||
'&:hover': {
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'primary.main'
|
||||
borderColor: (theme) => getAccent(theme).main
|
||||
}
|
||||
}
|
||||
},
|
||||
'& .MuiInputLabel-root': {
|
||||
fontSize: '0.8rem',
|
||||
fontWeight: 500
|
||||
fontSize: '0.9rem',
|
||||
fontWeight: 600,
|
||||
letterSpacing: '0.15px',
|
||||
color: (theme) => theme.palette.text.primary,
|
||||
'&.Mui-focused': {
|
||||
color: (theme) => getAccent(theme).main
|
||||
}
|
||||
},
|
||||
'& .MuiInputBase-input': {
|
||||
fontSize: '0.85rem',
|
||||
padding: '8px 12px'
|
||||
fontSize: '0.92rem',
|
||||
padding: isCompact ? '7px 10px' : '8px 12px'
|
||||
}
|
||||
}
|
||||
}}>
|
||||
@@ -534,7 +553,7 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
|
||||
{/* Validation status */}
|
||||
{value && !error && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<CheckCircleIcon color="success" sx={{ fontSize: 14 }} />
|
||||
<CheckCircleIcon sx={{ fontSize: 14, color: (theme) => getAccent(theme).main }} />
|
||||
<Typography variant="caption" color="success.main" sx={{ fontSize: '0.7rem' }}>
|
||||
Valid
|
||||
</Typography>
|
||||
@@ -548,7 +567,7 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
|
||||
label={dataQuality}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
color={accentColorKey as any}
|
||||
sx={{
|
||||
fontSize: '0.6rem',
|
||||
height: 20,
|
||||
@@ -557,8 +576,8 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Confidence Level indicator */}
|
||||
{confidenceLevel && (
|
||||
{/* Confidence Level indicator - REMOVED (Area 1) */}
|
||||
{/* {confidenceLevel && (
|
||||
<Chip
|
||||
label={`${Math.round(confidenceLevel * 100)}% confidence`}
|
||||
size="small"
|
||||
@@ -570,11 +589,11 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
|
||||
'& .MuiChip-label': { px: 1 }
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
</Box>
|
||||
|
||||
{/* Right side - Auto-population indicator */}
|
||||
{autoPopulated && (
|
||||
{/* Right side - Auto-population indicator - REMOVED (Area 2) */}
|
||||
{/* {autoPopulated && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<Chip
|
||||
icon={<AutoAwesomeIcon sx={{ fontSize: 12 }} />}
|
||||
@@ -596,6 +615,58 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
)} */}
|
||||
|
||||
{/* Enhanced Data Source Information */}
|
||||
{autoPopulated && dataSource && (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
mt: 0.5,
|
||||
p: 0.5,
|
||||
bgcolor: (theme) => `${getAccent(theme).main}0D`,
|
||||
borderRadius: 1,
|
||||
border: (theme) => `1px solid ${getAccent(theme).main}33`
|
||||
}}>
|
||||
<InfoIcon sx={{ fontSize: 12, color: (theme) => getAccent(theme).main }} />
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.6rem' }}>
|
||||
Data from: {dataSource.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}
|
||||
</Typography>
|
||||
{confidenceLevel && (
|
||||
<Chip
|
||||
label={`${Math.round(confidenceLevel * 100)}% confidence`}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color={confidenceLevel > 0.8 ? 'success' : confidenceLevel > 0.6 ? 'warning' : 'error'}
|
||||
sx={{
|
||||
fontSize: '0.5rem',
|
||||
height: 16,
|
||||
'& .MuiChip-label': { px: 0.5 }
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{onViewDataSource && (
|
||||
<Button
|
||||
size="small"
|
||||
variant="text"
|
||||
onClick={onViewDataSource}
|
||||
sx={{
|
||||
fontSize: '0.6rem',
|
||||
minWidth: 'auto',
|
||||
px: 1,
|
||||
py: 0.25,
|
||||
color: (theme) => getAccent(theme).main,
|
||||
textTransform: 'none',
|
||||
'&:hover': {
|
||||
bgcolor: (theme) => `${getAccent(theme).main}1A`
|
||||
}
|
||||
}}
|
||||
>
|
||||
View details
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -73,8 +73,8 @@ const CategoryList: React.FC<CategoryListProps> = ({
|
||||
>
|
||||
<ListItem
|
||||
sx={{
|
||||
p: 1.5, // 50% more compact padding
|
||||
mb: 0.5, // Reduced margin
|
||||
p: 1.25,
|
||||
mb: 0.4,
|
||||
borderRadius: 2,
|
||||
bgcolor: isSelected ? 'action.hover' : isNextInSequenceCategory ? 'rgba(25, 118, 210, 0.08)' : 'transparent',
|
||||
border: isSelected ? '2px solid' : isNextInSequenceCategory ? '1px solid' : '1px solid',
|
||||
@@ -106,7 +106,7 @@ const CategoryList: React.FC<CategoryListProps> = ({
|
||||
}}
|
||||
>
|
||||
{/* Category Header - Compact */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%', mb: 0.5, position: 'relative', zIndex: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%', mb: 0.4, position: 'relative', zIndex: 1 }}>
|
||||
<ListItemIcon sx={{ minWidth: 32 }}>
|
||||
{getCategoryIcon(categoryId)}
|
||||
</ListItemIcon>
|
||||
@@ -120,8 +120,8 @@ const CategoryList: React.FC<CategoryListProps> = ({
|
||||
size="small"
|
||||
color="primary"
|
||||
sx={{
|
||||
height: 16,
|
||||
fontSize: '0.6rem',
|
||||
height: 15,
|
||||
fontSize: '0.58rem',
|
||||
'& .MuiChip-label': { px: 0.5 }
|
||||
}}
|
||||
/>
|
||||
@@ -131,8 +131,8 @@ const CategoryList: React.FC<CategoryListProps> = ({
|
||||
secondary={`${Math.round(percentageValue)}% complete`}
|
||||
sx={{
|
||||
flex: 1,
|
||||
'& .MuiListItemText-primary': { fontSize: '0.9rem', fontWeight: 500 },
|
||||
'& .MuiListItemText-secondary': { fontSize: '0.7rem' }
|
||||
'& .MuiListItemText-primary': { fontSize: '0.88rem', fontWeight: 500 },
|
||||
'& .MuiListItemText-secondary': { fontSize: '0.68rem' }
|
||||
}}
|
||||
/>
|
||||
<Chip
|
||||
|
||||
@@ -37,44 +37,23 @@ const ProgressTracker: React.FC<ProgressTrackerProps> = ({
|
||||
onRefreshData
|
||||
}) => {
|
||||
return (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1.5 }}>
|
||||
<Typography variant="h6" gutterBottom sx={{ mb: 0 }}>
|
||||
Progress
|
||||
</Typography>
|
||||
{/* Spiral Progress - Moved from Region 2 to Region 3 */}
|
||||
<Box sx={{ mb: 1.5 }}>
|
||||
{/* Compact header row with title, progress, counts and actions */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 0.75 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="h6" sx={{ mb: 0, fontSize: '1rem' }}>
|
||||
Progress
|
||||
</Typography>
|
||||
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
|
||||
<CircularProgress
|
||||
variant="determinate"
|
||||
value={reviewProgressPercentage}
|
||||
size={40}
|
||||
size={28}
|
||||
thickness={4}
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
'& .MuiCircularProgress-circle': {
|
||||
strokeLinecap: 'round',
|
||||
}
|
||||
}}
|
||||
sx={{ color: 'primary.main', '& .MuiCircularProgress-circle': { strokeLinecap: 'round' } }}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
position: 'absolute',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
color="text.secondary"
|
||||
sx={{ fontSize: '0.7rem', fontWeight: 'bold' }}
|
||||
>
|
||||
<Box sx={{ top: 0, left: 0, bottom: 0, right: 0, position: 'absolute', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Typography variant="caption" component="div" color="text.secondary" sx={{ fontSize: '0.65rem', fontWeight: 700 }}>
|
||||
{`${Math.round(reviewProgressPercentage)}%`}
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -83,132 +62,51 @@ const ProgressTracker: React.FC<ProgressTrackerProps> = ({
|
||||
{reviewedCategoriesCount}/{totalCategories}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Status Indicators - Compact */}
|
||||
<Box sx={{ mb: 1.5 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 0.5 }}>
|
||||
<CheckCircleIcon color="success" fontSize="small" sx={{ fontSize: 14 }} />
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
|
||||
Auto-population: {Object.keys(autoPopulatedFields || {}).length} fields
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<AutoAwesomeIcon color="primary" fontSize="small" sx={{ fontSize: 14 }} />
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
|
||||
AI Insights: {aiGenerating ? 'Generating...' : 'Ready'}
|
||||
</Typography>
|
||||
|
||||
{/* Actions inline in header */}
|
||||
<Box sx={{ display: 'flex', gap: 0.75 }}>
|
||||
<MuiTooltip title="View AI-powered recommendations and insights" placement="top">
|
||||
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
|
||||
<IconButton
|
||||
onClick={onShowAIRecommendations}
|
||||
sx={{ color: 'primary.main', bgcolor: 'rgba(255, 193, 7, 0.08)', border: '1px solid rgba(255, 193, 7, 0.25)', width: 32, height: 32, '&:hover': { bgcolor: 'rgba(255, 193, 7, 0.16)' } }}
|
||||
>
|
||||
<Badge badgeContent={5} sx={{ '& .MuiBadge-badge': { fontSize: '0.55rem', fontWeight: 700, bgcolor: '#ff6b35', color: 'white' } }}>
|
||||
<AutoAwesomeIcon sx={{ fontSize: 16 }} />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
</motion.div>
|
||||
</MuiTooltip>
|
||||
|
||||
<MuiTooltip title="View data sources and transparency information" placement="top">
|
||||
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
|
||||
<IconButton
|
||||
onClick={onShowDataSourceTransparency}
|
||||
sx={{ color: 'primary.main', bgcolor: 'rgba(76, 175, 80, 0.08)', border: '1px solid rgba(76, 175, 80, 0.25)', width: 32, height: 32, '&:hover': { bgcolor: 'rgba(76, 175, 80, 0.16)' } }}
|
||||
>
|
||||
<Badge badgeContent={Object.keys(autoPopulatedFields || {}).length} sx={{ '& .MuiBadge-badge': { fontSize: '0.55rem', fontWeight: 700, bgcolor: '#2196f3', color: 'white' } }}>
|
||||
<InfoIcon sx={{ fontSize: 16 }} />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
</motion.div>
|
||||
</MuiTooltip>
|
||||
|
||||
<MuiTooltip title="Refresh auto-populated data" placement="top">
|
||||
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
|
||||
<IconButton onClick={onRefreshData} sx={{ color: 'primary.main', bgcolor: 'rgba(0,0,0,0.04)', border: '1px solid rgba(0,0,0,0.12)', width: 32, height: 32, '&:hover': { bgcolor: 'rgba(0,0,0,0.08)' } }}>
|
||||
<RefreshIcon sx={{ fontSize: 16 }} />
|
||||
</IconButton>
|
||||
</motion.div>
|
||||
</MuiTooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Icons moved from Region A to Region B - Now integrated into Progress title area */}
|
||||
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'center', mt: 1.5 }}>
|
||||
{/* AI Recommendations Button - Compact */}
|
||||
<MuiTooltip title="View AI-powered recommendations and insights" placement="top">
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<IconButton
|
||||
onClick={onShowAIRecommendations}
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
bgcolor: 'rgba(255, 193, 7, 0.1)',
|
||||
border: '1px solid rgba(255, 193, 7, 0.3)',
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(255, 193, 7, 0.2)',
|
||||
transform: 'translateY(-1px)',
|
||||
boxShadow: '0 4px 12px rgba(255, 193, 7, 0.3)'
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
width: 36,
|
||||
height: 36
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={5}
|
||||
sx={{
|
||||
'& .MuiBadge-badge': {
|
||||
fontSize: '0.6rem',
|
||||
fontWeight: 'bold',
|
||||
animation: 'pulse 2s infinite',
|
||||
bgcolor: '#ff6b35',
|
||||
color: 'white'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AutoAwesomeIcon sx={{ fontSize: 16 }} />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
</motion.div>
|
||||
</MuiTooltip>
|
||||
|
||||
{/* Data Source Transparency Button - Compact */}
|
||||
<MuiTooltip title="View data sources and transparency information" placement="top">
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<IconButton
|
||||
onClick={onShowDataSourceTransparency}
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
bgcolor: 'rgba(76, 175, 80, 0.1)',
|
||||
border: '1px solid rgba(76, 175, 80, 0.3)',
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(76, 175, 80, 0.2)',
|
||||
transform: 'translateY(-1px)',
|
||||
boxShadow: '0 4px 12px rgba(76, 175, 80, 0.3)'
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
width: 36,
|
||||
height: 36
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={Object.keys(autoPopulatedFields || {}).length}
|
||||
sx={{
|
||||
'& .MuiBadge-badge': {
|
||||
fontSize: '0.6rem',
|
||||
fontWeight: 'bold',
|
||||
animation: 'pulse 2s infinite',
|
||||
bgcolor: '#2196f3',
|
||||
color: 'white'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<InfoIcon sx={{ fontSize: 16 }} />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
</motion.div>
|
||||
</MuiTooltip>
|
||||
|
||||
{/* Refresh Button - Compact */}
|
||||
<MuiTooltip title="Refresh auto-populated data" placement="top">
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<IconButton
|
||||
onClick={onRefreshData}
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
bgcolor: 'rgba(0,0,0,0.05)',
|
||||
border: '1px solid rgba(0,0,0,0.1)',
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(0,0,0,0.1)',
|
||||
transform: 'translateY(-1px)',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.2)'
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
width: 36,
|
||||
height: 36
|
||||
}}
|
||||
>
|
||||
<RefreshIcon sx={{ fontSize: 16 }} />
|
||||
</IconButton>
|
||||
</motion.div>
|
||||
</MuiTooltip>
|
||||
{/* Combined info line */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<CheckCircleIcon color="success" sx={{ fontSize: 14 }} />
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
|
||||
Auto-population: {Object.keys(autoPopulatedFields || {}).length} fields • AI Insights: {aiGenerating ? 'Generating...' : 'Ready'}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const getCategoryColor = (categoryId: string): string => {
|
||||
case 'competitive_intelligence': return 'success';
|
||||
case 'content_strategy': return 'warning';
|
||||
case 'performance_analytics': return 'info';
|
||||
default: return 'default';
|
||||
default: return 'primary';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ import {
|
||||
Alert,
|
||||
IconButton,
|
||||
Collapse,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
Paper,
|
||||
Grid
|
||||
} from '@mui/material';
|
||||
import {
|
||||
DataUsage as DataUsageIcon,
|
||||
@@ -24,19 +26,26 @@ import {
|
||||
Info as InfoIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
ExpandLess as ExpandLessIcon,
|
||||
Refresh as RefreshIcon
|
||||
Refresh as RefreshIcon,
|
||||
Timeline as TimelineIcon,
|
||||
TrendingUp as TrendingUpIcon,
|
||||
Schedule as ScheduleIcon
|
||||
} from '@mui/icons-material';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface DataSourceTransparencyProps {
|
||||
autoPopulatedFields: Record<string, any>;
|
||||
dataSources: Record<string, string>;
|
||||
inputDataPoints?: Record<string, any>; // Actual input data used to generate each field
|
||||
}
|
||||
|
||||
const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
|
||||
autoPopulatedFields,
|
||||
dataSources
|
||||
dataSources,
|
||||
inputDataPoints = {}
|
||||
}) => {
|
||||
const [expanded, setExpanded] = React.useState(true);
|
||||
const [showDataFlow, setShowDataFlow] = React.useState(false);
|
||||
|
||||
const getDataSourceIcon = (source: string) => {
|
||||
const icons = {
|
||||
@@ -81,11 +90,99 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
|
||||
return 'Low Quality';
|
||||
};
|
||||
|
||||
const getDataFreshness = (source: string) => {
|
||||
// Mock data freshness (in hours)
|
||||
const freshness = {
|
||||
website_analysis: 2,
|
||||
research_preferences: 24,
|
||||
api_keys: 168, // 1 week
|
||||
onboarding_session: 48
|
||||
};
|
||||
return freshness[source as keyof typeof freshness] || 24;
|
||||
};
|
||||
|
||||
const getFreshnessColor = (hours: number) => {
|
||||
if (hours <= 6) return 'success';
|
||||
if (hours <= 24) return 'warning';
|
||||
return 'error';
|
||||
};
|
||||
|
||||
const getFreshnessLabel = (hours: number) => {
|
||||
if (hours <= 6) return 'Very Fresh';
|
||||
if (hours <= 24) return 'Fresh';
|
||||
if (hours <= 168) return 'Recent';
|
||||
return 'Stale';
|
||||
};
|
||||
|
||||
// Get input data points for a specific field
|
||||
const getInputDataPoints = (fieldId: string) => {
|
||||
return inputDataPoints[fieldId] || null;
|
||||
};
|
||||
|
||||
// Format input data points for display
|
||||
const formatInputDataPoints = (dataPoints: any) => {
|
||||
if (!dataPoints) return null;
|
||||
|
||||
if (typeof dataPoints === 'string') {
|
||||
return dataPoints;
|
||||
}
|
||||
|
||||
if (Array.isArray(dataPoints)) {
|
||||
return dataPoints.join(', ');
|
||||
}
|
||||
|
||||
if (typeof dataPoints === 'object') {
|
||||
return Object.entries(dataPoints)
|
||||
.map(([key, value]) => `${key}: ${value}`)
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
return String(dataPoints);
|
||||
};
|
||||
|
||||
// Get data transformation info
|
||||
const getDataTransformationInfo = (fieldId: string, source: string) => {
|
||||
const transformations = {
|
||||
business_objectives: {
|
||||
from: 'website_analysis',
|
||||
transformation: 'Extracted business goals from website content and meta descriptions',
|
||||
inputData: 'Website content, meta descriptions, about page'
|
||||
},
|
||||
target_metrics: {
|
||||
from: 'research_preferences',
|
||||
transformation: 'Derived KPIs from research preferences and industry standards',
|
||||
inputData: 'Research preferences, industry benchmarks, competitor analysis'
|
||||
},
|
||||
content_preferences: {
|
||||
from: 'onboarding_session',
|
||||
transformation: 'Inferred from user preferences and industry analysis',
|
||||
inputData: 'User preferences, industry trends, content consumption patterns'
|
||||
},
|
||||
preferred_formats: {
|
||||
from: 'website_analysis',
|
||||
transformation: 'Analyzed existing content formats and user engagement',
|
||||
inputData: 'Existing content types, engagement metrics, platform analysis'
|
||||
},
|
||||
content_frequency: {
|
||||
from: 'research_preferences',
|
||||
transformation: 'Calculated optimal frequency based on audience and industry',
|
||||
inputData: 'Audience research, industry standards, competitor frequency'
|
||||
}
|
||||
};
|
||||
|
||||
return transformations[fieldId as keyof typeof transformations] || {
|
||||
from: source,
|
||||
transformation: 'Data processed and transformed for optimal strategy',
|
||||
inputData: 'Various data sources combined'
|
||||
};
|
||||
};
|
||||
|
||||
const autoPopulatedFieldsList = Object.entries(autoPopulatedFields).map(([fieldId, value]) => ({
|
||||
fieldId,
|
||||
value,
|
||||
source: dataSources[fieldId] || 'unknown',
|
||||
qualityScore: getDataQualityScore(dataSources[fieldId] || 'unknown')
|
||||
qualityScore: getDataQualityScore(dataSources[fieldId] || 'unknown'),
|
||||
freshness: getDataFreshness(dataSources[fieldId] || 'unknown')
|
||||
}));
|
||||
|
||||
const sourceSummary = Object.entries(dataSources).reduce((acc, [fieldId, source]) => {
|
||||
@@ -106,7 +203,7 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 2 }}>
|
||||
<DataUsageIcon color="primary" />
|
||||
<Typography variant="h6">
|
||||
Data Sources
|
||||
Data Sources & Transparency
|
||||
</Typography>
|
||||
<Chip
|
||||
icon={<AutoAwesomeIcon />}
|
||||
@@ -130,6 +227,96 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
|
||||
</Typography>
|
||||
</Alert>
|
||||
|
||||
{/* Visual Data Flow Diagram */}
|
||||
<Paper sx={{ p: 2, mb: 2, bgcolor: 'background.default' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
|
||||
<TimelineIcon color="primary" />
|
||||
<Typography variant="subtitle2">Data Flow Visualization</Typography>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setShowDataFlow(!showDataFlow)}
|
||||
>
|
||||
{showDataFlow ? <ExpandLessIcon /> : <ExpandMoreIcon />}
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<Collapse in={showDataFlow}>
|
||||
<Grid container spacing={2}>
|
||||
{Object.entries(sourceSummary).map(([source, fields], index) => (
|
||||
<Grid item xs={12} md={6} key={source}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
>
|
||||
<Paper sx={{ p: 2, bgcolor: 'background.paper' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
|
||||
<Typography variant="h6" sx={{ fontSize: '1.2rem' }}>
|
||||
{getDataSourceIcon(source)}
|
||||
</Typography>
|
||||
<Typography variant="subtitle2" fontWeight="medium">
|
||||
{getDataSourceLabel(source)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Data Flow Path */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
|
||||
<Box sx={{
|
||||
width: 20,
|
||||
height: 20,
|
||||
borderRadius: '50%',
|
||||
bgcolor: 'primary.main',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<Typography variant="caption" color="white" fontWeight="bold">
|
||||
{fields.length}
|
||||
</Typography>
|
||||
</Box>
|
||||
<TrendingUpIcon color="primary" sx={{ fontSize: 16 }} />
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
→ {fields.length} fields populated
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Sample Input Data */}
|
||||
<Box sx={{ mb: 1 }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 500 }}>
|
||||
Sample Input Data:
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', fontSize: '0.6rem' }}>
|
||||
{source === 'website_analysis' && 'Website content, meta tags, page structure'}
|
||||
{source === 'research_preferences' && 'User preferences, industry research, competitor data'}
|
||||
{source === 'api_keys' && 'API configurations, service integrations, authentication'}
|
||||
{source === 'onboarding_session' && 'User responses, preferences, business information'}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Quality & Freshness */}
|
||||
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
||||
<Chip
|
||||
label={`${Math.round(getDataQualityScore(source) * 100)}% quality`}
|
||||
size="small"
|
||||
color={getDataQualityColor(getDataQualityScore(source))}
|
||||
sx={{ fontSize: '0.6rem' }}
|
||||
/>
|
||||
<Chip
|
||||
label={getFreshnessLabel(getDataFreshness(source))}
|
||||
size="small"
|
||||
color={getFreshnessColor(getDataFreshness(source))}
|
||||
icon={<ScheduleIcon sx={{ fontSize: 12 }} />}
|
||||
sx={{ fontSize: '0.6rem' }}
|
||||
/>
|
||||
</Box>
|
||||
</Paper>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Collapse>
|
||||
</Paper>
|
||||
|
||||
{/* Data Sources Breakdown */}
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="subtitle2" gutterBottom>
|
||||
@@ -169,9 +356,17 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
|
||||
{Math.round(getDataQualityScore(source) * 100)}%
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{getDataQualityLabel(getDataQualityScore(source))}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{getDataQualityLabel(getDataQualityScore(source))}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
•
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{getFreshnessLabel(getDataFreshness(source))} ({getDataFreshness(source)}h ago)
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
@@ -188,35 +383,73 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
|
||||
Auto-populated Fields
|
||||
</Typography>
|
||||
<List dense>
|
||||
{autoPopulatedFieldsList.map((field, index) => (
|
||||
<React.Fragment key={field.fieldId}>
|
||||
<ListItem sx={{ px: 0 }}>
|
||||
<ListItemIcon sx={{ minWidth: 40 }}>
|
||||
<CheckCircleIcon color="success" fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="body2" fontWeight="medium">
|
||||
{field.fieldId.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}
|
||||
</Typography>
|
||||
<Chip
|
||||
label={getDataSourceLabel(field.source)}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
secondary={
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Source: {getDataSourceLabel(field.source)} • Quality: {getDataQualityLabel(field.qualityScore)}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
{index < autoPopulatedFieldsList.length - 1 && <Divider />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
{autoPopulatedFieldsList.map((field, index) => {
|
||||
const inputData = getInputDataPoints(field.fieldId);
|
||||
const transformationInfo = getDataTransformationInfo(field.fieldId, field.source);
|
||||
|
||||
return (
|
||||
<React.Fragment key={field.fieldId}>
|
||||
<ListItem sx={{ px: 0 }}>
|
||||
<ListItemIcon sx={{ minWidth: 40 }}>
|
||||
<CheckCircleIcon color="success" fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="body2" fontWeight="medium">
|
||||
{field.fieldId.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}
|
||||
</Typography>
|
||||
<Chip
|
||||
label={getDataSourceLabel(field.source)}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
secondary={
|
||||
<Box sx={{ mt: 0.5 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Source: {getDataSourceLabel(field.source)} • Quality: {getDataQualityLabel(field.qualityScore)}
|
||||
</Typography>
|
||||
|
||||
{/* Data Transformation Info */}
|
||||
<Box sx={{ mt: 0.5, p: 1, bgcolor: 'rgba(76, 175, 80, 0.05)', borderRadius: 1 }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 500 }}>
|
||||
🔄 Transformation: {transformationInfo.transformation}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.5 }}>
|
||||
📊 Input Data: {transformationInfo.inputData}
|
||||
</Typography>
|
||||
{inputData && (
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.5 }}>
|
||||
📝 Actual Input: {formatInputDataPoints(inputData)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mt: 0.5 }}>
|
||||
<Chip
|
||||
label={`${Math.round(field.qualityScore * 100)}% confidence`}
|
||||
size="small"
|
||||
color={field.qualityScore > 0.8 ? 'success' : field.qualityScore > 0.6 ? 'warning' : 'error'}
|
||||
sx={{ fontSize: '0.5rem', height: 16 }}
|
||||
/>
|
||||
<Chip
|
||||
label={getFreshnessLabel(field.freshness)}
|
||||
size="small"
|
||||
color={getFreshnessColor(field.freshness)}
|
||||
icon={<ScheduleIcon sx={{ fontSize: 10 }} />}
|
||||
sx={{ fontSize: '0.5rem', height: 16 }}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
{index < autoPopulatedFieldsList.length - 1 && <Divider />}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ interface EnhancedStrategyStore {
|
||||
formErrors: Record<string, string>;
|
||||
autoPopulatedFields: Record<string, any>;
|
||||
dataSources: Record<string, string>;
|
||||
inputDataPoints: Record<string, any>; // Detailed input data points from backend
|
||||
|
||||
// UI State
|
||||
loading: boolean;
|
||||
@@ -600,6 +601,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
|
||||
formErrors: {},
|
||||
autoPopulatedFields: {},
|
||||
dataSources: {},
|
||||
inputDataPoints: {}, // Initialize inputDataPoints
|
||||
|
||||
// UI State
|
||||
loading: false,
|
||||
@@ -719,6 +721,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
|
||||
formErrors: {},
|
||||
autoPopulatedFields: {},
|
||||
dataSources: {},
|
||||
inputDataPoints: {}, // Reset inputDataPoints
|
||||
currentStep: 0,
|
||||
completedSteps: []
|
||||
});
|
||||
@@ -768,9 +771,11 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
|
||||
// Extract field values and sources from the new backend format
|
||||
const fields = response.data?.fields || {};
|
||||
const sources = response.data?.sources || {};
|
||||
const inputDataPoints = response.data?.input_data_points || {};
|
||||
|
||||
console.log('📋 Extracted fields:', fields);
|
||||
console.log('🔗 Data sources:', sources);
|
||||
console.log('📝 Input data points:', inputDataPoints);
|
||||
|
||||
// Transform the fields object to extract values for formData
|
||||
const fieldValues: Record<string, any> = {};
|
||||
@@ -795,6 +800,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
|
||||
set((state) => ({
|
||||
autoPopulatedFields,
|
||||
dataSources: sources,
|
||||
inputDataPoints, // Store the detailed input data points
|
||||
formData: { ...state.formData, ...fieldValues }
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user