EDMS/pages/dashboard/index.vue
2025-05-30 04:23:17 +00:00

339 lines
9.7 KiB
Vue

<script setup>
definePageMeta({
title: "Dashboard",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "Dashboard",
path: "/",
},
],
});
// Data baru untuk lapangan terbang teratas
const topAirports = ref([
{
rank: 1,
name: "Lapangan Terbang Antarabangsa Kuala Lumpur (KLIA)",
visitors: 62000000,
},
{
rank: 2,
name: "Lapangan Terbang Antarabangsa Kota Kinabalu",
visitors: 9000000,
},
{ rank: 3, name: "Lapangan Terbang Antarabangsa Penang", visitors: 8000000 },
{ rank: 4, name: "Lapangan Terbang Antarabangsa Kuching", visitors: 5500000 },
{
rank: 5,
name: "Lapangan Terbang Antarabangsa Langkawi",
visitors: 3000000,
},
]);
// Data baru untuk kad ringkasan pantas
const quickSummary = ref([
{ title: "Jumlah Pelawat", value: "10.5 Juta", icon: "ic:outline-people" },
{
title: "Pendapatan Pelancongan",
value: "RM 86.14 Bilion",
icon: "ic:outline-attach-money",
},
{
title: "Tempoh Penginapan Purata",
value: "6.1 Hari",
icon: "ic:outline-hotel",
},
{
title: "Kepuasan Pelancong",
value: "92%",
icon: "ic:outline-sentiment-satisfied",
},
]);
// Data Pelawat Malaysia
const visitorData = ref([
{
name: "Pelawat Tempatan",
data: [5000000, 5500000, 6000000, 6500000, 7000000, 7500000],
},
{
name: "Pelawat Asing",
data: [3000000, 3500000, 4000000, 4500000, 5000000, 5500000],
},
]);
// Data Pelawat Asing mengikut Negeri
const foreignVisitorsByState = ref([
{ state: "Selangor", visitors: 1500000 },
{ state: "Pulau Pinang", visitors: 1200000 },
{ state: "Johor", visitors: 1000000 },
{ state: "Sabah", visitors: 800000 },
{ state: "Sarawak", visitors: 600000 },
{ state: "Melaka", visitors: 500000 },
{ state: "Kedah", visitors: 400000 },
{ state: "Negeri Sembilan", visitors: 300000 },
{ state: "Perak", visitors: 250000 },
{ state: "Terengganu", visitors: 200000 },
{ state: "Kelantan", visitors: 150000 },
{ state: "Pahang", visitors: 100000 },
{ state: "Perlis", visitors: 50000 },
]);
// Lapangan Terbang Keberangkatan Teratas
const departureData = ref([
{ airport: "JFK", departures: 1500 },
{ airport: "LHR", departures: 1200 },
{ airport: "CDG", departures: 1000 },
{ airport: "DXB", departures: 800 },
{ airport: "SIN", departures: 600 },
]);
// Data Pelancong Berulang
const repeatVisitorsData = ref([
{ category: "1-2 kali", percentage: 45 },
{ category: "3-5 kali", percentage: 30 },
{ category: "6-10 kali", percentage: 15 },
{ category: ">10 kali", percentage: 10 },
]);
// Data Negara Asal Pelancong Asing Teratas
const topVisitorCountries = ref([
{ country: "Singapura", visitors: 1500000 },
{ country: "Indonesia", visitors: 1200000 },
{ country: "China", visitors: 1000000 },
{ country: "Thailand", visitors: 800000 },
{ country: "India", visitors: 600000 },
]);
const chartOptionsVisitors = computed(() => ({
chart: { height: 350, type: "line" },
stroke: { curve: "smooth", width: 2 },
xaxis: { categories: ["2018", "2019", "2020", "2021", "2022", "2023"] },
yaxis: { title: { text: "Bilangan Pelawat" } },
}));
const chartOptionsForeignVisitors = computed(() => ({
chart: { type: "bar" },
plotOptions: { bar: { horizontal: true } },
xaxis: { categories: foreignVisitorsByState.value.map((item) => item.state) },
}));
const chartOptionsDeparture = computed(() => ({
chart: { type: "bar" },
plotOptions: { bar: { horizontal: true } },
xaxis: { categories: departureData.value.map((item) => item.airport) },
}));
const chartOptionsRepeatVisitors = computed(() => ({
chart: { type: "pie" },
labels: repeatVisitorsData.value.map((item) => item.category),
responsive: [
{
breakpoint: 480,
options: {
chart: {
width: 200,
},
legend: {
position: "bottom",
},
},
},
],
}));
const chartOptionsTopCountries = computed(() => ({
chart: { type: "bar" },
plotOptions: {
bar: { horizontal: false, columnWidth: "55%", endingShape: "rounded" },
},
dataLabels: { enabled: false },
stroke: { show: true, width: 2, colors: ["transparent"] },
xaxis: { categories: topVisitorCountries.value.map((item) => item.country) },
yaxis: { title: { text: "Bilangan Pelawat" } },
fill: { opacity: 1 },
tooltip: {
y: {
formatter: function (val) {
return val.toLocaleString() + " pelawat";
},
},
},
}));
onMounted(() => {
// Sebarang logik yang diperlukan semasa pemasangan
});
</script>
<template>
<div>
<LayoutsBreadcrumb />
<!-- Kad Ringkasan Pantas -->
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-6">
<rs-card
v-for="(item, index) in quickSummary"
:key="index"
class="transition-all duration-300 hover:shadow-lg"
>
<div class="pt-5 pb-3 px-5 flex items-center gap-4">
<div
class="p-5 flex justify-center items-center bg-primary/20 rounded-2xl transition-all duration-300 hover:bg-primary/30"
>
<Icon class="text-primary text-3xl" :name="item.icon"></Icon>
</div>
<div class="flex-1 truncate">
<span class="block font-bold text-2xl leading-tight text-primary">
{{ item.value }}
</span>
<span class="text-sm font-medium text-gray-600">
{{ item.title }}
</span>
</div>
</div>
</rs-card>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<!-- Gambaran Keseluruhan Pelawat Malaysia -->
<rs-card class="col-span-1 lg:col-span-2">
<template #header>
<h2 class="text-xl font-bold text-primary">
Gambaran Keseluruhan Pelawat
</h2>
</template>
<template #body>
<client-only>
<VueApexCharts
width="100%"
height="350"
type="line"
:options="chartOptionsVisitors"
:series="visitorData"
></VueApexCharts>
</client-only>
</template>
</rs-card>
<!-- Pelawat Asing mengikut Negeri -->
<rs-card>
<template #header>
<h2 class="text-lg font-semibold text-primary">
Pelawat Asing mengikut Negeri
</h2>
</template>
<template #body>
<client-only>
<VueApexCharts
width="100%"
height="300"
type="bar"
:options="chartOptionsForeignVisitors"
:series="[
{ data: foreignVisitorsByState.map((item) => item.visitors) },
]"
></VueApexCharts>
</client-only>
</template>
</rs-card>
<!-- Pelancong Berulang -->
<rs-card>
<template #header>
<h2 class="text-lg font-semibold text-primary">
Kekerapan Lawatan Pelancong
</h2>
</template>
<template #body>
<client-only>
<VueApexCharts
width="100%"
height="300"
type="pie"
:options="chartOptionsRepeatVisitors"
:series="repeatVisitorsData.map((item) => item.percentage)"
></VueApexCharts>
</client-only>
</template>
</rs-card>
</div>
<!-- Negara Asal Pelancong Asing Teratas -->
<rs-card class="mb-6">
<template #header>
<h2 class="text-xl font-bold text-primary">
Negara Asal Pelancong Asing Teratas
</h2>
</template>
<template #body>
<client-only>
<VueApexCharts
width="100%"
height="350"
type="bar"
:options="chartOptionsTopCountries"
:series="[
{
name: 'Pelawat',
data: topVisitorCountries.map((item) => item.visitors),
},
]"
></VueApexCharts>
</client-only>
</template>
</rs-card>
<rs-card class="mb-6">
<template #header>
<h2 class="text-xl font-bold text-primary">
Lapangan Terbang Teratas dengan Pelawat Terbanyak
</h2>
</template>
<template #body>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Kedudukan
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Nama Lapangan Terbang
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Jumlah Pelawat
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr
v-for="airport in topAirports"
:key="airport.rank"
class="hover:bg-gray-50 transition-colors duration-200"
>
<td class="px-6 py-4 whitespace-nowrap font-medium">
{{ airport.rank }}
</td>
<td class="px-6 py-4 whitespace-nowrap">{{ airport.name }}</td>
<td
class="px-6 py-4 whitespace-nowrap font-semibold text-primary"
>
{{ airport.visitors.toLocaleString() }}
</td>
</tr>
</tbody>
</table>
</div>
</template>
</rs-card>
</div>
</template>