ALwrity version 0.5.4

This commit is contained in:
ajaysi
2025-08-08 10:50:31 +05:30
parent c5b54786f8
commit 01fe1e0a9c
14 changed files with 590 additions and 548 deletions

View File

@@ -942,13 +942,17 @@ class EnhancedStrategyService:
# Transform data into frontend-expected format # Transform data into frontend-expected format
auto_populated_fields = self._transform_onboarding_data_to_fields(processed_data) 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}") logger.info(f"Retrieved comprehensive onboarding data for user {user_id}")
return { return {
'fields': auto_populated_fields, 'fields': auto_populated_fields,
'sources': self._get_data_sources(processed_data), 'sources': self._get_data_sources(processed_data),
'quality_scores': processed_data['data_quality_scores'], 'quality_scores': processed_data['data_quality_scores'],
'confidence_levels': processed_data['confidence_levels'], '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: finally:
@@ -2443,3 +2447,86 @@ class EnhancedStrategyService:
'confidence_levels': {}, 'confidence_levels': {},
'data_freshness': {'status': 'unknown', 'age_days': 'unknown'} '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

View File

@@ -1,13 +1,13 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.c9966057.css", "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", "index.html": "/index.html",
"main.c9966057.css.map": "/static/css/main.c9966057.css.map", "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": [ "entrypoints": [
"static/css/main.c9966057.css", "static/css/main.c9966057.css",
"static/js/main.28afa9ad.js" "static/js/main.ba50e996.js"
] ]
} }

View File

@@ -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

View File

@@ -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

View File

@@ -78,6 +78,7 @@ const ContentStrategyBuilder: React.FC = () => {
formErrors, formErrors,
autoPopulatedFields, autoPopulatedFields,
dataSources, dataSources,
inputDataPoints, // Add inputDataPoints from store
loading, loading,
error, error,
saving, saving,
@@ -369,7 +370,7 @@ const ContentStrategyBuilder: React.FC = () => {
<Grid container spacing={3}> <Grid container spacing={3}>
{/* Category Overview Panel */} {/* Category Overview Panel */}
<Grid item xs={12} md={4}> <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 */} {/* Enhanced Completion Tracker - Integrated into Category List */}
<ProgressTracker <ProgressTracker
reviewProgressPercentage={reviewProgressPercentage} reviewProgressPercentage={reviewProgressPercentage}
@@ -439,7 +440,7 @@ const ContentStrategyBuilder: React.FC = () => {
{/* Main Content Area */} {/* Main Content Area */}
<Grid item xs={12} md={8}> <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 ? ( {activeCategory ? (
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
@@ -515,38 +516,46 @@ const ContentStrategyBuilder: React.FC = () => {
</Dialog> </Dialog>
{/* Category Fields */} {/* Category Fields */}
<Grid container spacing={1.5}> <Box sx={{ mt: 1 }}>
{STRATEGIC_INPUT_FIELDS <Grid container spacing={2}>
.filter(field => field.category === activeCategory) {STRATEGIC_INPUT_FIELDS
.map((field) => { .filter(field => field.category === activeCategory)
// Group number-based fields together .map((field, index) => {
const isNumberField = field.type === 'number' || // Determine grid size based on field type for better layout organization
field.id.includes('budget') || const type = field.type;
field.id.includes('size') || const isWideField = type === 'json';
field.id.includes('timeline') || const isMediumField = type === 'multiselect' || type === 'select' || type === 'text';
field.id.includes('metrics'); const isCompactField = type === 'number' || type === 'boolean';
const forceFullWidth = field.id === 'content_budget' || field.id === 'team_size';
// Determine grid size based on field type const gridMd = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4);
const gridSize = isNumberField ? 6 : 12; const gridLg = forceFullWidth ? 12 : (isWideField ? 12 : isMediumField ? 6 : 4);
const gridSm = 12;
return ( return (
<Grid item xs={12} md={gridSize} key={field.id}> <Grid item xs={12} sm={gridSm} md={gridMd} lg={gridLg} key={field.id}>
<StrategicInputField <motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.25, delay: index * 0.03 }}>
fieldId={field.id} <StrategicInputField
value={formData[field.id]} fieldId={field.id}
error={formErrors[field.id]} value={formData[field.id]}
autoPopulated={!!autoPopulatedFields[field.id]} error={formErrors[field.id]}
dataSource={dataSources[field.id]} autoPopulated={!!autoPopulatedFields[field.id]}
confidenceLevel={autoPopulatedFields[field.id] ? 0.8 : undefined} dataSource={dataSources[field.id]}
dataQuality={autoPopulatedFields[field.id] ? 'High Quality' : undefined} confidenceLevel={autoPopulatedFields[field.id] ? 0.8 : undefined}
onChange={(value: any) => updateFormField(field.id, value)} dataQuality={autoPopulatedFields[field.id] ? 'High Quality' : undefined}
onValidate={() => validateFormField(field.id)} onChange={(value: any) => updateFormField(field.id, value)}
onShowTooltip={() => setShowTooltip(field.id)} onValidate={() => validateFormField(field.id)}
/> onShowTooltip={() => setShowTooltip(field.id)}
</Grid> onViewDataSource={() => setShowDataSourceTransparency(true)}
); accentColorKey={getCategoryColor(activeCategory) as any}
})} isCompact={isCompactField}
</Grid> />
</motion.div>
</Grid>
);
})}
</Grid>
</Box>
{/* Category Actions */} {/* Category Actions */}
<Box sx={{ mt: 3, display: 'flex', gap: 2 }}> <Box sx={{ mt: 3, display: 'flex', gap: 2 }}>
@@ -681,6 +690,7 @@ const ContentStrategyBuilder: React.FC = () => {
<DataSourceTransparency <DataSourceTransparency
autoPopulatedFields={autoPopulatedFields} autoPopulatedFields={autoPopulatedFields}
dataSources={dataSources} dataSources={dataSources}
inputDataPoints={inputDataPoints} // Use real input data points from store
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View File

@@ -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;
}

View File

@@ -14,7 +14,8 @@ import {
Typography, Typography,
Alert, Alert,
Autocomplete, Autocomplete,
InputAdornment InputAdornment,
Button
} from '@mui/material'; } from '@mui/material';
import { import {
Help as HelpIcon, Help as HelpIcon,
@@ -37,6 +38,9 @@ interface StrategicInputFieldProps {
onChange: (value: any) => void; onChange: (value: any) => void;
onValidate: () => boolean; onValidate: () => boolean;
onShowTooltip: () => void; 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 // Define proper types for field configurations
@@ -78,11 +82,16 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
dataQuality, dataQuality,
onChange, onChange,
onValidate, onValidate,
onShowTooltip onShowTooltip,
onViewDataSource,
accentColorKey = 'primary',
isCompact = false
}) => { }) => {
const { getTooltipData } = useEnhancedStrategyStore(); const { getTooltipData } = useEnhancedStrategyStore();
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const getAccent = (theme: any) => (theme?.palette?.[accentColorKey] ?? theme?.palette?.primary);
// Get field configuration from store with proper null checking // Get field configuration from store with proper null checking
const tooltipData = getTooltipData(fieldId); const tooltipData = getTooltipData(fieldId);
@@ -484,16 +493,17 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
return ( return (
<Box sx={{ <Box sx={{
position: 'relative', position: 'relative',
mb: 1.5, mb: isCompact ? 1.25 : 2,
p: 1.5, p: isCompact ? 1 : 1.5,
borderRadius: 1.5, borderRadius: 2,
bgcolor: 'background.paper', bgcolor: 'rgba(255,255,255,0.9)',
border: '1px solid', 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': { '&:hover': {
borderColor: 'primary.main', borderColor: (theme) => getAccent(theme).main,
boxShadow: '0 2px 8px rgba(0,0,0,0.1)', boxShadow: (theme) => `0 10px 24px rgba(0,0,0,0.08), 0 0 0 2px ${getAccent(theme).main}22`
transition: 'all 0.2s ease'
} }
}}> }}>
{/* Field input - Enhanced styling */} {/* Field input - Enhanced styling */}
@@ -501,19 +511,28 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
'& .MuiTextField-root, & .MuiFormControl-root': { '& .MuiTextField-root, & .MuiFormControl-root': {
'& .MuiInputBase-root': { '& .MuiInputBase-root': {
borderRadius: 1, borderRadius: 1,
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
borderColor: (theme) => getAccent(theme).main,
boxShadow: (theme) => `0 0 0 2px ${getAccent(theme).main}22`
},
'&:hover': { '&:hover': {
'& .MuiOutlinedInput-notchedOutline': { '& .MuiOutlinedInput-notchedOutline': {
borderColor: 'primary.main' borderColor: (theme) => getAccent(theme).main
} }
} }
}, },
'& .MuiInputLabel-root': { '& .MuiInputLabel-root': {
fontSize: '0.8rem', fontSize: '0.9rem',
fontWeight: 500 fontWeight: 600,
letterSpacing: '0.15px',
color: (theme) => theme.palette.text.primary,
'&.Mui-focused': {
color: (theme) => getAccent(theme).main
}
}, },
'& .MuiInputBase-input': { '& .MuiInputBase-input': {
fontSize: '0.85rem', fontSize: '0.92rem',
padding: '8px 12px' padding: isCompact ? '7px 10px' : '8px 12px'
} }
} }
}}> }}>
@@ -534,7 +553,7 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
{/* Validation status */} {/* Validation status */}
{value && !error && ( {value && !error && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}> <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' }}> <Typography variant="caption" color="success.main" sx={{ fontSize: '0.7rem' }}>
Valid Valid
</Typography> </Typography>
@@ -548,7 +567,7 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
label={dataQuality} label={dataQuality}
size="small" size="small"
variant="outlined" variant="outlined"
color="primary" color={accentColorKey as any}
sx={{ sx={{
fontSize: '0.6rem', fontSize: '0.6rem',
height: 20, height: 20,
@@ -557,8 +576,8 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
/> />
)} )}
{/* Confidence Level indicator */} {/* Confidence Level indicator - REMOVED (Area 1) */}
{confidenceLevel && ( {/* {confidenceLevel && (
<Chip <Chip
label={`${Math.round(confidenceLevel * 100)}% confidence`} label={`${Math.round(confidenceLevel * 100)}% confidence`}
size="small" size="small"
@@ -570,11 +589,11 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
'& .MuiChip-label': { px: 1 } '& .MuiChip-label': { px: 1 }
}} }}
/> />
)} )} */}
</Box> </Box>
{/* Right side - Auto-population indicator */} {/* Right side - Auto-population indicator - REMOVED (Area 2) */}
{autoPopulated && ( {/* {autoPopulated && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
<Chip <Chip
icon={<AutoAwesomeIcon sx={{ fontSize: 12 }} />} icon={<AutoAwesomeIcon sx={{ fontSize: 12 }} />}
@@ -596,6 +615,58 @@ const StrategicInputField: React.FC<StrategicInputFieldProps> = ({
</Tooltip> </Tooltip>
)} )}
</Box> </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> </Box>

View File

@@ -73,8 +73,8 @@ const CategoryList: React.FC<CategoryListProps> = ({
> >
<ListItem <ListItem
sx={{ sx={{
p: 1.5, // 50% more compact padding p: 1.25,
mb: 0.5, // Reduced margin mb: 0.4,
borderRadius: 2, borderRadius: 2,
bgcolor: isSelected ? 'action.hover' : isNextInSequenceCategory ? 'rgba(25, 118, 210, 0.08)' : 'transparent', bgcolor: isSelected ? 'action.hover' : isNextInSequenceCategory ? 'rgba(25, 118, 210, 0.08)' : 'transparent',
border: isSelected ? '2px solid' : isNextInSequenceCategory ? '1px solid' : '1px solid', border: isSelected ? '2px solid' : isNextInSequenceCategory ? '1px solid' : '1px solid',
@@ -106,7 +106,7 @@ const CategoryList: React.FC<CategoryListProps> = ({
}} }}
> >
{/* Category Header - Compact */} {/* 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 }}> <ListItemIcon sx={{ minWidth: 32 }}>
{getCategoryIcon(categoryId)} {getCategoryIcon(categoryId)}
</ListItemIcon> </ListItemIcon>
@@ -120,8 +120,8 @@ const CategoryList: React.FC<CategoryListProps> = ({
size="small" size="small"
color="primary" color="primary"
sx={{ sx={{
height: 16, height: 15,
fontSize: '0.6rem', fontSize: '0.58rem',
'& .MuiChip-label': { px: 0.5 } '& .MuiChip-label': { px: 0.5 }
}} }}
/> />
@@ -131,8 +131,8 @@ const CategoryList: React.FC<CategoryListProps> = ({
secondary={`${Math.round(percentageValue)}% complete`} secondary={`${Math.round(percentageValue)}% complete`}
sx={{ sx={{
flex: 1, flex: 1,
'& .MuiListItemText-primary': { fontSize: '0.9rem', fontWeight: 500 }, '& .MuiListItemText-primary': { fontSize: '0.88rem', fontWeight: 500 },
'& .MuiListItemText-secondary': { fontSize: '0.7rem' } '& .MuiListItemText-secondary': { fontSize: '0.68rem' }
}} }}
/> />
<Chip <Chip

View File

@@ -37,44 +37,23 @@ const ProgressTracker: React.FC<ProgressTrackerProps> = ({
onRefreshData onRefreshData
}) => { }) => {
return ( return (
<Box sx={{ mb: 2 }}> <Box sx={{ mb: 1.5 }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1.5 }}> {/* Compact header row with title, progress, counts and actions */}
<Typography variant="h6" gutterBottom sx={{ mb: 0 }}> <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 0.75 }}>
Progress
</Typography>
{/* Spiral Progress - Moved from Region 2 to Region 3 */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> <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' }}> <Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress <CircularProgress
variant="determinate" variant="determinate"
value={reviewProgressPercentage} value={reviewProgressPercentage}
size={40} size={28}
thickness={4} thickness={4}
sx={{ sx={{ color: 'primary.main', '& .MuiCircularProgress-circle': { strokeLinecap: 'round' } }}
color: 'primary.main',
'& .MuiCircularProgress-circle': {
strokeLinecap: 'round',
}
}}
/> />
<Box <Box sx={{ top: 0, left: 0, bottom: 0, right: 0, position: 'absolute', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
sx={{ <Typography variant="caption" component="div" color="text.secondary" sx={{ fontSize: '0.65rem', fontWeight: 700 }}>
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' }}
>
{`${Math.round(reviewProgressPercentage)}%`} {`${Math.round(reviewProgressPercentage)}%`}
</Typography> </Typography>
</Box> </Box>
@@ -83,132 +62,51 @@ const ProgressTracker: React.FC<ProgressTrackerProps> = ({
{reviewedCategoriesCount}/{totalCategories} {reviewedCategoriesCount}/{totalCategories}
</Typography> </Typography>
</Box> </Box>
</Box>
{/* Status Indicators - Compact */} {/* Actions inline in header */}
<Box sx={{ mb: 1.5 }}> <Box sx={{ display: 'flex', gap: 0.75 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 0.5 }}> <MuiTooltip title="View AI-powered recommendations and insights" placement="top">
<CheckCircleIcon color="success" fontSize="small" sx={{ fontSize: 14 }} /> <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}> <IconButton
Auto-population: {Object.keys(autoPopulatedFields || {}).length} fields onClick={onShowAIRecommendations}
</Typography> 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)' } }}
</Box> >
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}> <Badge badgeContent={5} sx={{ '& .MuiBadge-badge': { fontSize: '0.55rem', fontWeight: 700, bgcolor: '#ff6b35', color: 'white' } }}>
<AutoAwesomeIcon color="primary" fontSize="small" sx={{ fontSize: 14 }} /> <AutoAwesomeIcon sx={{ fontSize: 16 }} />
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}> </Badge>
AI Insights: {aiGenerating ? 'Generating...' : 'Ready'} </IconButton>
</Typography> </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>
</Box> </Box>
{/* Icons moved from Region A to Region B - Now integrated into Progress title area */} {/* Combined info line */}
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'center', mt: 1.5 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{/* AI Recommendations Button - Compact */} <CheckCircleIcon color="success" sx={{ fontSize: 14 }} />
<MuiTooltip title="View AI-powered recommendations and insights" placement="top"> <Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
<motion.div Auto-population: {Object.keys(autoPopulatedFields || {}).length} fields AI Insights: {aiGenerating ? 'Generating...' : 'Ready'}
whileHover={{ scale: 1.05 }} </Typography>
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>
</Box> </Box>
</Box> </Box>
); );

View File

@@ -26,7 +26,7 @@ export const getCategoryColor = (categoryId: string): string => {
case 'competitive_intelligence': return 'success'; case 'competitive_intelligence': return 'success';
case 'content_strategy': return 'warning'; case 'content_strategy': return 'warning';
case 'performance_analytics': return 'info'; case 'performance_analytics': return 'info';
default: return 'default'; default: return 'primary';
} }
}; };

View File

@@ -14,7 +14,9 @@ import {
Alert, Alert,
IconButton, IconButton,
Collapse, Collapse,
Tooltip Tooltip,
Paper,
Grid
} from '@mui/material'; } from '@mui/material';
import { import {
DataUsage as DataUsageIcon, DataUsage as DataUsageIcon,
@@ -24,19 +26,26 @@ import {
Info as InfoIcon, Info as InfoIcon,
ExpandMore as ExpandMoreIcon, ExpandMore as ExpandMoreIcon,
ExpandLess as ExpandLessIcon, ExpandLess as ExpandLessIcon,
Refresh as RefreshIcon Refresh as RefreshIcon,
Timeline as TimelineIcon,
TrendingUp as TrendingUpIcon,
Schedule as ScheduleIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { motion } from 'framer-motion';
interface DataSourceTransparencyProps { interface DataSourceTransparencyProps {
autoPopulatedFields: Record<string, any>; autoPopulatedFields: Record<string, any>;
dataSources: Record<string, string>; dataSources: Record<string, string>;
inputDataPoints?: Record<string, any>; // Actual input data used to generate each field
} }
const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
autoPopulatedFields, autoPopulatedFields,
dataSources dataSources,
inputDataPoints = {}
}) => { }) => {
const [expanded, setExpanded] = React.useState(true); const [expanded, setExpanded] = React.useState(true);
const [showDataFlow, setShowDataFlow] = React.useState(false);
const getDataSourceIcon = (source: string) => { const getDataSourceIcon = (source: string) => {
const icons = { const icons = {
@@ -81,11 +90,99 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
return 'Low Quality'; 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]) => ({ const autoPopulatedFieldsList = Object.entries(autoPopulatedFields).map(([fieldId, value]) => ({
fieldId, fieldId,
value, value,
source: dataSources[fieldId] || 'unknown', 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]) => { 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 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 2 }}>
<DataUsageIcon color="primary" /> <DataUsageIcon color="primary" />
<Typography variant="h6"> <Typography variant="h6">
Data Sources Data Sources & Transparency
</Typography> </Typography>
<Chip <Chip
icon={<AutoAwesomeIcon />} icon={<AutoAwesomeIcon />}
@@ -130,6 +227,96 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
</Typography> </Typography>
</Alert> </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 */} {/* Data Sources Breakdown */}
<Box sx={{ mb: 2 }}> <Box sx={{ mb: 2 }}>
<Typography variant="subtitle2" gutterBottom> <Typography variant="subtitle2" gutterBottom>
@@ -169,9 +356,17 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
{Math.round(getDataQualityScore(source) * 100)}% {Math.round(getDataQualityScore(source) * 100)}%
</Typography> </Typography>
</Box> </Box>
<Typography variant="caption" color="text.secondary"> <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{getDataQualityLabel(getDataQualityScore(source))} <Typography variant="caption" color="text.secondary">
</Typography> {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> </Box>
} }
/> />
@@ -188,35 +383,73 @@ const DataSourceTransparency: React.FC<DataSourceTransparencyProps> = ({
Auto-populated Fields Auto-populated Fields
</Typography> </Typography>
<List dense> <List dense>
{autoPopulatedFieldsList.map((field, index) => ( {autoPopulatedFieldsList.map((field, index) => {
<React.Fragment key={field.fieldId}> const inputData = getInputDataPoints(field.fieldId);
<ListItem sx={{ px: 0 }}> const transformationInfo = getDataTransformationInfo(field.fieldId, field.source);
<ListItemIcon sx={{ minWidth: 40 }}>
<CheckCircleIcon color="success" fontSize="small" /> return (
</ListItemIcon> <React.Fragment key={field.fieldId}>
<ListItemText <ListItem sx={{ px: 0 }}>
primary={ <ListItemIcon sx={{ minWidth: 40 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> <CheckCircleIcon color="success" fontSize="small" />
<Typography variant="body2" fontWeight="medium"> </ListItemIcon>
{field.fieldId.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())} <ListItemText
</Typography> primary={
<Chip <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
label={getDataSourceLabel(field.source)} <Typography variant="body2" fontWeight="medium">
size="small" {field.fieldId.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}
variant="outlined" </Typography>
/> <Chip
</Box> label={getDataSourceLabel(field.source)}
} size="small"
secondary={ variant="outlined"
<Typography variant="caption" color="text.secondary"> />
Source: {getDataSourceLabel(field.source)} Quality: {getDataQualityLabel(field.qualityScore)} </Box>
</Typography> }
} secondary={
/> <Box sx={{ mt: 0.5 }}>
</ListItem> <Typography variant="caption" color="text.secondary">
{index < autoPopulatedFieldsList.length - 1 && <Divider />} Source: {getDataSourceLabel(field.source)} Quality: {getDataQualityLabel(field.qualityScore)}
</React.Fragment> </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> </List>
</Box> </Box>

View File

@@ -158,6 +158,7 @@ interface EnhancedStrategyStore {
formErrors: Record<string, string>; formErrors: Record<string, string>;
autoPopulatedFields: Record<string, any>; autoPopulatedFields: Record<string, any>;
dataSources: Record<string, string>; dataSources: Record<string, string>;
inputDataPoints: Record<string, any>; // Detailed input data points from backend
// UI State // UI State
loading: boolean; loading: boolean;
@@ -600,6 +601,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
formErrors: {}, formErrors: {},
autoPopulatedFields: {}, autoPopulatedFields: {},
dataSources: {}, dataSources: {},
inputDataPoints: {}, // Initialize inputDataPoints
// UI State // UI State
loading: false, loading: false,
@@ -719,6 +721,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
formErrors: {}, formErrors: {},
autoPopulatedFields: {}, autoPopulatedFields: {},
dataSources: {}, dataSources: {},
inputDataPoints: {}, // Reset inputDataPoints
currentStep: 0, currentStep: 0,
completedSteps: [] completedSteps: []
}); });
@@ -768,9 +771,11 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
// Extract field values and sources from the new backend format // Extract field values and sources from the new backend format
const fields = response.data?.fields || {}; const fields = response.data?.fields || {};
const sources = response.data?.sources || {}; const sources = response.data?.sources || {};
const inputDataPoints = response.data?.input_data_points || {};
console.log('📋 Extracted fields:', fields); console.log('📋 Extracted fields:', fields);
console.log('🔗 Data sources:', sources); console.log('🔗 Data sources:', sources);
console.log('📝 Input data points:', inputDataPoints);
// Transform the fields object to extract values for formData // Transform the fields object to extract values for formData
const fieldValues: Record<string, any> = {}; const fieldValues: Record<string, any> = {};
@@ -795,6 +800,7 @@ export const useEnhancedStrategyStore = create<EnhancedStrategyStore>((set, get)
set((state) => ({ set((state) => ({
autoPopulatedFields, autoPopulatedFields,
dataSources: sources, dataSources: sources,
inputDataPoints, // Store the detailed input data points
formData: { ...state.formData, ...fieldValues } formData: { ...state.formData, ...fieldValues }
})); }));