ok
This commit is contained in:
@@ -5,6 +5,7 @@ html, body, #root {
|
|||||||
|
|
||||||
.App {
|
.App {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import styles from './ChatBot.module.css';
|
import styles from './ChatBot.module.css';
|
||||||
|
|
||||||
const ChatBot = ({ existingConversation, readOnly, hh }) => {
|
const ChatBot = ({ existingConversation }) => {
|
||||||
const [messages, setMessages] = useState([
|
const [messages, setMessages] = useState([
|
||||||
{
|
{
|
||||||
sender: 'bot',
|
sender: 'bot',
|
||||||
text: 'Hai Dermalovers! 👋 Saya siap membantu anda tampil lebih percaya diri. Ada pertanyaan seputar perawatan kulit atau kecantikan hari ini?',
|
text: 'Hai! Saya Alle, asisten virtual Dermalounge Clinic. Ada yang bisa Alle bantu hari ini?',
|
||||||
time: getTime(),
|
time: getTime(),
|
||||||
quickReplies: [
|
quickReplies: [
|
||||||
'Konsultasi estetik',
|
'Konsultasi Estetik',
|
||||||
'Konsultasi kulit dan kelamin'
|
'Konsultasi Kulit & Kelamin'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@@ -231,8 +232,7 @@ function formatBoldText(text) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={styles.quickReply}
|
className={styles.nextButton}
|
||||||
style={{ color: name.length > 2 && phoneNumber.length >= 10 ? 'black' : '#ccc' }}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (name.length > 2 && phoneNumber.length >= 10) {
|
if (name.length > 2 && phoneNumber.length >= 10) {
|
||||||
const sessionData = JSON.parse(localStorage.getItem('session')) || {};
|
const sessionData = JSON.parse(localStorage.getItem('session')) || {};
|
||||||
|
|||||||
@@ -152,16 +152,30 @@
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quickReply:hover {
|
/* .quickReply:hover {
|
||||||
background: #075e54;
|
background: #075e54;
|
||||||
color: white;
|
color: white;
|
||||||
border-color: #075e54;
|
border-color: #075e54;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.quickReply:hover::placeholder {
|
/* .quickReply:hover::placeholder {
|
||||||
|
color: white;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.nextButton {
|
||||||
|
background: #075e54;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nextButton:hover::placeholder {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
.quickReply2 {
|
.quickReply2 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -174,13 +188,13 @@
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quickReply2:hover {
|
/* .quickReply2:hover {
|
||||||
background: #075e54;
|
background: #075e54;
|
||||||
color: white;
|
color: white;
|
||||||
border-color: #075e54;
|
border-color: #075e54;
|
||||||
border: 1px solid #075e54;
|
border: 1px solid #075e54;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.quickReply2:hover::placeholder {
|
/* .quickReply2:hover::placeholder {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
} */
|
||||||
242
src/Dashboard.js
242
src/Dashboard.js
@@ -22,6 +22,9 @@ const Dashboard = () => {
|
|||||||
const [modalContent, setModalContent] = useState(null);
|
const [modalContent, setModalContent] = useState(null);
|
||||||
const [rawData, setRawData] = useState([]);
|
const [rawData, setRawData] = useState([]);
|
||||||
const [loading, setLoading] = useState(true); // ⬅️ Tambahkan state loading
|
const [loading, setLoading] = useState(true); // ⬅️ Tambahkan state loading
|
||||||
|
const [fileList, setFileList] = useState([]);
|
||||||
|
const [selectedKeys, setSelectedKeys] = useState([]);
|
||||||
|
|
||||||
|
|
||||||
const [stats, setStats] = useState({
|
const [stats, setStats] = useState({
|
||||||
totalChats: 0,
|
totalChats: 0,
|
||||||
@@ -29,17 +32,21 @@ const Dashboard = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [selectedFile, setSelectedFile] = useState(null);
|
const [selectedFiles, setSelectedFiles] = useState([]);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleFile = (file) => {
|
const handleFiles = (files) => {
|
||||||
if (file) {
|
const newFiles = files.filter(file => {
|
||||||
setSelectedFile(file);
|
// Hindari duplikat berdasarkan nama (atau bisa pakai hash/md5 jika perlu)
|
||||||
}
|
return !selectedFiles.some(f => f.name === file.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
setSelectedFiles((prev) => [...prev, ...newFiles]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
localStorage.removeItem('user');
|
localStorage.removeItem('user');
|
||||||
@@ -90,16 +97,17 @@ const Dashboard = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
// if (!response.ok) {
|
||||||
throw new Error('Fetch gagal dengan status: ' + response.status);
|
// throw new Error('Fetch gagal dengan status: ' + response.status);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
console.log(data);
|
console.log(data);
|
||||||
setDiscussedTopics(data?.result?.topics)
|
setDiscussedTopics(data?.graph[0]?.json?.result?.topics)
|
||||||
setFollowUps(data?.result?.interested_users)
|
setFollowUps(data?.graph[0]?.json?.result?.interested_users)
|
||||||
|
setFileList(data?.files)
|
||||||
const graphObj = data.result.graph;
|
const graphObj = data?.graph[0]?.json?.result?.graph;
|
||||||
|
console.log(graphObj)
|
||||||
const rawDataArray = Object.entries(graphObj).map(([hour, sesi]) => ({
|
const rawDataArray = Object.entries(graphObj).map(([hour, sesi]) => ({
|
||||||
hour,
|
hour,
|
||||||
sesi,
|
sesi,
|
||||||
@@ -298,6 +306,109 @@ const subscribeUser = async () => {
|
|||||||
});
|
});
|
||||||
}, [rawData]);
|
}, [rawData]);
|
||||||
|
|
||||||
|
const handleDeleteFile = async (key) => {
|
||||||
|
if (!window.confirm(`Yakin ingin menghapus "${key}"?`)) return;
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fetch("https://bot.kediritechnopark.com/webhook/files/delete", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ key }),
|
||||||
|
});
|
||||||
|
|
||||||
|
// fetchFiles(); // Refresh list
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal menghapus file:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBatchDownload = async () => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
for (const key of selectedKeys) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://bot.kediritechnopark.com/webhook/files/download?key=${encodeURIComponent(key)}`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('Gagal download');
|
||||||
|
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
// Coba ambil nama file dari header Content-Disposition (jika tersedia)
|
||||||
|
let filename = key;
|
||||||
|
const disposition = response.headers.get('Content-Disposition');
|
||||||
|
if (disposition && disposition.includes('filename=')) {
|
||||||
|
const match = disposition.match(/filename="?(.+?)"?$/);
|
||||||
|
if (match) filename = match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buat URL dan download
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Gagal download ${key}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBatchUpload = async () => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
|
||||||
|
for (const file of selectedFiles) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file); // Kirim satu per satu file
|
||||||
|
|
||||||
|
const response = await fetch('https://bot.kediritechnopark.com/webhook/files/upload', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
// ❗Jangan set 'Content-Type' untuk FormData, biarkan browser mengaturnya
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`Upload gagal untuk file ${file.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alert('Upload selesai');
|
||||||
|
setSelectedFiles([]); // Kosongkan setelah selesai upload
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBatchDelete = async () => {
|
||||||
|
if (!window.confirm(`Yakin ingin menghapus ${selectedKeys.length} file?`)) return;
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
|
||||||
|
for (const key of selectedKeys) {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://bot.kediritechnopark.com/webhook/files/delete?key=${encodeURIComponent(key)}`,
|
||||||
|
{
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedKeys([]);
|
||||||
|
};
|
||||||
|
|
||||||
// ⬇️ Jika masih loading, tampilkan full white screen
|
// ⬇️ Jika masih loading, tampilkan full white screen
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div style={{ backgroundColor: 'white', width: '100vw', height: '100vh' }} />;
|
return <div style={{ backgroundColor: 'white', width: '100vw', height: '100vh' }} />;
|
||||||
@@ -354,53 +465,126 @@ const subscribeUser = async () => {
|
|||||||
<h2 className={styles.chartTitle}>Interactions</h2>
|
<h2 className={styles.chartTitle}>Interactions</h2>
|
||||||
<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 data</h2>
|
||||||
|
|
||||||
|
{/* ✅ TOMBOL AKSI */}
|
||||||
|
{selectedKeys.length > 0 && (
|
||||||
|
<div className={styles.actionBar}>
|
||||||
|
<button onClick={handleBatchDownload}>⬇️ Download</button>
|
||||||
|
<button onClick={handleBatchDelete}>🗑️ Hapus</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ✅ AREA UPLOAD */}
|
||||||
<div
|
<div
|
||||||
className={`${styles.uploadContainer} ${isDragging ? styles.dragActive : ""}`}
|
className={`${styles.uploadContainer} ${isDragging ? styles.dragActive : ""}`}
|
||||||
onClick={() => selectedFile ? null : document.getElementById("fileInput").click()}
|
|
||||||
onDragOver={(e) => {
|
onDragOver={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
}}
|
}}
|
||||||
onDragLeave={() => setIsDragging(false)}
|
onDragLeave={() => setIsDragging(false)}
|
||||||
|
|
||||||
onDrop={(e) => {
|
onDrop={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsDragging(false);
|
setIsDragging(false);
|
||||||
const file = e.dataTransfer.files[0];
|
const files = Array.from(e.dataTransfer.files);
|
||||||
handleFile(file);
|
handleFiles(files);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p className={styles.desktopText}>
|
|
||||||
Drop file here, or <span className={styles.uploadLink}>Click to upload</span>
|
|
||||||
</p>
|
|
||||||
<p className={styles.mobileText}>Click to upload</p>
|
|
||||||
|
|
||||||
{selectedFile && (
|
{/* ✅ TABEL FILE */}
|
||||||
<>
|
<table className={styles.fileTable}>
|
||||||
<div className={styles.fileInfo}>
|
<thead>
|
||||||
<strong>{selectedFile.name}</strong>
|
<tr>
|
||||||
|
<th>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={fileList.length > 0 && selectedKeys.length === fileList.length}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
setSelectedKeys(fileList.map((f) => f.json.Key));
|
||||||
|
} else {
|
||||||
|
setSelectedKeys([]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th>select all</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{fileList.map((file, index) => (
|
||||||
|
<tr key={index}>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedKeys.includes(file.json.Key)}
|
||||||
|
onChange={() => {
|
||||||
|
setSelectedKeys((prev) =>
|
||||||
|
prev.includes(file.json.Key)
|
||||||
|
? prev.filter((key) => key !== file.json.Key)
|
||||||
|
: [...prev, file.json.Key]
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>{file.json.Key}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p className={styles.desktopText}>
|
||||||
|
Drop file here, or <span onClick={() => document.getElementById("fileInput").click()} className={styles.uploadLink}>Click to upload</span>
|
||||||
|
</p>
|
||||||
|
<p className={styles.mobileText} onClick={() => document.getElementById("fileInput").click()}>Click to upload</p>
|
||||||
|
|
||||||
|
|
||||||
|
<div>
|
||||||
|
|
||||||
|
{selectedFiles.length > 0 &&
|
||||||
|
selectedFiles.map((file, index) => (
|
||||||
|
<div>
|
||||||
|
<div key={index} className={styles.fileInfo}>
|
||||||
|
<strong>{file.name}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.fileInfoClose} onClick={() => setSelectedFile(null)}>
|
<div
|
||||||
|
className={styles.fileInfoClose}
|
||||||
|
onClick={() =>
|
||||||
|
setSelectedFiles((prev) => prev.filter((_, i) => i !== index))
|
||||||
|
}
|
||||||
|
>
|
||||||
X
|
X
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
)}
|
))}
|
||||||
|
|
||||||
|
{selectedFiles.length > 0 &&
|
||||||
|
<div>
|
||||||
|
<div onClick={()=>handleBatchUpload()} className={styles.fileUpload}>
|
||||||
|
<strong>Upload</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
id="fileInput"
|
id="fileInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
multiple
|
||||||
style={{ display: "none" }}
|
style={{ display: "none" }}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const file = e.target.files[0];
|
const files = Array.from(e.target.files);
|
||||||
handleFile(file);
|
handleFiles(files);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
© 2025 Kediri Technopark
|
© 2025 Kediri Technopark
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.uploadContainer {
|
.uploadContainer {
|
||||||
|
overflow: hidden;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
border: 2px dashed #ccc;
|
border: 2px dashed #ccc;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 32px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border-color 0.3s;
|
transition: border-color 0.3s;
|
||||||
|
padding-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploadContainer:hover {
|
.uploadContainer:hover {
|
||||||
@@ -96,6 +97,7 @@
|
|||||||
|
|
||||||
.desktopText {
|
.desktopText {
|
||||||
display: block;
|
display: block;
|
||||||
|
/* padding: 32px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobileText {
|
.mobileText {
|
||||||
@@ -115,10 +117,12 @@
|
|||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border-radius: 8px 0 0 8px;
|
border-radius: 8px 0 0 8px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileInfoClose {
|
.fileInfoClose {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
|
margin-right: 6px;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #ff0000;
|
color: #ff0000;
|
||||||
@@ -130,6 +134,17 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fileUpload {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: white;
|
||||||
|
background: #2bb438;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mobile styles */
|
/* Mobile styles */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.h1 {
|
.h1 {
|
||||||
@@ -243,3 +258,41 @@ position: absolute;
|
|||||||
.dropdownItem:hover {
|
.dropdownItem:hover {
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fileTable {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTable th,
|
||||||
|
.fileTable td {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTable th {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionBar {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionBar button {
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: #00adef;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionBar button:hover {
|
||||||
|
background-color: #0095cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
131
src/FileManager.js
Normal file
131
src/FileManager.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
// import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
// const FileManager = ({ token }) => {
|
||||||
|
// const [files, setFiles] = useState([]);
|
||||||
|
// const [uploadFile, setUploadFile] = useState(null);
|
||||||
|
|
||||||
|
// const API_BASE = 'https://bot.kediritechnopark.com/webhook/files';
|
||||||
|
|
||||||
|
// // Ambil daftar file
|
||||||
|
// const fetchFiles = async () => {
|
||||||
|
// try {
|
||||||
|
// const response = await fetch(`${API_BASE}/list`, {
|
||||||
|
// method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json',
|
||||||
|
// 'Authorization': `Bearer ${token}`,
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const data = await response.json();
|
||||||
|
// setFiles(data);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Gagal mengambil daftar file:', error);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Hapus file
|
||||||
|
// const deleteFile = async (key) => {
|
||||||
|
// if (!window.confirm(`Yakin ingin hapus "${key}"?`)) return;
|
||||||
|
// try {
|
||||||
|
// await fetch(`${API_BASE}/delete`, {
|
||||||
|
// method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json',
|
||||||
|
// 'Authorization': `Bearer ${token}`,
|
||||||
|
// },
|
||||||
|
// body: JSON.stringify({ key }),
|
||||||
|
// });
|
||||||
|
|
||||||
|
// fetchFiles();
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Gagal menghapus file:', error);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Upload file
|
||||||
|
// const handleUpload = async () => {
|
||||||
|
// if (!uploadFile) return;
|
||||||
|
|
||||||
|
// const formData = new FormData();
|
||||||
|
// formData.append('file', uploadFile);
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// await fetch(`${API_BASE}/upload`, {
|
||||||
|
// method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// 'Authorization': `Bearer ${token}`,
|
||||||
|
// },
|
||||||
|
// body: formData,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// setUploadFile(null);
|
||||||
|
// fetchFiles();
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Gagal mengunggah file:', error);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Download file
|
||||||
|
// const downloadFile = async (key) => {
|
||||||
|
// try {
|
||||||
|
// const response = await fetch(`${API_BASE}/download?key=${encodeURIComponent(key)}`, {
|
||||||
|
// method: 'GET',
|
||||||
|
// headers: {
|
||||||
|
// 'Authorization': `Bearer ${token}`,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (!response.ok) throw new Error('Download gagal');
|
||||||
|
|
||||||
|
// const blob = await response.blob();
|
||||||
|
// const url = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// const a = document.createElement('a');
|
||||||
|
// a.href = url;
|
||||||
|
// a.download = key;
|
||||||
|
// document.body.appendChild(a);
|
||||||
|
// a.click();
|
||||||
|
// a.remove();
|
||||||
|
|
||||||
|
// window.URL.revokeObjectURL(url);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Gagal mengunduh file:', error);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// fetchFiles();
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div style={{ padding: 20, fontFamily: 'Arial' }}>
|
||||||
|
// <h2>📁 Manajemen File</h2>
|
||||||
|
|
||||||
|
// <ul>
|
||||||
|
// {files.map(file => (
|
||||||
|
// <li key={file.Key} style={{ marginBottom: 8 }}>
|
||||||
|
// <strong>{file.Key}</strong>{' '}
|
||||||
|
// <button onClick={() => downloadFile(file.Key)}>⬇️ Download</button>{' '}
|
||||||
|
// <button onClick={() => deleteFile(file.Key)}>🗑️ Hapus</button>
|
||||||
|
// </li>
|
||||||
|
// ))}
|
||||||
|
// </ul>
|
||||||
|
|
||||||
|
// <hr />
|
||||||
|
|
||||||
|
// <div>
|
||||||
|
// <h3>📤 Upload File</h3>
|
||||||
|
// <input
|
||||||
|
// type="file"
|
||||||
|
// onChange={e => setUploadFile(e.target.files[0])}
|
||||||
|
// />
|
||||||
|
// <button onClick={handleUpload} disabled={!uploadFile}>
|
||||||
|
// Upload
|
||||||
|
// </button>
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default FileManager;
|
||||||
Reference in New Issue
Block a user