Version 9b #1
|
|
@ -179,6 +179,10 @@ export default function Analysis() {
|
||||||
|
|
||||||
const activePrompts = prompts.filter(p=>p.active && !p.slug.startsWith('pipeline_'))
|
const activePrompts = prompts.filter(p=>p.active && !p.slug.startsWith('pipeline_'))
|
||||||
|
|
||||||
|
// Pipeline is available only if ALL pipeline sub-prompts are active
|
||||||
|
const pipelineSlugs = ['pipeline_body','pipeline_nutrition','pipeline_activity','pipeline_synthesis','pipeline_goals']
|
||||||
|
const pipelineAvailable = pipelineSlugs.every(slug => prompts.find(p=>p.slug===slug)?.active)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="page-title">KI-Analyse</h1>
|
<h1 className="page-title">KI-Analyse</h1>
|
||||||
|
|
@ -221,7 +225,8 @@ export default function Analysis() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Pipeline button */}
|
{/* Pipeline button - only if all sub-prompts are active */}
|
||||||
|
{pipelineAvailable && (
|
||||||
<div className="card" style={{marginBottom:16,borderColor:'var(--accent)',borderWidth:2}}>
|
<div className="card" style={{marginBottom:16,borderColor:'var(--accent)',borderWidth:2}}>
|
||||||
<div style={{display:'flex',alignItems:'flex-start',gap:12}}>
|
<div style={{display:'flex',alignItems:'flex-start',gap:12}}>
|
||||||
<div style={{flex:1}}>
|
<div style={{flex:1}}>
|
||||||
|
|
@ -251,6 +256,7 @@ export default function Analysis() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{!canUseAI && (
|
{!canUseAI && (
|
||||||
<div style={{padding:'14px 16px',background:'#FCEBEB',borderRadius:10,
|
<div style={{padding:'14px 16px',background:'#FCEBEB',borderRadius:10,
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ function PeriodSelector({ value, onChange }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Body Section (Weight + Composition combined) ──────────────────────────────
|
// ── Body Section (Weight + Composition combined) ──────────────────────────────
|
||||||
function BodySection({ weights, calipers, circs, profile, insights, onRequest, loadingSlug }) {
|
function BodySection({ weights, calipers, circs, profile, insights, onRequest, loadingSlug, filterActiveSlugs }) {
|
||||||
const [period, setPeriod] = useState(90)
|
const [period, setPeriod] = useState(90)
|
||||||
const sex = profile?.sex||'m'
|
const sex = profile?.sex||'m'
|
||||||
const height = profile?.height||178
|
const height = profile?.height||178
|
||||||
|
|
@ -394,14 +394,14 @@ function BodySection({ weights, calipers, circs, profile, insights, onRequest, l
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<InsightBox insights={insights} slugs={['pipeline','koerper','gesundheit','ziele']}
|
<InsightBox insights={insights} slugs={filterActiveSlugs(['pipeline','koerper','gesundheit','ziele'])}
|
||||||
onRequest={onRequest} loading={loadingSlug}/>
|
onRequest={onRequest} loading={loadingSlug}/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Nutrition Section ─────────────────────────────────────────────────────────
|
// ── Nutrition Section ─────────────────────────────────────────────────────────
|
||||||
function NutritionSection({ nutrition, weights, profile, insights, onRequest, loadingSlug }) {
|
function NutritionSection({ nutrition, weights, profile, insights, onRequest, loadingSlug, filterActiveSlugs }) {
|
||||||
const [period, setPeriod] = useState(30)
|
const [period, setPeriod] = useState(30)
|
||||||
if (!nutrition?.length) return (
|
if (!nutrition?.length) return (
|
||||||
<EmptySection text="Noch keine Ernährungsdaten." to="/nutrition" toLabel="FDDB importieren"/>
|
<EmptySection text="Noch keine Ernährungsdaten." to="/nutrition" toLabel="FDDB importieren"/>
|
||||||
|
|
@ -579,13 +579,13 @@ function NutritionSection({ nutrition, weights, profile, insights, onRequest, lo
|
||||||
<div style={{fontSize:12,fontWeight:600,color:'var(--text3)',marginBottom:8}}>BEWERTUNG</div>
|
<div style={{fontSize:12,fontWeight:600,color:'var(--text3)',marginBottom:8}}>BEWERTUNG</div>
|
||||||
{macroRules.map((item,i)=><RuleCard key={i} item={item}/>)}
|
{macroRules.map((item,i)=><RuleCard key={i} item={item}/>)}
|
||||||
</div>
|
</div>
|
||||||
<InsightBox insights={insights} slugs={['ernaehrung']} onRequest={onRequest} loading={loadingSlug}/>
|
<InsightBox insights={insights} slugs={filterActiveSlugs(['ernaehrung'])} onRequest={onRequest} loading={loadingSlug}/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Activity Section ──────────────────────────────────────────────────────────
|
// ── Activity Section ──────────────────────────────────────────────────────────
|
||||||
function ActivitySection({ activities, insights, onRequest, loadingSlug }) {
|
function ActivitySection({ activities, insights, onRequest, loadingSlug, filterActiveSlugs }) {
|
||||||
const [period, setPeriod] = useState(30)
|
const [period, setPeriod] = useState(30)
|
||||||
if (!activities?.length) return (
|
if (!activities?.length) return (
|
||||||
<EmptySection text="Noch keine Aktivitätsdaten." to="/activity" toLabel="Aktivität erfassen"/>
|
<EmptySection text="Noch keine Aktivitätsdaten." to="/activity" toLabel="Aktivität erfassen"/>
|
||||||
|
|
@ -657,13 +657,13 @@ function ActivitySection({ activities, insights, onRequest, loadingSlug }) {
|
||||||
<div style={{fontSize:12,fontWeight:600,color:'var(--text3)',marginBottom:8}}>BEWERTUNG</div>
|
<div style={{fontSize:12,fontWeight:600,color:'var(--text3)',marginBottom:8}}>BEWERTUNG</div>
|
||||||
{actRules.map((item,i)=><RuleCard key={i} item={item}/>)}
|
{actRules.map((item,i)=><RuleCard key={i} item={item}/>)}
|
||||||
</div>
|
</div>
|
||||||
<InsightBox insights={insights} slugs={['aktivitaet']} onRequest={onRequest} loading={loadingSlug}/>
|
<InsightBox insights={insights} slugs={filterActiveSlugs(['aktivitaet'])} onRequest={onRequest} loading={loadingSlug}/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Correlation Section ───────────────────────────────────────────────────────
|
// ── Correlation Section ───────────────────────────────────────────────────────
|
||||||
function CorrelationSection({ corrData, insights, profile, onRequest, loadingSlug }) {
|
function CorrelationSection({ corrData, insights, profile, onRequest, loadingSlug, filterActiveSlugs }) {
|
||||||
const filtered = (corrData||[]).filter(d=>d.kcal&&d.weight)
|
const filtered = (corrData||[]).filter(d=>d.kcal&&d.weight)
|
||||||
if (filtered.length < 5) return (
|
if (filtered.length < 5) return (
|
||||||
<EmptySection text="Für Korrelationen werden Gewichts- und Ernährungsdaten benötigt (mind. 5 gemeinsame Tage)."/>
|
<EmptySection text="Für Korrelationen werden Gewichts- und Ernährungsdaten benötigt (mind. 5 gemeinsame Tage)."/>
|
||||||
|
|
@ -852,7 +852,7 @@ function CorrelationSection({ corrData, insights, profile, onRequest, loadingSlu
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<InsightBox insights={insights} slugs={['gesamt','ziele']} onRequest={onRequest} loading={loadingSlug}/>
|
<InsightBox insights={insights} slugs={filterActiveSlugs(['gesamt','ziele'])} onRequest={onRequest} loading={loadingSlug}/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -903,6 +903,7 @@ export default function History() {
|
||||||
const [activities, setActivities] = useState([])
|
const [activities, setActivities] = useState([])
|
||||||
const [corrData, setCorrData] = useState([])
|
const [corrData, setCorrData] = useState([])
|
||||||
const [insights, setInsights] = useState([])
|
const [insights, setInsights] = useState([])
|
||||||
|
const [prompts, setPrompts] = useState([])
|
||||||
const [profile, setProfile] = useState(null)
|
const [profile, setProfile] = useState(null)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [loadingSlug,setLoadingSlug]= useState(null)
|
const [loadingSlug,setLoadingSlug]= useState(null)
|
||||||
|
|
@ -911,10 +912,12 @@ export default function History() {
|
||||||
api.listWeight(365), api.listCaliper(), api.listCirc(),
|
api.listWeight(365), api.listCaliper(), api.listCirc(),
|
||||||
api.listNutrition(90), api.listActivity(200),
|
api.listNutrition(90), api.listActivity(200),
|
||||||
api.nutritionCorrelations(), api.latestInsights(), api.getProfile(),
|
api.nutritionCorrelations(), api.latestInsights(), api.getProfile(),
|
||||||
]).then(([w,ca,ci,n,a,corr,ins,p])=>{
|
api.listPrompts(),
|
||||||
|
]).then(([w,ca,ci,n,a,corr,ins,p,pr])=>{
|
||||||
setWeights(w); setCalipers(ca); setCircs(ci)
|
setWeights(w); setCalipers(ca); setCircs(ci)
|
||||||
setNutrition(n); setActivities(a); setCorrData(corr)
|
setNutrition(n); setActivities(a); setCorrData(corr)
|
||||||
setInsights(Array.isArray(ins)?ins:[]); setProfile(p)
|
setInsights(Array.isArray(ins)?ins:[]); setProfile(p)
|
||||||
|
setPrompts(Array.isArray(pr)?pr:[])
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -923,17 +926,23 @@ export default function History() {
|
||||||
const requestInsight = async (slug) => {
|
const requestInsight = async (slug) => {
|
||||||
setLoadingSlug(slug)
|
setLoadingSlug(slug)
|
||||||
try {
|
try {
|
||||||
const pid=localStorage.getItem('bodytrack_active_profile')||''
|
const result = await api.runInsight(slug)
|
||||||
const r=await api.runInsight(slug)
|
// result is already JSON, not a Response object
|
||||||
if(!r.ok) throw new Error(await r.text())
|
const ins = await api.latestInsights()
|
||||||
const ins=await api.latestInsights()
|
|
||||||
setInsights(Array.isArray(ins)?ins:[])
|
setInsights(Array.isArray(ins)?ins:[])
|
||||||
} catch(e){ alert('KI-Fehler: '+e.message) }
|
} catch(e){
|
||||||
|
alert('KI-Fehler: '+e.message)
|
||||||
|
}
|
||||||
finally{ setLoadingSlug(null) }
|
finally{ setLoadingSlug(null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if(loading) return <div className="empty-state"><div className="spinner"/></div>
|
if(loading) return <div className="empty-state"><div className="spinner"/></div>
|
||||||
const sp={insights,onRequest:requestInsight,loadingSlug}
|
|
||||||
|
// Filter active prompts
|
||||||
|
const activeSlugs = prompts.filter(p=>p.active).map(p=>p.slug)
|
||||||
|
const filterActiveSlugs = (slugs) => slugs.filter(s=>activeSlugs.includes(s))
|
||||||
|
|
||||||
|
const sp={insights,onRequest:requestInsight,loadingSlug,filterActiveSlugs}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user