ok
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user