const rate = (v) => `${(v * 100).toFixed(2).replace(".", ",")}%`; export function SkippedRows({ audit, legacy = false, status = "completed" }) { if (!audit) { return legacy ?

Rincian baris yang dilewati belum dicatat untuk versi lama ini.

: null; } return (

{audit.uploaded_rows} diunggah → {audit.accepted_rows} diterima →{" "} {audit.skipped_rows.length} dilewati.

{audit.selected_column && (

Kolom yang digunakan: {audit.selected_column}

)}

{status === "completed" && audit.accepted_rows > 0 ? `Data latih: ${audit.training_before} sebelum penambahan → ${audit.training_before + audit.accepted_rows} setelah training berhasil.` : `Data latih tetap ${audit.training_before}.`}

{!!audit.skipped_rows.length && (
Lihat Data yang Dilewati ({audit.skipped_rows.length})

Nomor baris adalah urutan record CSV, termasuk judul kolom sebagai baris 1. Ulasan dengan baris baru di dalam tanda kutip dihitung satu record.

{audit.skipped_rows.map((r) => ( ))}
Baris CSV Ulasan Alasan dilewati
{r.source_row_number} {r.text || "(kosong)"} {r.reason}
)}
); } export function VersionComparison({ selected, original }) { if (!original || selected.version === "v1") { return (

Model penelitian awal: {selected.jumlah_data_training} data latih dan {selected.jumlah_data_testing} data uji.

); } const metrics = [ ["Akurasi", m => m.akurasi], ["Precision", m => m.classification_report["weighted avg"].precision], ["Recall", m => m.classification_report["weighted avg"].recall], ["F1-Score", m => m.classification_report["weighted avg"]["f1-score"]] ]; return (

Perbandingan dengan model awal v1

{selected.jumlah_data_training} data latih. Evaluasi pada{" "} {selected.jumlah_data_testing} data uji tetap yang sama.

{metrics.map(([name, get]) => ( ))}
Metrik v1 {selected.version} Selisih (poin persentase)
{name} {rate(get(original))} {rate(get(selected))} {((get(selected) - get(original)) * 100).toFixed(2)}

Precision, Recall, dan F1-Score menggunakan weighted average, yaitu rata-rata yang memperhitungkan jumlah data pada setiap kelas.

); }