This commit is contained in:
Vassshhh
2025-07-03 14:33:41 +07:00
parent fe7d67a2d8
commit c3cc8572ce
2 changed files with 157 additions and 63 deletions

View File

@@ -25,6 +25,7 @@ const Dashboard = () => {
const [loading, setLoading] = useState(true); // ⬅️ Tambahkan state loading const [loading, setLoading] = useState(true); // ⬅️ Tambahkan state loading
const [fileList, setFileList] = useState([]); const [fileList, setFileList] = useState([]);
const [selectedKeys, setSelectedKeys] = useState([]); const [selectedKeys, setSelectedKeys] = useState([]);
const [updateDetected, setUpdateDetected] = useState([]);
const [stats, setStats] = useState({ const [stats, setStats] = useState({
@@ -37,58 +38,108 @@ const Dashboard = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const handleFiles = async (files) => { const handleFiles = async (files) => {
const filteredFiles = []; const filteredFiles = [];
for (const file of files) { for (const file of files) {
const lowerName = file.name.toLowerCase(); const lowerName = file.name.toLowerCase();
const nameWithoutExt = lowerName.replace(/\.[^/.]+$/, ''); const nameWithoutExt = lowerName.replace(/\.[^/.]+$/, '');
// 1⃣ Cegah duplikat dari file yang sudah dipilih sebelumnya // 1⃣ Cegah duplikat dari file yang sudah dipilih sebelumnya
const alreadySelected = selectedFiles.some(f => const alreadySelected = selectedFiles.some(f =>
f.name.toLowerCase() === file.name.toLowerCase() f.name.toLowerCase() === file.name.toLowerCase()
);
if (alreadySelected) continue;
// 2⃣ Cari file server yang mirip (berisi / mengandung)
const similarFile = fileList.find(f => {
const serverName = f.json.Key.toLowerCase();
const serverNameWithoutExt = serverName.replace(/\.[^/.]+$/, '');
return (
serverName.includes(lowerName) ||
lowerName.includes(serverName) ||
serverNameWithoutExt.includes(nameWithoutExt) ||
nameWithoutExt.includes(serverNameWithoutExt)
); );
}); if (alreadySelected) continue;
if (similarFile) { // 2⃣ Cek apakah ada file server dengan nama persis
const confirmOverwrite = window.confirm( const exactMatch = fileList.find(f => {
`File "${file.name}" mirip atau mengandung "${similarFile.json.Key}" di server.\nIngin menimpa file tersebut?` const serverKey = f.json.Key;
); console.log(`Checking: "${serverKey}" === "${file.name}"`);
return serverKey === file.name;
});
if (confirmOverwrite) {
// Ganti nama agar ditimpa if (exactMatch) {
Object.defineProperty(file, 'name', { const confirmOverwrite = window.confirm(
writable: true, `File "${file.name}" sudah ada di server sebagai "${exactMatch.json.Key}".\nIngin menimpa file tersebut?`
value: similarFile.json.Key, );
});
filteredFiles.push(file); // tetap tambahkan if (confirmOverwrite) {
} else { // Ganti nama agar ditimpa
// Tidak ditimpa, tetap pakai nama asli Object.defineProperty(file, 'name', {
filteredFiles.push(file); // tambahkan tanpa modifikasi nama writable: true,
value: exactMatch.json.Key,
});
filteredFiles.push(file);
} else {
let counter = 1;
const extMatch = file.name.match(/(\.[^/.]+)$/);
const extension = extMatch ? extMatch[1] : '';
const baseName = file.name.replace(/\.[^/.]+$/, '');
let tryName = `${baseName} (${counter})${extension}`.trim();
const existingNames = [
...fileList.map(f => f.json.Key.toLowerCase()),
...selectedFiles.map(f => f.name.toLowerCase())
];
while (existingNames.includes(tryName.toLowerCase())) {
counter++;
tryName = `${baseName} (${counter})${extension}`.trim();
}
Object.defineProperty(file, 'name', {
writable: true,
value: tryName,
});
filteredFiles.push(file);
}
continue; // Lewati ke file berikutnya karena sudah ditangani
} }
} else {
// Tidak ada kemiripan, langsung tambahkan
filteredFiles.push(file);
}
}
if (filteredFiles.length > 0) { // 3⃣ Jika tidak ada yang sama persis, cari yang mirip
setSelectedFiles(prev => [...prev, ...filteredFiles]); const similarFile = fileList.find(f => {
} const serverName = f.json.Key.toLowerCase();
}; const serverNameWithoutExt = serverName.replace(/\.[^/.]+$/, '');
return (
serverName.includes(lowerName) ||
lowerName.includes(serverName) ||
serverNameWithoutExt.includes(nameWithoutExt) ||
nameWithoutExt.includes(serverNameWithoutExt)
);
});
if (similarFile) {
const confirmOverwrite = window.confirm(
`File "${file.name}" mirip atau mengandung "${similarFile.json.Key}" di server.\nIngin menimpa file tersebut?`
);
if (confirmOverwrite) {
Object.defineProperty(file, 'name', {
writable: true,
value: similarFile.json.Key,
});
filteredFiles.push(file);
} else {
Object.defineProperty(file, 'name', {
writable: true,
value: file.name.replace(/(\.[^/.]+)$/, ' (1)$1'),
});
filteredFiles.push(file);
}
} else {
filteredFiles.push(file);
}
}
if (filteredFiles.length > 0) {
setSelectedFiles(prev => [...prev, ...filteredFiles]);
}
};
@@ -150,10 +201,11 @@ const handleFiles = async (files) => {
const data = await response.json(); const data = await response.json();
console.log(data); console.log(data);
setDiscussedTopics(data?.graph[0]?.json?.result?.topics) setDiscussedTopics(data[0]?.graph[0]?.json?.result?.topics)
setFollowUps(data?.graph[0]?.json?.result?.interested_users) setFollowUps(data[0]?.graph[0]?.json?.result?.interested_users)
setFileList(data?.files) setFileList(data[0]?.files)
const graphObj = data?.graph[0]?.json?.result?.graph; setUpdateDetected(data[1]?.updateDetected)
const graphObj = data[0]?.graph[0]?.json?.result?.graph;
console.log(graphObj) console.log(graphObj)
const rawDataArray = Object.entries(graphObj).map(([hour, sesi]) => ({ const rawDataArray = Object.entries(graphObj).map(([hour, sesi]) => ({
hour, hour,
@@ -411,13 +463,14 @@ const handleFiles = async (files) => {
} }
} }
}; };
const handleBatchUpload = async () => { const handleBatchUpload = async () => {
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
const newFiles = []; const newFiles = [];
for (const file of selectedFiles) { for (const file of selectedFiles) {
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file, file.name);
const response = await fetch('https://bot.kediritechnopark.com/webhook/files/upload', { const response = await fetch('https://bot.kediritechnopark.com/webhook/files/upload', {
method: 'POST', method: 'POST',
@@ -428,27 +481,35 @@ const handleFiles = async (files) => {
}); });
if (response.ok) { if (response.ok) {
newFiles.push({ const newFile = {
json: { json: {
Key: file.name, Key: file.name,
LastModified: new Date().toISOString(), LastModified: new Date().toISOString(),
Size: file.size, Size: file.size,
StorageClass: 'STANDARD' StorageClass: 'STANDARD'
} }
};
// 1⃣ Hapus file lama dari fileList yang punya nama sama
setFileList(prev => {
const filtered = prev.filter(f => f.json.Key !== file.name);
return [...filtered, newFile];
}); });
newFiles.push(newFile);
setUpdateDetected(true);
} else { } else {
console.error(`Upload gagal untuk file ${file.name}`); console.error(`Upload gagal untuk file ${file.name}`);
} }
} }
// ✅ Set fileList sekaligus
setFileList((prev) => [...prev, ...newFiles]);
alert('Upload selesai'); alert('Upload selesai');
setSelectedFiles([]); setSelectedFiles([]);
}; };
const handleBatchDelete = async () => { const handleBatchDelete = async () => {
if (!window.confirm(`Yakin ingin menghapus ${selectedKeys.length} file?`)) return; if (!window.confirm(`Yakin ingin menghapus ${selectedKeys.length} file?`)) return;
@@ -495,6 +556,32 @@ const handleFiles = async (files) => {
} }
}; };
const handleBatchPush = async () => {
if (!window.confirm(`Yakin ingin mengupdate pengetahuan AI?`)) return;
const token = localStorage.getItem('token');
try {
const response = await fetch(
`https://bot.kediritechnopark.com/webhook/files/push`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
}
);
if (response.ok) {
alert('Pengetahuan berhasil diperbarui.');
} else {
alert(`Gagal memperbarui pengetahuan AI`);
}
} catch (err) {
alert(`Gagal memperbarui pengetahuan AI`);
}
};
// ⬇️ Jika masih loading, tampilkan full white screen // ⬇️ Jika masih loading, tampilkan full white screen
if (loading) { if (loading) {
@@ -550,15 +637,14 @@ const handleFiles = async (files) => {
<canvas ref={chartRef}></canvas> <canvas ref={chartRef}></canvas>
</div> </div>
<div className={styles.chartSection}> <div className={styles.chartSection}>
<h2 className={styles.chartTitle}>Update data</h2> <h2 className={styles.chartTitle}>Update AI data</h2>
{/* ✅ TOMBOL AKSI */} {/* ✅ TOMBOL AKSI */}
{selectedKeys.length > 0 && ( <div className={styles.actionBar}>
<div className={styles.actionBar}> <button onClick={handleBatchDownload} disabled={selectedKeys.length < 1}> Download</button>
<button onClick={handleBatchDownload}> Download</button> <button onClick={handleBatchDelete} disabled={selectedKeys.length < 1}>🗑 Delete</button>
<button onClick={handleBatchDelete}>🗑 Hapus</button> {updateDetected && <button onClick={handleBatchPush}>🔄 Update</button>}
</div> </div>
)}
{/* ✅ AREA UPLOAD */} {/* ✅ AREA UPLOAD */}
<div <div

View File

@@ -137,8 +137,8 @@
.fileUpload { .fileUpload {
margin-top: 16px; margin-top: 16px;
font-size: 14px; font-size: 14px;
color: #333; color: white;
background: #2bb438; background: #6bc073;
padding: 8px 12px; padding: 8px 12px;
border-radius: 8px; border-radius: 8px;
display: inline-block; display: inline-block;
@@ -284,13 +284,21 @@ position: absolute;
.actionBar button { .actionBar button {
padding: 6px 12px; padding: 6px 12px;
background-color: #00adef; background-color: #6bc073;
color: white; color: white;
font-weight: bold; font-weight: bold;
border: none; border: none;
border-radius: 6px; border-radius: 6px;
cursor: pointer; cursor: pointer;
} }
.actionBar button:disabled,
.actionBar button[disabled] {
background-color: #cccccc; /* Warna abu-abu untuk tombol nonaktif */
color: #666666; /* Warna teks yang lebih redup */
cursor: not-allowed; /* Cursor tanda larangan */
opacity: 0.7; /* Opsional: efek transparan */
}
.actionBar button:hover { .actionBar button:hover {
background-color: #0095cc; background-color: #0095cc;