This commit is contained in:
everythingonblack
2025-10-10 04:46:53 +07:00
parent fbabd2ab64
commit 3deb2ea030

View File

@@ -1,16 +1,8 @@
import React, { useState, useRef, useEffect, useCallback } from 'react'; import React, { useState, useRef, useEffect, useCallback } from 'react';
import { useParams, Link, useLocation } from 'react-router-dom'; import { useParams, Link, useLocation } from 'react-router-dom';
import CameraModal from './CameraModal'; import CameraModal from './CameraModal';
import { fetchEntries } from '../api';
// --- MOCK API LOGIC ---
const fetchEntries = async (dataTypeId) => {
console.log(`Fetching entries for data_type_id: ${dataTypeId}`);
return new Promise(resolve => {
setTimeout(() => {
resolve([]);
}, 500);
});
};
// Icons // Icons
const BackIcon = () => (<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" /></svg>); const BackIcon = () => (<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" /></svg>);
@@ -44,6 +36,7 @@ export default function InputDataPage() {
setLoadingEntries(true); setLoadingEntries(true);
try { try {
const data = await fetchEntries(data_type_id); const data = await fetchEntries(data_type_id);
console.log(data)
setEntries(data); setEntries(data);
} catch (error) { } catch (error) {
console.error("Error fetch entries:", error); console.error("Error fetch entries:", error);
@@ -69,7 +62,7 @@ export default function InputDataPage() {
return [...prevFiles, ...uniqueNewFiles]; return [...prevFiles, ...uniqueNewFiles];
}); });
}; };
const handleCaptureComplete = (imageFile) => { const handleCaptureComplete = (imageFile) => {
if (imageFile) { if (imageFile) {
handleFiles([imageFile]); handleFiles([imageFile]);
@@ -80,7 +73,7 @@ export default function InputDataPage() {
const removeFile = (index) => { const removeFile = (index) => {
setFilesToUpload(prevFiles => prevFiles.filter((_, i) => i !== index)); setFilesToUpload(prevFiles => prevFiles.filter((_, i) => i !== index));
}; };
const handleUpload = async () => { const handleUpload = async () => {
if (filesToUpload.length === 0) return; if (filesToUpload.length === 0) return;
setIsUploading(true); setIsUploading(true);
@@ -110,7 +103,7 @@ export default function InputDataPage() {
saved: false, saved: false,
}); });
} else { } else {
console.warn(`Hasil scan kosong untuk file ${file.name}`); console.warn(`Hasil scan kosong untuk file ${file.name}`);
} }
} catch (error) { } catch (error) {
console.error(`Gagal mengupload ${file.name}:`, error); console.error(`Gagal mengupload ${file.name}:`, error);
@@ -135,6 +128,7 @@ export default function InputDataPage() {
const renderField = (key, value) => { const renderField = (key, value) => {
let type = "text"; let type = "text";
let options = []; let options = [];
if (typeof expectation[key] === "object" && expectation[key] !== null) { if (typeof expectation[key] === "object" && expectation[key] !== null) {
type = expectation[key].type; type = expectation[key].type;
options = expectation[key].options || []; options = expectation[key].options || [];
@@ -142,12 +136,60 @@ export default function InputDataPage() {
type = expectation[key] || "text"; type = expectation[key] || "text";
} }
if (type === "date") return <input type="date" value={value} onChange={(e) => updateField(key, e.target.value)} className="mt-1 block w-full border border-gray-300 rounded-md p-1" />; const commonClass = "mt-1 block w-full border border-gray-300 rounded-md p-1";
if (type === "selection") return <select value={value} onChange={(e) => updateField(key, e.target.value)} className="mt-1 block w-full border border-gray-300 rounded-md p-1">{options.map((opt, idx) => (<option key={idx} value={opt}>{opt}</option>))}</select>;
if (type === "number") return <input type="number" value={value} onChange={(e) => updateField(key, e.target.value)} className="mt-1 block w-full border border-gray-300 rounded-md p-1" />; if (type === "date") {
return <input type="text" value={value} onChange={(e) => updateField(key, e.target.value)} className="mt-1 block w-full border border-gray-300 rounded-md p-1" />; return (
<input
type="date"
value={value || ""}
placeholder="Pilih tanggal"
onChange={(e) => updateField(key, e.target.value)}
className={commonClass}
/>
);
}
if (type === "selection") {
return (
<select
value={value || ""}
onChange={(e) => updateField(key, e.target.value)}
className={commonClass}
>
<option disabled value="">-- Pilih salah satu --</option>
{(options || []).map((opt, idx) => (
<option key={idx} value={opt}>{opt}</option>
))}
</select>
);
}
if (type === "number") {
return (
<input
type="number"
value={value || ""}
placeholder="Masukkan angka"
onChange={(e) => updateField(key, e.target.value)}
className={commonClass}
/>
);
}
// Default: text input
return (
<input
type="text"
value={value || ""}
placeholder="Masukkan teks"
onChange={(e) => updateField(key, e.target.value)}
className={commonClass}
/>
);
}; };
const toggleDelete = () => { const toggleDelete = () => {
const newResults = [...uploadResults]; const newResults = [...uploadResults];
if (newResults[resultIndex]) { if (newResults[resultIndex]) {
@@ -155,7 +197,7 @@ export default function InputDataPage() {
setUploadResults(newResults); setUploadResults(newResults);
} }
}; };
const handleSaveAllValid = async () => { const handleSaveAllValid = async () => {
const itemsToSave = uploadResults.filter(r => !r.deleted && !r.saved); const itemsToSave = uploadResults.filter(r => !r.deleted && !r.saved);
if (itemsToSave.length === 0) { if (itemsToSave.length === 0) {
@@ -230,7 +272,7 @@ export default function InputDataPage() {
</div> </div>
); );
}; };
const currentResult = uploadResults[resultIndex]; const currentResult = uploadResults[resultIndex];
const fieldsPerPage = 3; const fieldsPerPage = 3;
const allFields = currentResult?.formData ? Object.entries(currentResult.formData) : []; const allFields = currentResult?.formData ? Object.entries(currentResult.formData) : [];
@@ -294,7 +336,7 @@ export default function InputDataPage() {
</div> </div>
{renderEntriesTable()} {renderEntriesTable()}
</main> </main>
{modalVisible && currentResult && ( {modalVisible && currentResult && (
<div className="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center z-50 p-4"> <div className="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg shadow-2xl w-full max-w-4xl max-h-[95vh] flex flex-col"> <div className="bg-white rounded-lg shadow-2xl w-full max-w-4xl max-h-[95vh] flex flex-col">
@@ -318,7 +360,7 @@ export default function InputDataPage() {
<TrashIcon /> Hapus Data Ini <TrashIcon /> Hapus Data Ini
</button> </button>
</aside> </aside>
<section className="md:col-span-2 space-y-4 flex flex-col justify-between"> <section className="md:col-span-2 space-y-4 flex flex-col justify-between">
<div> <div>
{visibleFields.map(([key, value]) => ( {visibleFields.map(([key, value]) => (
@@ -328,7 +370,7 @@ export default function InputDataPage() {
</div> </div>
))} ))}
</div> </div>
{totalFieldPages > 1 && ( {totalFieldPages > 1 && (
<div className="flex justify-between items-center mt-4 pt-4 border-t border-gray-200"> <div className="flex justify-between items-center mt-4 pt-4 border-t border-gray-200">
<button onClick={goToPreviousFields} disabled={fieldPage === 0} className="bg-white text-gray-700 py-2 px-4 rounded-lg border border-gray-300 disabled:opacity-50 hover:bg-gray-100 text-sm"> <button onClick={goToPreviousFields} disabled={fieldPage === 0} className="bg-white text-gray-700 py-2 px-4 rounded-lg border border-gray-300 disabled:opacity-50 hover:bg-gray-100 text-sm">
@@ -345,7 +387,7 @@ export default function InputDataPage() {
</section> </section>
</div> </div>
</main> </main>
<footer className="flex flex-col-reverse sm:flex-row sm:justify-between items-center p-4 border-t bg-gray-50 rounded-b-lg gap-3"> <footer className="flex flex-col-reverse sm:flex-row sm:justify-between items-center p-4 border-t bg-gray-50 rounded-b-lg gap-3">
<div className="flex w-full sm:w-auto space-x-2"> <div className="flex w-full sm:w-auto space-x-2">
<button onClick={goToPrevious} disabled={resultIndex === 0} className="flex-1 bg-white text-gray-700 py-2 px-4 rounded-lg border border-gray-300 disabled:opacity-50 hover:bg-gray-100">Sebelumnya</button> <button onClick={goToPrevious} disabled={resultIndex === 0} className="flex-1 bg-white text-gray-700 py-2 px-4 rounded-lg border border-gray-300 disabled:opacity-50 hover:bg-gray-100">Sebelumnya</button>
@@ -362,7 +404,7 @@ export default function InputDataPage() {
</div> </div>
)} )}
<CameraModal <CameraModal
isOpen={isCameraOpen} isOpen={isCameraOpen}
onClose={() => setIsCameraOpen(false)} onClose={() => setIsCameraOpen(false)}
onCapture={handleCaptureComplete} onCapture={handleCaptureComplete}