ok
This commit is contained in:
206
src/Dashboard.js
206
src/Dashboard.js
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user