This commit is contained in:
everythingonblack
2025-06-12 20:52:46 +07:00
parent 0c065f7be5
commit d9cdbbcde6
2 changed files with 124 additions and 124 deletions

View File

@@ -16,11 +16,10 @@ const Dashboard = () => {
const [rawData, setRawData] = useState([]);
const [stats, setStats] = useState({
totalChats: 0,
userMessages: 0,
botMessages: 0,
mostDiscussedTopics: '-',
});
totalChats: 0,
userMessages: 0,
botMessages: 0,
});
const [isDragging, setIsDragging] = useState(false);
@@ -38,17 +37,22 @@ const Dashboard = () => {
try {
const response = await fetch('https://bot.kediritechnopark.com/webhook/master-agent/dashboard');
const data = await response.json();
setDiscussedTopics(data[0].result.topics)
console.log(data[0].result.topics[0].topic)
setRawData(data);
// Hitung statistik
let totalSessions = new Set(); // unik session_id
const graphObj = data[0].result.graph;
const rawDataArray = Object.entries(graphObj).map(([hour, sesi]) => ({
hour,
sesi,
}));
setRawData(rawDataArray);
let totalSessions = new Set();
let userMessages = 0;
let botMessages = 0;
data.forEach(({ sesi }) => {
Object.entries(sesi).forEach(([channel, messages]) => {
messages.forEach((msg) => {
rawDataArray.forEach(({ sesi }) => {
Object.values(sesi).forEach(messages => {
messages.forEach(msg => {
totalSessions.add(msg.session_id);
if (msg.message.type === 'human') userMessages++;
if (msg.message.type === 'ai') botMessages++;
@@ -56,11 +60,11 @@ const Dashboard = () => {
});
});
setStats({
totalChats: totalSessions.size,
userMessages,
botMessages,
mostDiscussedTopics: '-', // placeholder, bisa ditambah nanti
});
} catch (error) {
@@ -80,90 +84,86 @@ const Dashboard = () => {
};
useEffect(() => {
if (!rawData.length) return;
useEffect(() => {
if (!rawData.length) return;
const ctx = chartRef.current?.getContext('2d');
if (!ctx) return;
const ctx = chartRef.current?.getContext('2d');
if (!ctx) return;
if (chartInstanceRef.current) {
chartInstanceRef.current.destroy();
}
if (chartInstanceRef.current) {
chartInstanceRef.current.destroy();
}
const prefixLabelMap = {
WEB: 'Web App',
WAP: 'WhatsApp',
DME: 'Instagram',
};
const prefixLabelMap = {
WEB: 'Web App',
WAP: 'WhatsApp',
DME: 'Instagram',
};
const prefixColors = {
WEB: { border: '#4285F4', background: 'rgba(66, 133, 244, 0.2)' },
WAP: { border: '#25D366', background: 'rgba(37, 211, 102, 0.2)' },
DME: { border: '#AA00FF', background: 'rgba(170, 0, 255, 0.2)' },
};
const prefixColors = {
WEB: { border: '#4285F4', background: 'rgba(66, 133, 244, 0.2)' },
WAP: { border: '#25D366', background: 'rgba(37, 211, 102, 0.2)' },
DME: { border: '#AA00FF', background: 'rgba(170, 0, 255, 0.2)' },
};
const prefixes = Object.keys(prefixLabelMap);
const prefixes = Object.keys(prefixLabelMap);
const hours = rawData.map(d => d.hour).sort((a, b) => parseFloat(a) - parseFloat(b));
// Ambil semua grub (jam)
const hours = rawData.map(d => d.grub).sort((a, b) => parseFloat(a) - parseFloat(b));
// Inisialisasi data per platform
const counts = {};
prefixes.forEach(prefix => {
counts[prefix] = hours.map(() => 0);
});
// Hitung jumlah pesan berdasarkan panjang array per grub & prefix
rawData.forEach((entry, index) => {
const sesi = entry.sesi || {};
const counts = {};
prefixes.forEach(prefix => {
if (Array.isArray(sesi[prefix])) {
counts[prefix][index] = sesi[prefix].length;
}
counts[prefix] = hours.map(() => 0);
});
});
const datasets = prefixes.map(prefix => ({
label: prefixLabelMap[prefix],
data: counts[prefix],
borderColor: prefixColors[prefix].border,
backgroundColor: prefixColors[prefix].background,
fill: true,
tension: 0.3,
}));
rawData.forEach(({ sesi }, index) => {
prefixes.forEach(prefix => {
if (Array.isArray(sesi[prefix])) {
counts[prefix][index] = sesi[prefix].length;
}
});
});
chartInstanceRef.current = new Chart(ctx, {
type: 'line',
data: {
labels: hours,
datasets,
},
options: {
responsive: true,
plugins: {
legend: {
display: true,
position: 'bottom',
},
const datasets = prefixes.map(prefix => ({
label: prefixLabelMap[prefix],
data: counts[prefix],
borderColor: prefixColors[prefix].border,
backgroundColor: prefixColors[prefix].background,
fill: true,
tension: 0.3,
}));
chartInstanceRef.current = new Chart(ctx, {
type: 'line',
data: {
labels: hours,
datasets,
},
scales: {
y: {
beginAtZero: true,
title: {
options: {
responsive: true,
plugins: {
legend: {
display: true,
text: 'Jumlah Pesan',
position: 'bottom',
},
},
x: {
title: {
display: true,
text: 'Jam',
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Jumlah Pesan',
},
},
x: {
title: {
display: true,
text: 'Jam',
},
},
},
},
},
});
}, [rawData]);
});
}, [rawData]);
return (
<div className={styles.dashboardContainer}>
@@ -189,7 +189,7 @@ useEffect(() => {
<p>Respons Bot</p>
</div>
<div className={styles.statCard} onClick={openTopicsModal}>
<h2 style={{ fontSize: '17px' }}>{stats.mostDiscussedTopics}</h2>
<h2 style={{ fontSize: '17px' }}>{discussedTopics[0]?.topic}</h2>
<p>Paling sering ditanyakan</p>
</div>
</div>
@@ -200,51 +200,51 @@ useEffect(() => {
</div>
<div className={styles.chartSection}>
<h2 className={styles.chartTitle}>Update data</h2>
<div className={styles.chartSection}>
<h2 className={styles.chartTitle}>Update data</h2>
<div
className={`${styles.uploadContainer} ${isDragging ? styles.dragActive : ""}`}
onClick={() => selectedFile ? null : document.getElementById("fileInput").click()}
onDragOver={(e) => {
e.preventDefault();
setIsDragging(true);
}}
onDragLeave={() => setIsDragging(false)}
onDrop={(e) => {
e.preventDefault();
setIsDragging(false);
const file = e.dataTransfer.files[0];
handleFile(file);
}}
>
<p className={styles.desktopText}>
Seret file ke sini, atau <span className={styles.uploadLink}>Klik untuk unggah</span>
</p>
<p className={styles.mobileText}>Klik untuk unggah</p>
{selectedFile && (
<>
<div className={styles.fileInfo}>
<strong>{selectedFile.name}</strong>
</div>
<div className={styles.fileInfoClose} onClick={()=>setSelectedFile(null)}>
X
</div>
</>
)}
<input
id="fileInput"
type="file"
style={{ display: "none" }}
onChange={(e) => {
const file = e.target.files[0];
<div
className={`${styles.uploadContainer} ${isDragging ? styles.dragActive : ""}`}
onClick={() => selectedFile ? null : document.getElementById("fileInput").click()}
onDragOver={(e) => {
e.preventDefault();
setIsDragging(true);
}}
onDragLeave={() => setIsDragging(false)}
onDrop={(e) => {
e.preventDefault();
setIsDragging(false);
const file = e.dataTransfer.files[0];
handleFile(file);
}}
/>
>
<p className={styles.desktopText}>
Seret file ke sini, atau <span className={styles.uploadLink}>Klik untuk unggah</span>
</p>
<p className={styles.mobileText}>Klik untuk unggah</p>
{selectedFile && (
<>
<div className={styles.fileInfo}>
<strong>{selectedFile.name}</strong>
</div>
<div className={styles.fileInfoClose} onClick={() => setSelectedFile(null)}>
X
</div>
</>
)}
<input
id="fileInput"
type="file"
style={{ display: "none" }}
onChange={(e) => {
const file = e.target.files[0];
handleFile(file);
}}
/>
</div>
</div>
</div>
<div className={styles.footer}>

View File

@@ -8,7 +8,7 @@ const DiscussedTopics = ({ topics }) => {
<h2>Topik yang Sering Ditanyakan</h2>
<ul>
{topics.map((topic, idx) => (
<li key={idx}><strong>{topic.topic}</strong> - {topic.question_count} pertanyaan</li>
<li key={idx}><strong>{topic.topic}</strong> - {topic.count} kali</li>
))}
</ul>
</div>