ai insight rework #3, add root cause to daily ai insight
This commit is contained in:
1 parent
c963bb778c
commit
d5b0c12c40
8 files changed
+385
-47
No files matched your search
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Dashboard AI Insight card (page / daily / weekly / end_cycle report modes).
|
||||
* Dashboard AI Insight card (daily / end_cycle report modes).
|
||||
*
|
||||
* Parallel client to `PageAiInsight` (topic pages) — same API, different UX.
|
||||
* Architecture: docs/ai-insight/README.md
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
scopeDashboardInsightMetrics,
|
||||
} from '../utils/insightContextBuilders.ts';
|
||||
import { getCobbStandardForDay } from '../utils/insightHelpers.ts';
|
||||
import { parseAiResult, type AiResult } from '../utils/insightParse.ts';
|
||||
import { formatRatio } from '../utils/format.ts';
|
||||
import {
|
||||
HeroFigure,
|
||||
@@ -253,6 +254,7 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
const { selectedCycle, selectedKandang, selectedSite, selectedKandangId } = useFarm();
|
||||
|
||||
const [insightText, setInsightText] = useState('');
|
||||
const [dailyInsight, setDailyInsight] = useState<AiResult | null>(null);
|
||||
const [structuredInsight, setStructuredInsight] = useState<InsightStructuredReport | null>(null);
|
||||
const [endCycleInsight, setEndCycleInsight] = useState<EndCycleStructuredInsight | null>(null);
|
||||
const [citations, setCitations] = useState<InsightCitation[]>([]);
|
||||
@@ -275,9 +277,7 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
reportType === 'daily'
|
||||
? (dayPeriod ??
|
||||
(typeof currentDayVal === 'number' && currentDayVal >= 0 ? `Hari ${currentDayVal}` : null))
|
||||
: reportType === 'weekly'
|
||||
? dayPeriod
|
||||
: null;
|
||||
: null;
|
||||
|
||||
const scopedMetrics = useMemo(() => {
|
||||
if (!dashboardMetrics) return null;
|
||||
@@ -293,7 +293,9 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
text: string,
|
||||
nextCitations?: InsightCitation[] | 'cache' | 'local_fallback',
|
||||
structuredEndCycle?: EndCycleStructuredInsight | null,
|
||||
summary?: string | null
|
||||
summary?: string | null,
|
||||
akarMasalah?: string | null,
|
||||
insightBody?: string | null
|
||||
) => {
|
||||
const citationsArg = Array.isArray(nextCitations) ? nextCitations : [];
|
||||
setInsightText(text);
|
||||
@@ -305,6 +307,22 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
// Prefer end-cycle structured UI over legacy multi-topic structured dump.
|
||||
setStructuredInsight(endCycle ? null : normalizeStructuredInsight(text));
|
||||
setCitations(citationsArg);
|
||||
|
||||
if (!endCycle) {
|
||||
if (summary || akarMasalah || insightBody) {
|
||||
setDailyInsight({
|
||||
summary: summary || '',
|
||||
akar_masalah: akarMasalah || undefined,
|
||||
insight: insightBody || text || '',
|
||||
});
|
||||
} else if (text) {
|
||||
setDailyInsight(parseAiResult(text));
|
||||
} else {
|
||||
setDailyInsight(null);
|
||||
}
|
||||
} else {
|
||||
setDailyInsight(null);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
@@ -451,7 +469,9 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
apiResult.insight_text,
|
||||
apiResult.citations,
|
||||
apiResult.structured_end_cycle ?? null,
|
||||
apiResult.summary
|
||||
apiResult.summary,
|
||||
apiResult.akar_masalah,
|
||||
apiResult.insight
|
||||
);
|
||||
setLastUpdated(new Date());
|
||||
setDetailsOpen(false);
|
||||
@@ -1013,11 +1033,45 @@ export const AiInsightCard: React.FC<AiInsightCardProps> = ({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : dailyInsight ? (
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-xl border border-gray-100 bg-white p-5 shadow-sm">
|
||||
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
|
||||
<i className="fa-solid fa-circle-check text-green-600" /> Kesimpulan
|
||||
</h4>
|
||||
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg border border-gray-100">
|
||||
{dailyInsight.summary}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{dailyInsight.akar_masalah && (
|
||||
<div className="rounded-xl border border-amber-100/80 bg-amber-50/40 p-5 shadow-sm">
|
||||
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
|
||||
<i className="fa-solid fa-magnifying-glass-chart text-amber-600" /> Akar Masalah
|
||||
</h4>
|
||||
<p className="text-sm text-gray-700 bg-white/80 p-3 rounded-lg border border-amber-100/80">
|
||||
{dailyInsight.akar_masalah}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="rounded-xl border border-gray-100 bg-white p-5 shadow-sm">
|
||||
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
|
||||
<i className="fa-solid fa-brain text-red-600" /> AI Insight & Rekomendasi
|
||||
</h4>
|
||||
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg border border-gray-100 whitespace-pre-wrap">
|
||||
{dailyInsight.insight}
|
||||
</p>
|
||||
<CitationsBlock citations={citations} />
|
||||
</div>
|
||||
</div>
|
||||
) : insightText ? (
|
||||
<div className="text-sm text-gray-800 leading-relaxed whitespace-pre-wrap">
|
||||
{insightText.includes(END_CYCLE_JSON_MARKER)
|
||||
? insightText.split(END_CYCLE_JSON_MARKER)[0]?.trim()
|
||||
: insightText}
|
||||
: insightText.includes('<!--daily_json-->')
|
||||
? insightText.split('<!--daily_json-->')[0]?.trim()
|
||||
: insightText}
|
||||
<CitationsBlock citations={citations} />
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -603,10 +603,11 @@ export const PageAiInsight: React.FC<PageAiInsightProps> = ({
|
||||
}
|
||||
|
||||
const text = apiResult.insight_text ?? '';
|
||||
let parsed: { summary: string; insight: string } | null = null;
|
||||
if (apiResult.summary || apiResult.insight) {
|
||||
let parsed: AiResult | null = null;
|
||||
if (apiResult.summary || apiResult.insight || apiResult.akar_masalah) {
|
||||
parsed = {
|
||||
summary: apiResult.summary || '',
|
||||
akar_masalah: apiResult.akar_masalah || undefined,
|
||||
insight: apiResult.insight || apiResult.insight_text || '',
|
||||
};
|
||||
} else {
|
||||
@@ -619,6 +620,7 @@ export const PageAiInsight: React.FC<PageAiInsightProps> = ({
|
||||
|
||||
const wrapped: PageInsightResult = {
|
||||
summary: parsed.summary,
|
||||
akar_masalah: parsed.akar_masalah,
|
||||
insight: parsed.insight,
|
||||
citations: apiResult.citations,
|
||||
};
|
||||
@@ -1041,9 +1043,20 @@ export const PageAiInsight: React.FC<PageAiInsightProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{result.akar_masalah && (
|
||||
<div className="mt-4">
|
||||
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
|
||||
<i className="fa-solid fa-magnifying-glass-chart text-amber-600" /> Akar Masalah
|
||||
</h4>
|
||||
<p className="text-sm text-gray-700 bg-amber-50/50 p-3 rounded-lg border border-amber-100/80">
|
||||
{result.akar_masalah}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-4">
|
||||
<h4 className="text-sm font-bold text-gray-800 mb-2 flex items-center gap-2">
|
||||
<i className="fa-solid fa-brain text-red-600" /> AI Insight
|
||||
<i className="fa-solid fa-brain text-red-600" /> AI Insight & Rekomendasi
|
||||
</h4>
|
||||
<p className="text-sm text-gray-700 bg-gray-50 p-3 rounded-lg border border-gray-100 whitespace-pre-wrap">
|
||||
{result.insight}
|
||||
|
||||
Reference in new issue
Block a user