feat: category grouping in value table (Issue #47)
FEATURE: Gruppierung nach Kategorien
- Wertetabelle jetzt nach Modulen/Kategorien gruppiert
- Bessere Übersicht und Zuordnung der Werte
BACKEND: Category Metadata
- Für normale Platzhalter: Kategorie aus Catalog (Profil, Körper, Ernährung, etc.)
- Für extrahierte Werte: "Stage X - [Output Name]"
- Für Rohdaten: "Stage X - Rohdaten"
- Fallback: "Sonstiges"
FRONTEND: Grouped Display
- sortedCategories: Sortierung (Normal → Stage Outputs → Rohdaten)
- Section Headers: Grauer Hintergrund mit Kategorie-Name
- React.Fragment für Gruppierung
SORTIERUNG:
1. Normale Kategorien (Profil, Körper, Ernährung, Training, etc.)
2. Stage Outputs (Stage 1 - Body, Stage 1 - Nutrition, etc.)
3. Rohdaten (Stage 1 - Rohdaten, Stage 2 - Rohdaten)
4. Innerhalb: Alphabetisch
BEISPIEL:
┌────────────────────────────────────────────┐
│ PROFIL │
├────────────────────────────────────────────┤
│ name │ Lars │ Name des Nutzers │
│ age │ 55 │ Alter in Jahren │
├────────────────────────────────────────────┤
│ KÖRPER │
├────────────────────────────────────────────┤
│ weight_... │ 85.2 kg │ Aktuelles Gewicht │
│ bmi │ 26.6 │ Body Mass Index │
├────────────────────────────────────────────┤
│ ERNÄHRUNG │
├────────────────────────────────────────────┤
│ kcal_avg │ 1427... │ Durchschn. Kalorien│
│ protein... │ 106g... │ Durchschn. Protein │
├────────────────────────────────────────────┤
│ STAGE 1 - BODY │
├────────────────────────────────────────────┤
│ ↳ bmi │ 26.6 │ Aus Stage 1 (body) │
│ ↳ trend │ sinkend │ Aus Stage 1 (body) │
├────────────────────────────────────────────┤
│ STAGE 1 - NUTRITION │
├────────────────────────────────────────────┤
│ ↳ kcal_... │ 1427 │ Aus Stage 1 (nutr.)│
└────────────────────────────────────────────┘
Experten-Modus zusätzlich:
├────────────────────────────────────────────┤
│ STAGE 1 - ROHDATEN │
├────────────────────────────────────────────┤
│ 🔬 stage...│ {"bmi"..│ Rohdaten Stage 1 │
└────────────────────────────────────────────┘
version: 9.10.0 (feature)
module: prompts 2.5.0, insights 1.8.0
This commit is contained in:
parent
da803da816
commit
adb5dcea88
|
|
@ -840,17 +840,20 @@ async def execute_unified_prompt(
|
|||
# Get full untruncated value
|
||||
value = cleaned_values.get(key, result['debug']['resolved_placeholders'].get(key, ''))
|
||||
|
||||
# Find description in catalog
|
||||
# Find description and category in catalog
|
||||
desc = None
|
||||
for cat_items in catalog.values():
|
||||
category = 'Sonstiges'
|
||||
for cat_name, cat_items in catalog.items():
|
||||
matching = [item for item in cat_items if item['key'] == key]
|
||||
if matching:
|
||||
desc = matching[0].get('description', '')
|
||||
category = cat_name
|
||||
break
|
||||
|
||||
metadata['placeholders'][key] = {
|
||||
'value': value,
|
||||
'description': desc or ''
|
||||
'description': desc or '',
|
||||
'category': category
|
||||
}
|
||||
|
||||
elif result['type'] == 'pipeline':
|
||||
|
|
@ -886,10 +889,15 @@ async def execute_unified_prompt(
|
|||
# Add extracted values from stage outputs (individual fields)
|
||||
for field_key, field_data in extracted_values.items():
|
||||
if field_key not in metadata['placeholders']:
|
||||
# Determine category for extracted values
|
||||
output_name = field_data['source_output'].replace('stage1_', '').replace('_', ' ').title()
|
||||
category = f"Stage {field_data['source_stage']} - {output_name}"
|
||||
|
||||
metadata['placeholders'][field_key] = {
|
||||
'value': field_data['value'],
|
||||
'description': f"Aus Stage {field_data['source_stage']} ({field_data['source_output']})",
|
||||
'is_extracted': True # Mark as extracted for filtering
|
||||
'is_extracted': True, # Mark as extracted for filtering
|
||||
'category': category
|
||||
}
|
||||
|
||||
# Collect all resolved placeholders from prompts (input placeholders)
|
||||
|
|
@ -909,15 +917,20 @@ async def execute_unified_prompt(
|
|||
|
||||
# For stage output placeholders (raw JSON), add special description
|
||||
if key.startswith('stage_'):
|
||||
desc = f"Rohdaten Stage {key.split('_')[1]} (Basis-Analyse JSON)"
|
||||
stage_parts = key.split('_')
|
||||
stage_num = stage_parts[1] if len(stage_parts) > 1 else '?'
|
||||
desc = f"Rohdaten Stage {stage_num} (Basis-Analyse JSON)"
|
||||
category = f"Stage {stage_num} - Rohdaten"
|
||||
is_stage_raw = True
|
||||
else:
|
||||
# Find description in catalog
|
||||
# Find description and category in catalog
|
||||
desc = None
|
||||
for cat_items in catalog.values():
|
||||
category = 'Sonstiges'
|
||||
for cat_name, cat_items in catalog.items():
|
||||
matching = [item for item in cat_items if item['key'] == key]
|
||||
if matching:
|
||||
desc = matching[0].get('description', '')
|
||||
category = cat_name
|
||||
break
|
||||
desc = desc or ''
|
||||
is_stage_raw = False
|
||||
|
|
@ -925,7 +938,8 @@ async def execute_unified_prompt(
|
|||
metadata['placeholders'][key] = {
|
||||
'value': value if isinstance(value, str) else json.dumps(value, ensure_ascii=False),
|
||||
'description': desc,
|
||||
'is_stage_raw': is_stage_raw # Mark raw stage outputs for expert mode
|
||||
'is_stage_raw': is_stage_raw, # Mark raw stage outputs for expert mode
|
||||
'category': category
|
||||
}
|
||||
|
||||
# Save to database with metadata
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Brain, Trash2, ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import { api } from '../utils/api'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
|
|
@ -51,6 +51,33 @@ function InsightCard({ ins, onDelete, defaultOpen=false, prompts=[] }) {
|
|||
const placeholderCount = Object.keys(placeholders).length
|
||||
const hiddenCount = Object.keys(allPlaceholders).length - placeholderCount
|
||||
|
||||
// Group placeholders by category
|
||||
const groupedPlaceholders = Object.entries(placeholders).reduce((acc, [key, data]) => {
|
||||
const category = data.category || 'Sonstiges'
|
||||
if (!acc[category]) acc[category] = []
|
||||
acc[category].push([key, data])
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
// Sort categories: Regular categories first, then Stage outputs, then Rohdaten
|
||||
const sortedCategories = Object.keys(groupedPlaceholders).sort((a, b) => {
|
||||
const aIsStage = a.startsWith('Stage')
|
||||
const bIsStage = b.startsWith('Stage')
|
||||
const aIsRohdaten = a.includes('Rohdaten')
|
||||
const bIsRohdaten = b.includes('Rohdaten')
|
||||
|
||||
// Rohdaten last
|
||||
if (aIsRohdaten && !bIsRohdaten) return 1
|
||||
if (!aIsRohdaten && bIsRohdaten) return -1
|
||||
|
||||
// Stage outputs after regular categories
|
||||
if (!aIsStage && bIsStage) return -1
|
||||
if (aIsStage && !bIsStage) return 1
|
||||
|
||||
// Otherwise alphabetical
|
||||
return a.localeCompare(b)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="card section-gap" style={{borderLeft:`3px solid var(--accent)`}}>
|
||||
<div style={{display:'flex',alignItems:'center',gap:8,marginBottom:open?12:0,cursor:'pointer'}}
|
||||
|
|
@ -152,7 +179,22 @@ function InsightCard({ ins, onDelete, defaultOpen=false, prompts=[] }) {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.entries(placeholders).map(([key, data]) => {
|
||||
{sortedCategories.map(category => (
|
||||
<React.Fragment key={category}>
|
||||
{/* Category Header */}
|
||||
<tr style={{ background: 'var(--surface2)', borderTop: '2px solid var(--border)' }}>
|
||||
<td colSpan="3" style={{
|
||||
padding: '8px',
|
||||
fontWeight: 600,
|
||||
fontSize: 11,
|
||||
color: 'var(--text2)',
|
||||
letterSpacing: '0.5px'
|
||||
}}>
|
||||
{category}
|
||||
</td>
|
||||
</tr>
|
||||
{/* Category Values */}
|
||||
{groupedPlaceholders[category].map(([key, data]) => {
|
||||
const isExtracted = data.is_extracted
|
||||
const isStageRaw = data.is_stage_raw
|
||||
|
||||
|
|
@ -194,8 +236,10 @@ function InsightCard({ ins, onDelete, defaultOpen=false, prompts=[] }) {
|
|||
{data.description || '—'}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
)
|
||||
})}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user