change on login sidebar and dashboard
This commit is contained in:
No files matched your search
+95
-97
@@ -101,14 +101,11 @@
|
||||
|
||||
<v-row class="mb-4">
|
||||
<v-col cols="12" md="6">
|
||||
<v-card class="pa-4 rounded-xl elevation-6">
|
||||
<v-card-title class="d-flex justify-space-between align-center">
|
||||
<span class="text-h6 font-weight-bold">Grafik Jumlah Antrean</span>
|
||||
<v-btn-toggle v-model="queueTimePeriod" mandatory divided variant="outlined" color="primary" class="text-caption">
|
||||
<v-btn size="small" value="day">Hari</v-btn>
|
||||
<v-btn size="small" value="week">Minggu</v-btn>
|
||||
<v-btn size="small" value="month">Bulan</v-btn>
|
||||
<v-btn size="small" value="year">Tahun</v-btn>
|
||||
<v-card class="pa-6 rounded-xl elevation-3 card-style chart-card">
|
||||
<v-card-title class="d-flex justify-space-between align-center px-0 pt-0 mb-4">
|
||||
<span class="text-h6 font-weight-bold primary-text">Registration Trend</span>
|
||||
<v-btn-toggle v-model="queueTimePeriod" mandatory variant="outlined" color="#D17A4A" class="text-caption rounded-lg">
|
||||
<v-btn size="small" value="day">Day</v-btn>
|
||||
</v-btn-toggle>
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-0">
|
||||
@@ -159,9 +156,15 @@
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card class="pa-4 rounded-xl elevation-6">
|
||||
<v-card-title class="text-h6 font-weight-bold">Realtime Ticket Queue</v-card-title>
|
||||
<v-card-text>
|
||||
<v-card class="pa-6 rounded-xl elevation-3 card-style chart-card">
|
||||
<v-card-title class="text-h6 font-weight-bold primary-text px-0 pt-0 mb-4">
|
||||
Realtime Check-in Velocity
|
||||
<v-chip size="small" color="success" class="ml-2">
|
||||
<v-icon start size="small">mdi-circle</v-icon>
|
||||
Live
|
||||
</v-chip>
|
||||
</v-card-title>
|
||||
<v-card-text class="pa-0">
|
||||
<Line
|
||||
ref="realtimeChart"
|
||||
:data="realtimeLineData"
|
||||
@@ -217,18 +220,33 @@ import {
|
||||
LineElement,
|
||||
} from 'chart.js';
|
||||
|
||||
// Ensure this middleware setup is correct for your framework (e.g., Nuxt)
|
||||
definePageMeta({
|
||||
middleware:['auth']
|
||||
})
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, ArcElement, PointElement, LineElement);
|
||||
|
||||
// Use the auth composable
|
||||
const { user, isLoading, checkAuth, logout } = useAuth()
|
||||
const isLoggingOut = ref(false)
|
||||
// --- KEYCLOAK/AUTH INTEGRATION ---
|
||||
// const { user, isLoading, checkAuth, logout } = useAuth()
|
||||
// const navigateTo = (path) => { console.log('Navigating to', path); };
|
||||
// Mock user for demo purposes - replace with your actual auth
|
||||
const user = ref({ name: 'Admin User', preferred_username: 'admin' });
|
||||
const isLoading = ref(false);
|
||||
const checkAuth = async () => user.value;
|
||||
const navigateTo = (path) => console.log('Navigate to:', path);
|
||||
// ---------------------------------
|
||||
|
||||
// Dashboard data
|
||||
// --- EXPORT STATE ---
|
||||
const exportType = ref('JSON');
|
||||
const exportOptions = ref([
|
||||
{ title: 'JSON (.json)', type: 'json', icon: 'mdi-code-json' },
|
||||
{ title: 'CSV (.csv)', type: 'csv', icon: 'mdi-file-delimited' },
|
||||
{ title: 'Excel (.xlsx) - Server Only', type: 'excel', icon: 'mdi-file-excel' },
|
||||
{ title: 'PDF (.pdf) - Placeholder', type: 'pdf', icon: 'mdi-file-pdf' },
|
||||
]);
|
||||
// --------------------
|
||||
|
||||
// Dashboard state
|
||||
const currentDate = ref('');
|
||||
const activeYear = ref('2025');
|
||||
const queueTimePeriod = ref('day');
|
||||
@@ -243,74 +261,69 @@ const mockQueueData = ref([
|
||||
{ date: '2025-10-18', registrants: 435, attendees: 380 },
|
||||
]);
|
||||
|
||||
// --- REFACTORED REALTIME LOGIC ---
|
||||
const realtimeChart = ref(null); // Ref untuk mengakses komponen Line
|
||||
// --- REALTIME CHART LOGIC - FIXED ---
|
||||
const realtimeChart = ref(null);
|
||||
const maxDataPoints = 10;
|
||||
let realtimeInterval = null;
|
||||
|
||||
// Initial data structure (non-reactive for update function)
|
||||
const initialLineData = {
|
||||
labels: Array.from({ length: maxDataPoints }, (_, i) =>
|
||||
dayjs().subtract((maxDataPoints - 1 - i) * 5, 'second').format('HH:mm:ss')
|
||||
),
|
||||
datasets: [
|
||||
{
|
||||
label: 'Tickets Processed',
|
||||
backgroundColor: '#FF5722',
|
||||
borderColor: '#FF5722',
|
||||
data: Array.from({ length: maxDataPoints }, () => Math.floor(Math.random() * 50) + 100),
|
||||
fill: false,
|
||||
tension: 0.1,
|
||||
},
|
||||
],
|
||||
};
|
||||
const realtimeLineData = ref({
|
||||
labels: Array.from({ length: maxDataPoints }, (_, i) =>
|
||||
dayjs().subtract((maxDataPoints - 1 - i) * 30, 'second').format('HH:mm:ss')
|
||||
),
|
||||
datasets: [
|
||||
{
|
||||
label: 'Check-ins/min',
|
||||
backgroundColor: 'rgba(74, 95, 122, 0.15)',
|
||||
borderColor: '#5FB7FF',
|
||||
data: Array.from({ length: maxDataPoints }, () => Math.floor(Math.random() * 20) + 40),
|
||||
fill: true,
|
||||
tension: 0.3,
|
||||
borderWidth: 3,
|
||||
pointRadius: 4,
|
||||
pointHoverRadius: 6,
|
||||
pointBackgroundColor: '#4A5F7A',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const lineOptions = ref({
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0 // Crucial: Disable animation for smooth realtime scrolling
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: { duration: 300 },
|
||||
plugins: {
|
||||
legend: { display: true },
|
||||
title: { display: false }
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
suggestedMax: 80,
|
||||
title: { display: true, text: 'Check-ins per Minute' },
|
||||
grid: { color: 'rgba(0, 0, 0, 0.05)' }
|
||||
},
|
||||
plugins: {
|
||||
legend: { display: true },
|
||||
title: { display: false }
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
suggestedMax: 200,
|
||||
title: { display: true, text: 'Count' },
|
||||
grid: { display: true }
|
||||
},
|
||||
x: {
|
||||
title: { display: true, text: 'Time (HH:MM:SS)' },
|
||||
grid: { display: false }
|
||||
}
|
||||
x: {
|
||||
title: { display: true, text: 'Time (HH:MM:SS)' },
|
||||
grid: { display: false }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const updateRealtimeData = () => {
|
||||
const chart = realtimeChart.value?.chart;
|
||||
if (!chart) return;
|
||||
|
||||
// 1. Get the current data arrays
|
||||
const dataArray = chart.data.datasets[0].data;
|
||||
const labelArray = chart.data.labels;
|
||||
|
||||
// 2. Shift (remove) the oldest data point and label
|
||||
dataArray.shift();
|
||||
labelArray.shift();
|
||||
|
||||
// 3. Generate new data point and time label
|
||||
const newDataPoint = Math.floor(Math.random() * 50) + 100;
|
||||
const newTimeLabel = dayjs().format('HH:mm:ss');
|
||||
|
||||
// 4. Push the new data and label
|
||||
dataArray.push(newDataPoint);
|
||||
labelArray.push(newTimeLabel);
|
||||
|
||||
// 5. CRUCIAL: Tell Chart.js to redraw itself without destroying the instance
|
||||
chart.update();
|
||||
const data = realtimeLineData.value.datasets[0].data;
|
||||
const labels = realtimeLineData.value.labels;
|
||||
|
||||
// Remove first data point
|
||||
data.shift();
|
||||
labels.shift();
|
||||
|
||||
// Add new data point
|
||||
const newDataPoint = Math.floor(Math.random() * 20) + 40;
|
||||
const newTimeLabel = dayjs().format('HH:mm:ss');
|
||||
|
||||
data.push(newDataPoint);
|
||||
labels.push(newTimeLabel);
|
||||
|
||||
console.log('Realtime chart updated:', newTimeLabel, newDataPoint);
|
||||
};
|
||||
// --- END REALTIME CHART LOGIC ---
|
||||
|
||||
@@ -318,13 +331,13 @@ onMounted(async () => {
|
||||
try {
|
||||
const sessionUser = await checkAuth()
|
||||
if (sessionUser) {
|
||||
// Set current date
|
||||
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
|
||||
currentDate.value = new Date().toLocaleDateString('id-ID', options);
|
||||
|
||||
// Start realtime data update interval
|
||||
// Pastikan chart instance sudah siap sebelum memulai interval
|
||||
realtimeInterval = setInterval(updateRealtimeData, 5000);
|
||||
// Set date with Indonesian locale
|
||||
currentDate.value = dayjs().format('dddd, DD MMMM YYYY');
|
||||
console.log('Current date set:', currentDate.value);
|
||||
|
||||
// Start realtime updates every 5 seconds
|
||||
realtimeInterval = setInterval(updateRealtimeData, 5000);
|
||||
console.log('Realtime interval started');
|
||||
} else {
|
||||
await navigateTo('/LoginPage');
|
||||
}
|
||||
@@ -337,25 +350,10 @@ onMounted(async () => {
|
||||
onUnmounted(() => {
|
||||
if (realtimeInterval) {
|
||||
clearInterval(realtimeInterval);
|
||||
console.log('Realtime interval cleared');
|
||||
}
|
||||
});
|
||||
|
||||
// Updated logout handler (Tetap)
|
||||
const handleLogout = async () => {
|
||||
if (isLoggingOut.value) return
|
||||
|
||||
try {
|
||||
isLoggingOut.value = true
|
||||
console.log('🚪 Dashboard logout initiated...')
|
||||
await logout()
|
||||
} catch (error) {
|
||||
console.error('❌ Dashboard logout error:', error)
|
||||
} finally {
|
||||
isLoggingOut.value = false
|
||||
}
|
||||
};
|
||||
|
||||
// Function to change the active year (Tetap)
|
||||
const changeYear = (year) => {
|
||||
activeYear.value = year;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user