feat(style-acq T6+T13): פנקס-התאמה + מדד מרחק-סגנון ב-UI
ה"איך מנהלים/רואים את הלמידה": טאב "למידה" ב-/training.
- app.py: GET /api/learning/pairs (פנקס-ההתאמה — כל ההחלטות + סטטוס draft↔final,
INV-LRN4) + GET /api/learning/style-distance/{case} (מדד T7).
- web-ui: learning.ts hooks + LearningPanel (טבלת פנקס; לחיצה על תיק →
מדד מרחק-הסגנון: שינוי draft→final, סטיית יחסי-זהב, אנטי-דפוסים) + טאב ב-/training.
מכסה גם את T6 (רשימת כל ההחלטות הנסגרות מול הסופי). ללא endpoint-schema חדש
לטיפוסים מחוללים (טיפוסים ידניים). G9, INV-LRN4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
69
web-ui/src/lib/api/learning.ts
Normal file
69
web-ui/src/lib/api/learning.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Style-acquisition learning surface (T6/T13) — reconciliation ledger + style-distance.
|
||||
* Backs the /training "למידה" tab. Endpoints under /api/learning.
|
||||
*/
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { apiRequest } from "./client";
|
||||
|
||||
export type DraftFinalPair = {
|
||||
id: string;
|
||||
case_id: string | null;
|
||||
case_number: string;
|
||||
title: string;
|
||||
status: "final_received" | "analyzed" | "lessons_folded" | string;
|
||||
change_percent: number | null;
|
||||
created_at: string | null;
|
||||
updated_at: string | null;
|
||||
};
|
||||
|
||||
export type StyleDistance = {
|
||||
case_number: string;
|
||||
outcome: string;
|
||||
golden_ratio_adherence: {
|
||||
outcome: string;
|
||||
total_words: number;
|
||||
sections: Record<string, { actual_pct: number; target: [number, number]; deviation_pp: number }>;
|
||||
max_deviation: number | null;
|
||||
};
|
||||
anti_pattern_hits: { total: number; by_pattern: Record<string, { count: number; note: string }> };
|
||||
draft_to_final_diff: { change_percent?: number } | null;
|
||||
pair_status: string | null;
|
||||
summary: {
|
||||
ratio_max_deviation_pp: number | null;
|
||||
anti_pattern_total: number;
|
||||
change_percent: number | null;
|
||||
};
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export const learningKeys = {
|
||||
all: ["learning"] as const,
|
||||
pairs: (status: string) => [...learningKeys.all, "pairs", status] as const,
|
||||
distance: (caseNumber: string) => [...learningKeys.all, "distance", caseNumber] as const,
|
||||
};
|
||||
|
||||
export function useReconciliationLedger(status = "") {
|
||||
return useQuery({
|
||||
queryKey: learningKeys.pairs(status),
|
||||
queryFn: ({ signal }) =>
|
||||
apiRequest<{ items: DraftFinalPair[]; count: number }>(
|
||||
`/api/learning/pairs${status ? `?status=${status}` : ""}`,
|
||||
{ signal },
|
||||
),
|
||||
staleTime: 15_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useStyleDistance(caseNumber: string | null) {
|
||||
return useQuery({
|
||||
queryKey: learningKeys.distance(caseNumber ?? ""),
|
||||
queryFn: ({ signal }) =>
|
||||
apiRequest<StyleDistance>(
|
||||
`/api/learning/style-distance/${encodeURIComponent(caseNumber!)}`,
|
||||
{ signal },
|
||||
),
|
||||
enabled: Boolean(caseNumber),
|
||||
staleTime: 15_000,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user