Done ui niise
This commit is contained in:
parent
45bd30b143
commit
772d425fdd
2
app.vue
2
app.vue
@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
useHead({
|
||||
title: "Niise | Innovative solutions for captivating content",
|
||||
title: "Niise",
|
||||
description: "Home page",
|
||||
htmlAttrs: {
|
||||
lang: "en",
|
||||
|
@ -105,42 +105,78 @@ export default [
|
||||
child: [],
|
||||
meta: {},
|
||||
},
|
||||
{
|
||||
title: "Tipografi",
|
||||
path: "/tipografi",
|
||||
icon: "ph:text-aa",
|
||||
child: [],
|
||||
meta: {},
|
||||
},
|
||||
{
|
||||
title: "Senarai Mesej",
|
||||
path: "/senarai-mesej",
|
||||
icon: "ic:outline-mail",
|
||||
child: [],
|
||||
meta: {},
|
||||
},
|
||||
],
|
||||
meta: {},
|
||||
},
|
||||
{
|
||||
header: "Administration",
|
||||
header: "Prototaip Forensik",
|
||||
description: "",
|
||||
child: [
|
||||
{
|
||||
title: "Configuration",
|
||||
title: "FOR-01",
|
||||
icon: "ph:number-circle-one-fill",
|
||||
child: [
|
||||
{
|
||||
title: "Permohonan",
|
||||
path: "/prototype/for-01/permohonan",
|
||||
child: [],
|
||||
},
|
||||
{
|
||||
title: "Kemaskini Daftar",
|
||||
path: "/prototype/for-01/kemaskini-daftar",
|
||||
child: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
header: "Pengurusan",
|
||||
description: "",
|
||||
child: [
|
||||
{
|
||||
title: "Konfigurasi",
|
||||
icon: "ic:outline-settings",
|
||||
child: [
|
||||
{
|
||||
title: "Environment",
|
||||
title: "Persekitaran",
|
||||
path: "/devtool/config/environment",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Menu Editor",
|
||||
title: "Penyelia Menu",
|
||||
icon: "ci:menu-alt-03",
|
||||
path: "/devtool/menu-editor",
|
||||
child: [],
|
||||
},
|
||||
{
|
||||
title: "Manage Users",
|
||||
title: "Penyelia Pengguna",
|
||||
path: "/devtool/user-management",
|
||||
icon: "ph:user-circle-gear",
|
||||
child: [
|
||||
{
|
||||
title: "User List",
|
||||
title: "Pengguna",
|
||||
path: "/devtool/user-management/user-list",
|
||||
icon: "",
|
||||
child: [],
|
||||
},
|
||||
{
|
||||
title: "Role List",
|
||||
title: "Peranan",
|
||||
path: "/devtool/user-management/role-list",
|
||||
icon: "",
|
||||
child: [],
|
||||
@ -148,21 +184,21 @@ export default [
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Content",
|
||||
title: "Kandungan",
|
||||
icon: "mdi:pencil-ruler",
|
||||
child: [
|
||||
{
|
||||
title: "Editor",
|
||||
title: "Penyelia Kandungan",
|
||||
path: "/devtool/content-editor",
|
||||
},
|
||||
{
|
||||
title: "Template",
|
||||
title: "Templat",
|
||||
path: "/devtool/content-editor/template",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "API Editor",
|
||||
title: "Penyelia API",
|
||||
path: "/devtool/api-editor",
|
||||
icon: "material-symbols:api-rounded",
|
||||
child: [],
|
||||
|
@ -11,475 +11,328 @@ definePageMeta({
|
||||
],
|
||||
});
|
||||
|
||||
const data1 = ref([]);
|
||||
const data2 = ref([]);
|
||||
const data3 = ref([]);
|
||||
const data4 = ref([]);
|
||||
var sparkline1Data = [47, 45, 54, 38, 56, 24, 65];
|
||||
var sparkline2Data = [61, 35, 66, 41, 59, 25, 32];
|
||||
var sparkline3Data = [25, 18, 36, 41, 43, 35, 14];
|
||||
var sparkline4Data = [8, 16, 22, 41, 43, 35, 14];
|
||||
|
||||
const changeKey = ref(0);
|
||||
|
||||
const customers = [
|
||||
// Data baru untuk lapangan terbang teratas
|
||||
const topAirports = ref([
|
||||
{
|
||||
name: "Iqmal",
|
||||
age: "25",
|
||||
city: "Kuala Lumpur",
|
||||
country: "Malaysia",
|
||||
totalPurchase: 1524,
|
||||
purchase: 23,
|
||||
rank: 1,
|
||||
name: "Lapangan Terbang Antarabangsa Kuala Lumpur (KLIA)",
|
||||
visitors: 62000000,
|
||||
},
|
||||
{
|
||||
name: "Adi",
|
||||
age: "45",
|
||||
city: "Pulau Pinang",
|
||||
country: "Malaysia",
|
||||
totalPurchase: 643,
|
||||
purchase: 14,
|
||||
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 },
|
||||
{
|
||||
name: "Raziq",
|
||||
age: "21",
|
||||
city: "Kelantan",
|
||||
country: "Malaysia",
|
||||
totalPurchase: 543,
|
||||
purchase: 12,
|
||||
},
|
||||
{
|
||||
name: "Haqeem",
|
||||
age: "19",
|
||||
city: "Negeri Sembilan",
|
||||
country: "Malaysia",
|
||||
totalPurchase: 258,
|
||||
purchase: 6,
|
||||
},
|
||||
];
|
||||
|
||||
const randomizeArray = function (arg) {
|
||||
var array = arg.slice();
|
||||
var currentIndex = array.length,
|
||||
temporaryValue,
|
||||
randomIndex;
|
||||
|
||||
while (0 !== currentIndex) {
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex -= 1;
|
||||
|
||||
temporaryValue = array[currentIndex];
|
||||
array[currentIndex] = array[randomIndex];
|
||||
array[randomIndex] = temporaryValue;
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
data1.value.push({
|
||||
name: "Revenues",
|
||||
data: randomizeArray(sparkline1Data),
|
||||
});
|
||||
|
||||
data2.value.push({
|
||||
name: "Users",
|
||||
data: randomizeArray(sparkline2Data),
|
||||
});
|
||||
|
||||
data3.value.push({
|
||||
name: "Products",
|
||||
data: randomizeArray(sparkline3Data),
|
||||
});
|
||||
|
||||
data4.value.push({
|
||||
name: "Viewers",
|
||||
data: randomizeArray(sparkline4Data),
|
||||
});
|
||||
|
||||
const chartOptions = computed(() => ({
|
||||
chart: {
|
||||
type: "area",
|
||||
sparkline: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: "smooth",
|
||||
},
|
||||
fill: {
|
||||
opacity: 1,
|
||||
},
|
||||
labels: [...Array(7).keys()].map((n) => `2022-06-0${n + 1}`),
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
},
|
||||
}));
|
||||
|
||||
// Radial Chart
|
||||
|
||||
const radialData = ref([44, 55, 67, 83]);
|
||||
|
||||
const chartOptionsRadial = computed(() => ({
|
||||
chart: {
|
||||
height: 350,
|
||||
type: "radialBar",
|
||||
},
|
||||
plotOptions: {
|
||||
radialBar: {
|
||||
dataLabels: {
|
||||
style: {
|
||||
colors: "#9CA3AF",
|
||||
},
|
||||
name: {
|
||||
offsetY: 30,
|
||||
fontSize: "18px",
|
||||
},
|
||||
value: {
|
||||
offsetY: -15,
|
||||
fontSize: "30px",
|
||||
},
|
||||
total: {
|
||||
show: true,
|
||||
label: "Total",
|
||||
formatter: function (w) {
|
||||
// By default this function returns the average of all series. The below is just an example to show the use of custom formatter function
|
||||
return 249;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
labels: ["Product A", "Product B", "Product C", "Product D"],
|
||||
stroke: {
|
||||
lineCap: "round",
|
||||
},
|
||||
}));
|
||||
|
||||
// Transaction Graph
|
||||
const transactionData = ref([
|
||||
{
|
||||
name: "Bill A",
|
||||
data: [...Array(12).keys()].map((n) => Math.round(Math.random() * 100)),
|
||||
},
|
||||
{
|
||||
name: "Bill B",
|
||||
data: [...Array(12).keys()].map((n) => Math.round(Math.random() * 100)),
|
||||
rank: 5,
|
||||
name: "Lapangan Terbang Antarabangsa Langkawi",
|
||||
visitors: 3000000,
|
||||
},
|
||||
]);
|
||||
|
||||
const chartOptionsTransaction = computed(() => ({
|
||||
chart: {
|
||||
height: 350,
|
||||
type: "area",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
// 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",
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
{
|
||||
title: "Tempoh Penginapan Purata",
|
||||
value: "6.1 Hari",
|
||||
icon: "ic:outline-hotel",
|
||||
},
|
||||
stroke: {
|
||||
curve: "smooth",
|
||||
{
|
||||
title: "Kepuasan Pelancong",
|
||||
value: "92%",
|
||||
icon: "ic:outline-sentiment-satisfied",
|
||||
},
|
||||
colors: ["#6366F1", "#F97316"],
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: {
|
||||
colors: "#9CA3AF",
|
||||
fontSize: "12px",
|
||||
]);
|
||||
|
||||
// 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" },
|
||||
},
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
categories: [
|
||||
"2022-01-01",
|
||||
"2022-02-01",
|
||||
"2022-03-01",
|
||||
"2022-04-01",
|
||||
"2022-05-01",
|
||||
"2022-06-01",
|
||||
"2022-07-01",
|
||||
"2022-08-01",
|
||||
"2022-09-01",
|
||||
"2022-10-01",
|
||||
"2022-11-01",
|
||||
"2022-12-01",
|
||||
],
|
||||
labels: {
|
||||
style: {
|
||||
colors: "#9CA3AF",
|
||||
fontSize: "14px",
|
||||
fontWeight: 400,
|
||||
},
|
||||
datetimeFormatter: {
|
||||
month: "MMM",
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: "top",
|
||||
horizontalAlign: "left",
|
||||
labels: {
|
||||
colors: "#9CA3AF",
|
||||
useSeriesColors: false,
|
||||
},
|
||||
},
|
||||
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: {
|
||||
x: {
|
||||
format: "MMMM",
|
||||
y: {
|
||||
formatter: function (val) {
|
||||
return val.toLocaleString() + " pelawat";
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
changeKey.value++;
|
||||
}, 500);
|
||||
// Sebarang logik yang diperlukan semasa pemasangan
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<LayoutsBreadcrumb />
|
||||
<!-- First Row -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-x-6">
|
||||
<!-- Summary Card #1 -->
|
||||
<rs-card>
|
||||
<div class="summary-1 pt-5 pb-3 px-5 flex items-center gap-4">
|
||||
<!-- 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"
|
||||
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" name="ic:outline-attach-money"></Icon>
|
||||
<Icon class="text-primary text-3xl" :name="item.icon"></Icon>
|
||||
</div>
|
||||
<div class="flex-1 truncate">
|
||||
<span class="block font-semibold text-xl leading-tight">
|
||||
RM 100,000</span
|
||||
>
|
||||
<span class="text-base font-semibold text-gray-500"
|
||||
>Total Revenues</span
|
||||
>
|
||||
<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>
|
||||
<client-only>
|
||||
<VueApexCharts
|
||||
:key="changeKey"
|
||||
width="100%"
|
||||
height="53"
|
||||
:options="{
|
||||
...chartOptions,
|
||||
colors: ['#F43F5E'],
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: Math.max(...data1[0].data) + 10,
|
||||
},
|
||||
}"
|
||||
:series="data1"
|
||||
></VueApexCharts>
|
||||
</client-only>
|
||||
</rs-card>
|
||||
<!-- Summary Card #2 -->
|
||||
<rs-card>
|
||||
<div class="summary-2 pt-5 pb-3 px-5 flex items-center gap-4">
|
||||
<div
|
||||
class="p-5 flex justify-center items-center bg-indigo-100 rounded-2xl"
|
||||
>
|
||||
<Icon
|
||||
class="text-indigo-500"
|
||||
name="ic:outline-account-circle"
|
||||
></Icon>
|
||||
</div>
|
||||
<div class="flex-1 truncate">
|
||||
<span class="block font-semibold text-xl leading-tight"> 512</span>
|
||||
<span class="text-base font-semibold text-gray-500"
|
||||
>Total Users</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<client-only>
|
||||
<VueApexCharts
|
||||
:key="changeKey"
|
||||
width="100%"
|
||||
height="53"
|
||||
:options="{
|
||||
...chartOptions,
|
||||
colors: ['#6366F1'],
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: Math.max(...data2[0].data) + 10,
|
||||
},
|
||||
}"
|
||||
:series="data2"
|
||||
></VueApexCharts>
|
||||
</client-only>
|
||||
</rs-card>
|
||||
<!-- Summary Card #3 -->
|
||||
<rs-card>
|
||||
<div class="summary-3 pt-5 pb-3 px-5 flex items-center gap-4">
|
||||
<div
|
||||
class="p-5 flex justify-center items-center bg-orange-100 rounded-2xl"
|
||||
>
|
||||
<Icon class="text-orange-500" name="ic:outline-shopping-bag"></Icon>
|
||||
</div>
|
||||
<div class="flex-1 truncate">
|
||||
<span class="block font-semibold text-xl leading-tight"> 20</span>
|
||||
<span class="text-base font-semibold text-gray-500"
|
||||
>Total Products</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<client-only>
|
||||
<VueApexCharts
|
||||
:key="changeKey"
|
||||
width="100%"
|
||||
height="53"
|
||||
:options="{
|
||||
...chartOptions,
|
||||
colors: ['#F97316'],
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: Math.max(...data3[0].data) + 10,
|
||||
},
|
||||
}"
|
||||
:series="data3"
|
||||
></VueApexCharts>
|
||||
</client-only>
|
||||
</rs-card>
|
||||
<!-- Summary Card #4 -->
|
||||
<rs-card>
|
||||
<div class="summary-4 pt-5 pb-3 px-5 flex items-center gap-4">
|
||||
<div
|
||||
class="p-5 flex justify-center items-center bg-blue-100 rounded-2xl"
|
||||
>
|
||||
<Icon class="text-blue-500" name="ic:outline-remove-red-eye"></Icon>
|
||||
</div>
|
||||
<div class="flex-1 truncate">
|
||||
<span class="block font-semibold text-xl leading-tight">
|
||||
2,452</span
|
||||
>
|
||||
<span class="text-base font-semibold text-gray-500"
|
||||
>Total Viewers</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<client-only>
|
||||
<VueApexCharts
|
||||
:key="changeKey"
|
||||
width="100%"
|
||||
height="53"
|
||||
:options="{
|
||||
...chartOptions,
|
||||
colors: ['#3B82F6'],
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: Math.max(...data4[0].data) + 10,
|
||||
},
|
||||
}"
|
||||
:series="data4"
|
||||
></VueApexCharts>
|
||||
</client-only>
|
||||
</rs-card>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-x-6">
|
||||
<div class="w-12/2 md:w-8/12 flex flex-col">
|
||||
<!-- Graph -->
|
||||
<rs-card class="flex-1">
|
||||
<template #header> Transaction </template>
|
||||
<template #body>
|
||||
<client-only>
|
||||
<VueApexCharts
|
||||
:key="changeKey"
|
||||
width="100%"
|
||||
height="300"
|
||||
name="area"
|
||||
:options="chartOptionsTransaction"
|
||||
:series="transactionData"
|
||||
></VueApexCharts
|
||||
></client-only>
|
||||
</template>
|
||||
</rs-card>
|
||||
<rs-card class="flex-1">
|
||||
<template #header> Referral</template>
|
||||
<template #body>
|
||||
<div
|
||||
v-for="(val, index) in customers"
|
||||
:key="index"
|
||||
class="flex justify-between items-center rounded-lg bg-[rgb(var(--bg-1))] p-5 first:mt-0 mt-3"
|
||||
>
|
||||
<div class="flex items-center gap-x-4">
|
||||
<img
|
||||
src="@/assets/img/avatar/user.webp"
|
||||
class="h-10 w-10 rounded-lg"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-col">
|
||||
<span
|
||||
class="text-gray-900 dark:text-white font-semibold text-lg"
|
||||
>
|
||||
{{ val.name }}
|
||||
</span>
|
||||
<span class="text-gray-600 dark:text-gray-50 text-sm">
|
||||
RM{{ parseFloat(val.totalPurchase).toFixed(2) }} |
|
||||
{{ val.purchase }} sold
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="flex items-center p-4 rounded-full bg-[rgb(var(--bg-2))] hover:bg-[rgb(var(--bg-2))]/10 shadow-md"
|
||||
>
|
||||
<Icon size="20px" name="ic:baseline-mail-outline"></Icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
<div class="w-12/2 md:w-4/12 flex flex-col">
|
||||
<!-- Monthly Target Radial -->
|
||||
<rs-card class="flex-1">
|
||||
<template #header> Monthly Target </template>
|
||||
<template #body>
|
||||
<client-only>
|
||||
<VueApexCharts
|
||||
:key="changeKey"
|
||||
width="100%"
|
||||
height="300"
|
||||
name="radialBar"
|
||||
:options="chartOptionsRadial"
|
||||
:series="radialData"
|
||||
></VueApexCharts>
|
||||
</client-only>
|
||||
<hr class="my-4" />
|
||||
<p class="text-xl py-5 font-medium">Products</p>
|
||||
<div
|
||||
class="flex item-center gap-x-4"
|
||||
:class="{
|
||||
'mt-0': index === 0,
|
||||
'mt-3': index !== 0,
|
||||
}"
|
||||
v-for="(val, index) in ['A', 'B', 'C', 'D', 'E']"
|
||||
:key="index"
|
||||
>
|
||||
<img
|
||||
src="@/assets/img/default-thumbnail.jpg"
|
||||
class="h-20 w-20 object-cover rounded-lg"
|
||||
/>
|
||||
<div class="flex-1 flex items-center">
|
||||
<div>
|
||||
<span class="font-semibold text-lg leading-tight"
|
||||
>Product {{ val }}</span
|
||||
>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</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>
|
||||
|
@ -3,7 +3,7 @@
|
||||
import { useThemeStore } from "~/stores/theme";
|
||||
|
||||
definePageMeta({
|
||||
title: "API Code Editor",
|
||||
title: "Penyunting Kod API",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
});
|
||||
@ -63,8 +63,8 @@ if (data.value.statusCode === 200) {
|
||||
} else {
|
||||
$swal
|
||||
.fire({
|
||||
title: "Error",
|
||||
text: "The API you are trying to edit is not found. Please choose a API to edit.",
|
||||
title: "Ralat",
|
||||
text: "API yang anda cuba sunting tidak dijumpai. Sila pilih API untuk disunting.",
|
||||
icon: "error",
|
||||
confirmButtonText: "Ok",
|
||||
})
|
||||
@ -134,8 +134,8 @@ const saveCode = async () => {
|
||||
|
||||
if (linterError.value) {
|
||||
$swal.fire({
|
||||
title: "Error",
|
||||
text: "There is an error in your code. Please fix it before saving.",
|
||||
title: "Ralat",
|
||||
text: "Terdapat ralat dalam kod anda. Sila betulkannya sebelum menyimpan.",
|
||||
icon: "error",
|
||||
confirmButtonText: "Ok",
|
||||
});
|
||||
@ -153,8 +153,8 @@ const saveCode = async () => {
|
||||
});
|
||||
if (data.value.statusCode === 200) {
|
||||
$swal.fire({
|
||||
title: "Success",
|
||||
text: "The code has been saved successfully.",
|
||||
title: "Berjaya",
|
||||
text: "Kod telah berjaya disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonText: "Ok",
|
||||
timer: 1000,
|
||||
@ -175,11 +175,11 @@ const saveCode = async () => {
|
||||
}}</rs-alert>
|
||||
<rs-card>
|
||||
<rs-tab fill>
|
||||
<rs-tab-item title="Editor">
|
||||
<rs-tab-item title="Penyunting">
|
||||
<div class="flex justify-end gap-2 mb-4">
|
||||
<rs-button class="!p-2" @click="formatCode">
|
||||
<Icon name="simple-icons:prettier" size="20px" class="mr-1" />
|
||||
Format Code</rs-button
|
||||
Format Kod</rs-button
|
||||
>
|
||||
<rs-button class="!p-2" @click="saveCode">
|
||||
<Icon
|
||||
@ -187,7 +187,7 @@ const saveCode = async () => {
|
||||
size="20px"
|
||||
class="mr-1"
|
||||
/>
|
||||
Save API
|
||||
Simpan API
|
||||
</rs-button>
|
||||
</div>
|
||||
<Transition>
|
||||
@ -198,12 +198,12 @@ const saveCode = async () => {
|
||||
size="20px"
|
||||
/>
|
||||
<div>
|
||||
<div class="font-bold">ESLint Error</div>
|
||||
<div class="font-bold">Ralat ESLint</div>
|
||||
<div class="text-sm">
|
||||
{{ linterErrorText }}
|
||||
</div>
|
||||
<div class="text-xs mt-2">
|
||||
Line: {{ linterErrorLine }} Column: {{ linterErrorColumn }}
|
||||
Baris: {{ linterErrorLine }} Lajur: {{ linterErrorColumn }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -216,7 +216,7 @@ const saveCode = async () => {
|
||||
mode="javascript"
|
||||
/>
|
||||
</rs-tab-item>
|
||||
<rs-tab-item title="API Tester">
|
||||
<rs-tab-item title="Penguji API">
|
||||
<rs-api-tester :url="route.query?.path" />
|
||||
</rs-tab-item>
|
||||
</rs-tab>
|
||||
|
@ -78,8 +78,8 @@ const saveAddAPI = async () => {
|
||||
|
||||
if (data.value.statusCode === 200) {
|
||||
nuxtApp.$swal.fire({
|
||||
title: "Success",
|
||||
text: "The code has been saved successfully.",
|
||||
title: "Berjaya",
|
||||
text: "Kod telah berjaya disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonText: "Ok",
|
||||
timer: 1000,
|
||||
@ -105,8 +105,8 @@ const saveEditAPI = async () => {
|
||||
|
||||
if (data.value.statusCode === 200) {
|
||||
nuxtApp.$swal.fire({
|
||||
title: "Success",
|
||||
text: "The code has been saved successfully.",
|
||||
title: "Berjaya",
|
||||
text: "Kod telah berjaya disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonText: "Ok",
|
||||
timer: 1000,
|
||||
@ -122,13 +122,14 @@ const saveEditAPI = async () => {
|
||||
const deleteAPI = async (apiURL) => {
|
||||
nuxtApp.$swal
|
||||
.fire({
|
||||
title: "Are you sure to delete this API?",
|
||||
text: "You won't be able to revert this!",
|
||||
title: "Adakah anda pasti untuk memadam API ini?",
|
||||
text: "Anda tidak akan dapat memulihkan ini!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
confirmButtonText: "Ya, padamkan!",
|
||||
cancelButtonText: "Batal",
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
@ -143,8 +144,8 @@ const deleteAPI = async (apiURL) => {
|
||||
|
||||
if (data.value.statusCode === 200) {
|
||||
nuxtApp.$swal.fire({
|
||||
title: "Success",
|
||||
text: "The code has been saved successfully.",
|
||||
title: "Berjaya",
|
||||
text: "Kod telah berjaya disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonText: "Ok",
|
||||
timer: 1000,
|
||||
@ -164,13 +165,13 @@ const deleteAPI = async (apiURL) => {
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<Icon class="mr-2 flex justify-center" name="ic:outline-info"></Icon
|
||||
>Info
|
||||
>Maklumat
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="mb-4">
|
||||
This page is used to edit the api for the server side. You can edit
|
||||
the api by choosing the api to edit from the card list below.
|
||||
Halaman ini digunakan untuk mengedit API untuk bahagian pelayan. Anda boleh mengedit
|
||||
API dengan memilih API untuk diedit dari senarai kad di bawah.
|
||||
</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
@ -180,14 +181,14 @@ const deleteAPI = async (apiURL) => {
|
||||
<div class="flex justify-end items-center mb-4">
|
||||
<rs-button @click="openModalAdd">
|
||||
<Icon name="material-symbols:add" class="mr-1"></Icon>
|
||||
Add API
|
||||
Tambah API
|
||||
</rs-button>
|
||||
</div>
|
||||
|
||||
<!-- Search Button -->
|
||||
<FormKit
|
||||
v-model="searchText"
|
||||
placeholder="Search Title..."
|
||||
placeholder="Cari Tajuk..."
|
||||
type="search"
|
||||
class="mb-4"
|
||||
/>
|
||||
@ -214,7 +215,7 @@ const deleteAPI = async (apiURL) => {
|
||||
name="material-symbols:code-blocks-outline-rounded"
|
||||
class="mr-2"
|
||||
/>
|
||||
Code Editor
|
||||
Editor Kod
|
||||
</rs-button>
|
||||
<div class="flex gap-2">
|
||||
<rs-button @click="openModalEdit(api.url)">
|
||||
@ -233,9 +234,9 @@ const deleteAPI = async (apiURL) => {
|
||||
</rs-card>
|
||||
|
||||
<rs-modal
|
||||
title="Add API"
|
||||
title="Tambah API"
|
||||
v-model="showModalAdd"
|
||||
ok-title="Save"
|
||||
ok-title="Simpan"
|
||||
:ok-callback="saveAddAPI"
|
||||
>
|
||||
<FormKit type="text" label="Url" v-model="showModalAddForm.apiURL">
|
||||
@ -248,9 +249,9 @@ const deleteAPI = async (apiURL) => {
|
||||
</rs-modal>
|
||||
|
||||
<rs-modal
|
||||
title="Add API"
|
||||
title="Edit API"
|
||||
v-model="showModalEdit"
|
||||
ok-title="Save"
|
||||
ok-title="Simpan"
|
||||
:ok-callback="saveEditAPI"
|
||||
>
|
||||
<FormKit type="text" label="Url" v-model="showModalEditForm.apiURL">
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Code Editor",
|
||||
title: "Penyunting Kod",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
});
|
||||
@ -28,8 +28,8 @@ const page = router.getRoutes().find((page) => {
|
||||
if (!route.query.page || !page) {
|
||||
$swal
|
||||
.fire({
|
||||
title: "Error",
|
||||
text: "The page you are trying to edit is not found. Please choose a page to edit.",
|
||||
title: "Ralat",
|
||||
text: "Halaman yang anda cuba sunting tidak dijumpai. Sila pilih halaman untuk disunting.",
|
||||
icon: "error",
|
||||
confirmButtonText: "Ok",
|
||||
})
|
||||
@ -62,8 +62,8 @@ if (data.value.statusCode === 200) {
|
||||
if (data.value?.mode == "index") page.path = page.path + "/index";
|
||||
} else {
|
||||
$swal.fire({
|
||||
title: "Error",
|
||||
text: "The page you are trying to edit is not found. Please choose a page to edit. You will be redirected to the content editor page.",
|
||||
title: "Ralat",
|
||||
text: "Halaman yang anda cuba sunting tidak dijumpai. Sila pilih halaman untuk disunting. Anda akan dialihkan ke halaman penyunting kandungan.",
|
||||
icon: "error",
|
||||
confirmButtonText: "Ok",
|
||||
timer: 3000,
|
||||
@ -132,8 +132,8 @@ const saveCode = async () => {
|
||||
|
||||
if (linterError.value) {
|
||||
$swal.fire({
|
||||
title: "Error",
|
||||
text: "There is an error in your code. Please fix it before saving.",
|
||||
title: "Ralat",
|
||||
text: "Terdapat ralat dalam kod anda. Sila betulkannya sebelum menyimpan.",
|
||||
icon: "error",
|
||||
confirmButtonText: "Ok",
|
||||
});
|
||||
@ -150,8 +150,8 @@ const saveCode = async () => {
|
||||
});
|
||||
if (data.value.statusCode === 200) {
|
||||
$swal.fire({
|
||||
title: "Success",
|
||||
text: "The code has been saved successfully.",
|
||||
title: "Berjaya",
|
||||
text: "Kod telah berjaya disimpan.",
|
||||
icon: "success",
|
||||
confirmButtonText: "Ok",
|
||||
timer: 1000,
|
||||
@ -175,7 +175,7 @@ const saveCode = async () => {
|
||||
<div class="flex justify-end gap-2 mb-4">
|
||||
<rs-button class="!p-2" @click="formatCode">
|
||||
<Icon name="simple-icons:prettier" size="20px" class="mr-1" />
|
||||
Format Code</rs-button
|
||||
Format Kod</rs-button
|
||||
>
|
||||
<rs-button class="!p-2" @click="saveCode">
|
||||
<Icon
|
||||
@ -183,7 +183,7 @@ const saveCode = async () => {
|
||||
size="20px"
|
||||
class="mr-1"
|
||||
/>
|
||||
Save Code
|
||||
Simpan Kod
|
||||
</rs-button>
|
||||
</div>
|
||||
<Transition>
|
||||
@ -191,12 +191,12 @@ const saveCode = async () => {
|
||||
<div class="flex gap-2">
|
||||
<Icon name="material-symbols:error-outline-rounded" size="20px" />
|
||||
<div>
|
||||
<div class="font-bold">ESLint Error</div>
|
||||
<div class="font-bold">Ralat ESLint</div>
|
||||
<div class="text-sm">
|
||||
{{ linterErrorText }}
|
||||
</div>
|
||||
<div class="text-xs mt-2">
|
||||
Line: {{ linterErrorLine }} Column: {{ linterErrorColumn }}
|
||||
Baris: {{ linterErrorLine }} Lajur: {{ linterErrorColumn }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Content Editor",
|
||||
title: "Penyunting Kandungan",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
});
|
||||
@ -15,7 +15,7 @@ const pages = getPages.filter((page) => {
|
||||
return (
|
||||
page.path.includes("/devtool") === false &&
|
||||
page.meta?.title &&
|
||||
page.meta?.title !== "Home" &&
|
||||
page.meta?.title !== "Laman Utama" &&
|
||||
page.name
|
||||
);
|
||||
});
|
||||
@ -44,7 +44,7 @@ const capitalizeSentence = (sentence) => {
|
||||
.join(" ");
|
||||
};
|
||||
|
||||
const templateOptions = ref([{ label: "Select Template", value: "" }]);
|
||||
const templateOptions = ref([{ label: "Pilih Templat", value: "" }]);
|
||||
const selectTemplate = ref("");
|
||||
|
||||
const { data: templates } = await useFetch(
|
||||
@ -74,13 +74,13 @@ const importTemplate = (pageName) => {
|
||||
const confirmModal = async () => {
|
||||
$swal
|
||||
.fire({
|
||||
title: "Are you sure you want to import this template?",
|
||||
text: "This action cannot be undone.",
|
||||
title: "Adakah anda pasti mahu mengimport templat ini?",
|
||||
text: "Tindakan ini tidak boleh dibatalkan.",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes",
|
||||
confirmButtonText: "Ya",
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
@ -98,7 +98,7 @@ const confirmModal = async () => {
|
||||
|
||||
if (res.value.statusCode == 200) {
|
||||
$swal.fire({
|
||||
title: "Success",
|
||||
title: "Berjaya",
|
||||
text: res.value.message,
|
||||
icon: "success",
|
||||
confirmButtonText: "Ok",
|
||||
@ -123,14 +123,14 @@ const confirmModal = async () => {
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<Icon class="mr-2 flex justify-center" name="ic:outline-info"></Icon
|
||||
>Info
|
||||
>Maklumat
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="mb-4">
|
||||
This page is used to edit the content of a page. You can edit the
|
||||
content of the page by choosing the page to edit from the card list
|
||||
below.
|
||||
Halaman ini digunakan untuk menyunting kandungan halaman. Anda boleh menyunting
|
||||
kandungan halaman dengan memilih halaman untuk disunting dari senarai kad
|
||||
di bawah.
|
||||
</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
@ -140,7 +140,7 @@ const confirmModal = async () => {
|
||||
<!-- Search Button -->
|
||||
<FormKit
|
||||
v-model="searchText"
|
||||
placeholder="Search Title..."
|
||||
placeholder="Cari Tajuk..."
|
||||
type="search"
|
||||
/>
|
||||
|
||||
@ -152,7 +152,7 @@ const confirmModal = async () => {
|
||||
class="page border-2 border-gray-400 border-dashed rounded-lg"
|
||||
style="min-height: 250px"
|
||||
>
|
||||
Add New Page
|
||||
Tambah Halaman Baru
|
||||
</div> -->
|
||||
<div
|
||||
v-for="page in searchPages()"
|
||||
@ -180,13 +180,6 @@ const confirmModal = async () => {
|
||||
class="button-list flex justify-between border-t pt-4 border-gray-300"
|
||||
>
|
||||
<div class="flex gap-x-2">
|
||||
<nuxt-link
|
||||
:to="`/devtool/content-editor/canvas?page=${page.name}`"
|
||||
>
|
||||
<rs-button variant="primary" class="!py-2 !px-3">
|
||||
<Icon name="ph:paint-brush-broad"></Icon>
|
||||
</rs-button>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
:to="`/devtool/content-editor/code?page=${page.name}`"
|
||||
>
|
||||
@ -214,18 +207,18 @@ const confirmModal = async () => {
|
||||
<FormKit
|
||||
v-model="selectTemplate"
|
||||
type="select"
|
||||
label="Content Template"
|
||||
label="Templat Kandungan"
|
||||
:options="templateOptions"
|
||||
validation="required"
|
||||
validation-visibility="dirty"
|
||||
help="Please choose carefully the template that you want to import. This action cannot be undone."
|
||||
help="Sila pilih dengan teliti templat yang anda ingin import. Tindakan ini tidak boleh dibatalkan."
|
||||
/>
|
||||
<template #footer>
|
||||
<rs-button @click="showModal = false" variant="primary-text">
|
||||
Cancel
|
||||
Batal
|
||||
</rs-button>
|
||||
<rs-button @click="confirmModal" :disabled="!selectTemplate"
|
||||
>Confirm</rs-button
|
||||
>Sahkan</rs-button
|
||||
>
|
||||
</template>
|
||||
</rs-modal>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Template Editor",
|
||||
title: "Penyunting Templat",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
});
|
||||
@ -31,14 +31,14 @@ const searchTemplate = () => {
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<Icon class="mr-2 flex justify-center" name="ic:outline-info"></Icon
|
||||
>Info
|
||||
>Maklumat
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="mb-4">
|
||||
This webpage serves as a platform for template management, enabling
|
||||
users to select and utilize templates for rendering pages according to
|
||||
their chosen design.
|
||||
Laman web ini berfungsi sebagai platform untuk pengurusan templat, membolehkan
|
||||
pengguna memilih dan menggunakan templat untuk memaparkan halaman mengikut
|
||||
reka bentuk pilihan mereka.
|
||||
</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
@ -48,7 +48,7 @@ const searchTemplate = () => {
|
||||
<!-- Search Button -->
|
||||
<FormKit
|
||||
v-model="searchText"
|
||||
placeholder="Search Title..."
|
||||
placeholder="Cari Tajuk..."
|
||||
type="search"
|
||||
/>
|
||||
|
||||
@ -60,7 +60,7 @@ const searchTemplate = () => {
|
||||
class="page border-2 border-gray-400 border-dashed rounded-lg"
|
||||
style="min-height: 250px"
|
||||
>
|
||||
Add New Page
|
||||
Tambah Halaman Baru
|
||||
</div> -->
|
||||
<div
|
||||
v-for="val in searchTemplate()"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Template Viewer",
|
||||
title: "Pelihat Templat",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
});
|
||||
|
@ -408,14 +408,14 @@ const addMenuFromList = () => {
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<Icon class="mr-2 flex justify-center" name="ic:outline-info"></Icon
|
||||
>Info
|
||||
>Maklumat
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="mb-4">
|
||||
This page is used to edit the menu of the website. You can add, edit,
|
||||
and delete menu items. You can also change the order of the menu items
|
||||
by dragging and dropping them.
|
||||
Halaman ini digunakan untuk mengedit menu laman web. Anda boleh
|
||||
menambah, mengedit, dan memadam item menu. Anda juga boleh mengubah
|
||||
susunan item menu dengan menyeret dan melepaskannya.
|
||||
</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
@ -423,11 +423,11 @@ const addMenuFromList = () => {
|
||||
<rs-card>
|
||||
<div class="pt-2">
|
||||
<rs-tab fill>
|
||||
<rs-tab-item title="All Menu">
|
||||
<rs-tab-item title="Semua Menu">
|
||||
<div class="flex justify-end items-center mb-4">
|
||||
<rs-button @click="openModalAdd">
|
||||
<Icon name="material-symbols:add" class="mr-1"></Icon>
|
||||
Add Menu
|
||||
Tambah Menu
|
||||
</rs-button>
|
||||
</div>
|
||||
<!-- Table All Menu -->
|
||||
@ -491,18 +491,18 @@ const addMenuFromList = () => {
|
||||
</template>
|
||||
</rs-table>
|
||||
</rs-tab-item>
|
||||
<rs-tab-item title="Manage Side Menu">
|
||||
<rs-tab-item title="Urus Menu Sisi">
|
||||
<div class="flex justify-end items-center mb-4">
|
||||
<rs-button
|
||||
class="mr-2"
|
||||
@click="showCode ? (showCode = false) : (showCode = true)"
|
||||
>
|
||||
<Icon name="ic:baseline-code" class="mr-2"></Icon>
|
||||
{{ showCode ? "Hide" : "Show" }} JSON Code
|
||||
{{ showCode ? "Sembunyikan" : "Tunjukkan" }} Kod JSON
|
||||
</rs-button>
|
||||
<rs-button @click="overwriteJsonFileLocal(sideMenuList)">
|
||||
<Icon name="mdi:content-save-outline" class="mr-2"></Icon>
|
||||
Save Menu
|
||||
Simpan Menu
|
||||
</rs-button>
|
||||
</div>
|
||||
|
||||
@ -510,7 +510,7 @@ const addMenuFromList = () => {
|
||||
<div>
|
||||
<FormKit
|
||||
type="search"
|
||||
placeholder="Search Menu..."
|
||||
placeholder="Cari Menu..."
|
||||
outer-class="mb-5"
|
||||
v-model="searchInput"
|
||||
/>
|
||||
@ -526,7 +526,9 @@ const addMenuFromList = () => {
|
||||
:sort="false"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<rs-card class="p-4 mb-4 border-2 border-[rgb(var(--border-color))] !shadow-none">
|
||||
<rs-card
|
||||
class="p-4 mb-4 border-2 border-[rgb(var(--border-color))] !shadow-none"
|
||||
>
|
||||
<div class="flex justify-between items-center">
|
||||
<p>
|
||||
{{ kebabtoTitle(element.name) }} (
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Role List",
|
||||
title: "Senarai Peranan",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
});
|
||||
@ -38,8 +38,8 @@ const showModalDeleteForm = ref({
|
||||
});
|
||||
|
||||
const statusDropdown = ref([
|
||||
{ label: "Active", value: "ACTIVE" },
|
||||
{ label: "Inactive", value: "INACTIVE" },
|
||||
{ label: "Aktif", value: "ACTIVE" },
|
||||
{ label: "Tidak Aktif", value: "INACTIVE" },
|
||||
]);
|
||||
|
||||
const roleListbyUser = ref([]);
|
||||
@ -199,8 +199,8 @@ const saveUser = async () => {
|
||||
if (data.value.statusCode === 200) {
|
||||
$swal.fire({
|
||||
icon: "success",
|
||||
title: "Success",
|
||||
text: "User has been added successfully",
|
||||
title: "Berjaya",
|
||||
text: "Pengguna telah berjaya ditambah",
|
||||
});
|
||||
|
||||
await getUserList();
|
||||
@ -209,7 +209,7 @@ const saveUser = async () => {
|
||||
} else {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
title: "Ralat",
|
||||
text: data.value.message,
|
||||
});
|
||||
}
|
||||
@ -227,8 +227,8 @@ const saveRole = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "success",
|
||||
title: "Success",
|
||||
text: "Role has been updated successfully",
|
||||
title: "Berjaya",
|
||||
text: "Peranan telah berjaya dikemas kini",
|
||||
timer: 1000,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
@ -242,7 +242,7 @@ const saveRole = async () => {
|
||||
} else {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
title: "Ralat",
|
||||
text: data.value.message,
|
||||
});
|
||||
}
|
||||
@ -257,8 +257,8 @@ const saveRole = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "success",
|
||||
title: "Success",
|
||||
text: "Role has been added",
|
||||
title: "Berjaya",
|
||||
text: "Peranan telah ditambah",
|
||||
timer: 1000,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
@ -272,7 +272,7 @@ const saveRole = async () => {
|
||||
} else {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
title: "Ralat",
|
||||
text: data.value.message,
|
||||
});
|
||||
}
|
||||
@ -290,8 +290,8 @@ const deleteRole = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "success",
|
||||
title: "Success",
|
||||
text: "User has been deleted",
|
||||
title: "Berjaya",
|
||||
text: "Pengguna telah dipadam",
|
||||
timer: 1000,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
@ -304,7 +304,7 @@ const deleteRole = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
title: "Ralat",
|
||||
text: data.value.message,
|
||||
});
|
||||
}
|
||||
@ -331,13 +331,13 @@ function groupRoleByUser() {
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<Icon class="mr-2 flex justify-center" name="ic:outline-info"></Icon
|
||||
>Info
|
||||
>Maklumat
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="mb-4">
|
||||
This page is only accessible by admin users. You can manage users
|
||||
here. You can also add new users. You can also change user roles.
|
||||
Halaman ini hanya boleh diakses oleh pengguna admin. Anda boleh menguruskan pengguna
|
||||
di sini. Anda juga boleh menambah pengguna baru. Anda juga boleh menukar peranan pengguna.
|
||||
</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
@ -345,11 +345,11 @@ function groupRoleByUser() {
|
||||
<rs-card>
|
||||
<div class="pt-2">
|
||||
<rs-tab fill>
|
||||
<rs-tab-item title="All Role">
|
||||
<rs-tab-item title="Semua Peranan">
|
||||
<div class="flex justify-end items-center mb-4">
|
||||
<rs-button @click="openModal(null, 'add')">
|
||||
<Icon name="material-symbols:add" class="mr-1"></Icon>
|
||||
Add Role
|
||||
Tambah Peranan
|
||||
</rs-button>
|
||||
</div>
|
||||
<rs-table
|
||||
@ -397,31 +397,31 @@ function groupRoleByUser() {
|
||||
</rs-card>
|
||||
|
||||
<rs-modal
|
||||
:title="modalType == 'edit' ? 'Edit Role' : 'Add Role'"
|
||||
ok-title="Save"
|
||||
:title="modalType == 'edit' ? 'Sunting Peranan' : 'Tambah Peranan'"
|
||||
ok-title="Simpan"
|
||||
:ok-callback="saveRole"
|
||||
v-model="showModal"
|
||||
>
|
||||
<FormKit
|
||||
type="text"
|
||||
v-model="showModalForm.name"
|
||||
label="Name"
|
||||
label="Nama"
|
||||
validation="required"
|
||||
validation-visibility="live"
|
||||
/>
|
||||
<FormKit
|
||||
type="textarea"
|
||||
v-model="showModalForm.description"
|
||||
label="Description"
|
||||
label="Penerangan"
|
||||
/>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<label
|
||||
class="formkit-label font-semibold text-gray-700 dark:text-gray-200 blockfont-semibold text-sm formkit-invalid:text-red-500 dark:formkit-invalid:text-danger"
|
||||
for="input_4"
|
||||
>
|
||||
Users
|
||||
Pengguna
|
||||
</label>
|
||||
<rs-button size="sm" @click="openModalUser"> Add User </rs-button>
|
||||
<rs-button size="sm" @click="openModalUser"> Tambah Pengguna </rs-button>
|
||||
</div>
|
||||
<v-select
|
||||
class="formkit-vselect"
|
||||
@ -432,7 +432,7 @@ function groupRoleByUser() {
|
||||
<FormKit
|
||||
type="checkbox"
|
||||
v-model="checkAllUser"
|
||||
label="All Users"
|
||||
label="Semua Pengguna"
|
||||
input-class="icon-check"
|
||||
/>
|
||||
<FormKit
|
||||
@ -446,9 +446,9 @@ function groupRoleByUser() {
|
||||
|
||||
<!-- Modal Role -->
|
||||
<rs-modal
|
||||
title="Add User"
|
||||
ok-title="Save"
|
||||
cancel-title="Back"
|
||||
title="Tambah Pengguna"
|
||||
ok-title="Simpan"
|
||||
cancel-title="Kembali"
|
||||
:cancel-callback="closeModalUser"
|
||||
:ok-callback="saveUser"
|
||||
v-model="showModalUser"
|
||||
@ -457,19 +457,19 @@ function groupRoleByUser() {
|
||||
type="text"
|
||||
v-model="showModalUserForm.username"
|
||||
name="username"
|
||||
label="Username"
|
||||
label="Nama Pengguna"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
v-model="showModalUserForm.fullname"
|
||||
name="fullname"
|
||||
label="Fullname"
|
||||
label="Nama Penuh"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
v-model="showModalUserForm.email"
|
||||
name="email"
|
||||
label="Email"
|
||||
label="E-mel"
|
||||
validation="email"
|
||||
validation-visibility="dirty"
|
||||
/>
|
||||
@ -477,7 +477,7 @@ function groupRoleByUser() {
|
||||
type="mask"
|
||||
v-model="showModalUserForm.phone"
|
||||
name="phone"
|
||||
label="Phone"
|
||||
label="Telefon"
|
||||
mask="###########"
|
||||
/>
|
||||
|
||||
@ -492,14 +492,14 @@ function groupRoleByUser() {
|
||||
|
||||
<!-- Modal Delete Confirmation -->
|
||||
<rs-modal
|
||||
title="Delete Confirmation"
|
||||
ok-title="Yes"
|
||||
cancel-title="No"
|
||||
title="Pengesahan Padam"
|
||||
ok-title="Ya"
|
||||
cancel-title="Tidak"
|
||||
:ok-callback="deleteRole"
|
||||
v-model="showModalDelete"
|
||||
>
|
||||
<p>
|
||||
Are you sure want to delete this role ({{ showModalDeleteForm.name }})?
|
||||
Adakah anda pasti mahu memadam peranan ini ({{ showModalDeleteForm.name }})?
|
||||
</p>
|
||||
</rs-modal>
|
||||
</div>
|
||||
|
@ -37,8 +37,8 @@ const showModalDeleteForm = ref({
|
||||
});
|
||||
|
||||
const statusDropdown = ref([
|
||||
{ label: "Active", value: "ACTIVE" },
|
||||
{ label: "Inactive", value: "INACTIVE" },
|
||||
{ label: "Aktif", value: "ACTIVE" },
|
||||
{ label: "Tidak Aktif", value: "INACTIVE" },
|
||||
]);
|
||||
|
||||
const checkAllRole = ref(false);
|
||||
@ -195,8 +195,8 @@ const saveUser = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "success",
|
||||
title: "Success",
|
||||
text: "User has been added",
|
||||
title: "Berjaya",
|
||||
text: "Pengguna telah ditambah",
|
||||
timer: 1000,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
@ -210,7 +210,7 @@ const saveUser = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
title: "Ralat",
|
||||
text: data.value.message,
|
||||
});
|
||||
}
|
||||
@ -225,8 +225,8 @@ const saveUser = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "success",
|
||||
title: "Success",
|
||||
text: "User has been updated",
|
||||
title: "Berjaya",
|
||||
text: "Pengguna telah dikemaskini",
|
||||
timer: 1000,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
@ -240,7 +240,7 @@ const saveUser = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
title: "Ralat",
|
||||
text: data.value.message,
|
||||
});
|
||||
}
|
||||
@ -258,8 +258,8 @@ const deleteUser = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "success",
|
||||
title: "Success",
|
||||
text: "User has been deleted",
|
||||
title: "Berjaya",
|
||||
text: "Pengguna telah dipadam",
|
||||
timer: 1000,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
@ -272,7 +272,7 @@ const deleteUser = async () => {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
title: "Ralat",
|
||||
text: data.value.message,
|
||||
});
|
||||
}
|
||||
@ -298,7 +298,7 @@ const saveRole = async () => {
|
||||
if (data.value.statusCode === 200) {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
title: "Success",
|
||||
title: "Berjaya",
|
||||
text: data.value.message,
|
||||
icon: "success",
|
||||
timer: 1000,
|
||||
@ -310,7 +310,7 @@ const saveRole = async () => {
|
||||
} else {
|
||||
$swal.fire({
|
||||
position: "center",
|
||||
title: "Error",
|
||||
title: "Ralat",
|
||||
text: data.value.message,
|
||||
icon: "error",
|
||||
});
|
||||
@ -338,13 +338,13 @@ function groupUserByRole() {
|
||||
<template #header>
|
||||
<div class="flex">
|
||||
<Icon class="mr-2 flex justify-center" name="ic:outline-info"></Icon
|
||||
>Info
|
||||
>Maklumat
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="mb-4">
|
||||
This page is only accessible by admin users. You can manage users
|
||||
here. You can also add new users. You can also change user roles.
|
||||
Halaman ini hanya boleh diakses oleh pengguna admin. Anda boleh menguruskan pengguna
|
||||
di sini. Anda juga boleh menambah pengguna baru. Anda juga boleh menukar peranan pengguna.
|
||||
</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
@ -352,11 +352,11 @@ function groupUserByRole() {
|
||||
<rs-card>
|
||||
<div class="pt-2">
|
||||
<rs-tab fill>
|
||||
<rs-tab-item title="All User">
|
||||
<rs-tab-item title="Semua Pengguna">
|
||||
<div class="flex justify-end items-center mb-4">
|
||||
<rs-button @click="openModal(null, 'add')">
|
||||
<Icon name="material-symbols:add" class="mr-1"></Icon>
|
||||
Add User
|
||||
Tambah Pengguna
|
||||
</rs-button>
|
||||
</div>
|
||||
<rs-table
|
||||
@ -405,8 +405,8 @@ function groupUserByRole() {
|
||||
</rs-card>
|
||||
|
||||
<rs-modal
|
||||
:title="modalType == 'edit' ? 'Edit User' : 'Add User'"
|
||||
ok-title="Save"
|
||||
:title="modalType == 'edit' ? 'Sunting Pengguna' : 'Tambah Pengguna'"
|
||||
ok-title="Simpan"
|
||||
:ok-callback="saveUser"
|
||||
v-model="showModal"
|
||||
>
|
||||
@ -414,20 +414,20 @@ function groupUserByRole() {
|
||||
type="text"
|
||||
v-model="showModalForm.username"
|
||||
name="username"
|
||||
label="Username"
|
||||
label="Nama Pengguna"
|
||||
:disabled="modalType == 'edit' ? true : false"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
v-model="showModalForm.fullname"
|
||||
name="fullname"
|
||||
label="Fullname"
|
||||
label="Nama Penuh"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
v-model="showModalForm.email"
|
||||
name="email"
|
||||
label="Email"
|
||||
label="E-mel"
|
||||
validation="email"
|
||||
validation-visibility="dirty"
|
||||
/>
|
||||
@ -435,7 +435,7 @@ function groupUserByRole() {
|
||||
type="mask"
|
||||
v-model="showModalForm.phone"
|
||||
name="phone"
|
||||
label="Phone"
|
||||
label="Telefon"
|
||||
mask="###########"
|
||||
/>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
@ -443,9 +443,9 @@ function groupUserByRole() {
|
||||
class="formkit-label flex items-center gap-x-4 font-semibold text-gray-700 dark:text-gray-200 blockfont-semibold text-sm formkit-invalid:text-red-500 dark:formkit-invalid:text-danger"
|
||||
for="input_4"
|
||||
>
|
||||
Role
|
||||
Peranan
|
||||
</label>
|
||||
<rs-button size="sm" @click="openModalRole"> Add Role </rs-button>
|
||||
<rs-button size="sm" @click="openModalRole"> Tambah Peranan </rs-button>
|
||||
</div>
|
||||
<v-select
|
||||
class="formkit-vselect"
|
||||
@ -456,7 +456,7 @@ function groupUserByRole() {
|
||||
<FormKit
|
||||
type="checkbox"
|
||||
v-model="checkAllRole"
|
||||
label="All Role"
|
||||
label="Semua Peranan"
|
||||
input-class="icon-check"
|
||||
/>
|
||||
<FormKit
|
||||
@ -470,9 +470,9 @@ function groupUserByRole() {
|
||||
|
||||
<!-- Modal Role -->
|
||||
<rs-modal
|
||||
title="Add Role"
|
||||
ok-title="Save"
|
||||
cancel-title="Back"
|
||||
title="Tambah Peranan"
|
||||
ok-title="Simpan"
|
||||
cancel-title="Kembali"
|
||||
:cancel-callback="closeModalRole"
|
||||
:ok-callback="saveRole"
|
||||
v-model="showModalRole"
|
||||
@ -480,27 +480,27 @@ function groupUserByRole() {
|
||||
<FormKit
|
||||
type="text"
|
||||
v-model="showModalRoleForm.role"
|
||||
label="Name"
|
||||
label="Nama"
|
||||
validation="required"
|
||||
validation-visibility="live"
|
||||
/>
|
||||
<FormKit
|
||||
type="textarea"
|
||||
v-model="showModalRoleForm.description"
|
||||
label="Description"
|
||||
label="Penerangan"
|
||||
/>
|
||||
</rs-modal>
|
||||
|
||||
<!-- Modal Delete Confirmation -->
|
||||
<rs-modal
|
||||
title="Delete Confirmation"
|
||||
ok-title="Yes"
|
||||
cancel-title="No"
|
||||
title="Pengesahan Padam"
|
||||
ok-title="Ya"
|
||||
cancel-title="Tidak"
|
||||
:ok-callback="deleteUser"
|
||||
v-model="showModalDelete"
|
||||
>
|
||||
<p>
|
||||
Are you sure want to delete this user ({{
|
||||
Adakah anda pasti mahu memadam pengguna ini ({{
|
||||
showModalDeleteForm.username
|
||||
}})?
|
||||
</p>
|
||||
|
233
pages/prototype/for-01/kemaskini-daftar/index.vue
Normal file
233
pages/prototype/for-01/kemaskini-daftar/index.vue
Normal file
@ -0,0 +1,233 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Kemaskini Daftar FR2",
|
||||
breadcrumb: [
|
||||
{
|
||||
name: "Kemaskini Daftar FR2",
|
||||
type: "current",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const { $swal } = useNuxtApp();
|
||||
|
||||
const caseId = ref("");
|
||||
const equipmentCondition = ref(false);
|
||||
const officerQualification = ref(false);
|
||||
const methodApplicability = ref(false);
|
||||
const externalSupport = ref(false);
|
||||
const taskAcceptance = ref("");
|
||||
const officerComments = ref("");
|
||||
const showForm = ref(false);
|
||||
|
||||
const verifyCase = async () => {
|
||||
if (!caseId.value) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "ID Kes diperlukan",
|
||||
text: "Sila masukkan ID Kes untuk menyemak.",
|
||||
timer: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
$swal.fire({
|
||||
icon: "success",
|
||||
title: "ID Kes disahkan",
|
||||
text: "Maklumat kes telah berjaya diambil.",
|
||||
timer: 3000,
|
||||
});
|
||||
showForm.value = true;
|
||||
} catch (error) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal mengesahkan ID Kes",
|
||||
text: "Sila cuba lagi atau hubungi pentadbir sistem.",
|
||||
timer: 3000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const updateForm = async () => {
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
$swal.fire({
|
||||
icon: "success",
|
||||
title: "Rekod telah berjaya dikemas kini",
|
||||
text: "Maklumat telah dikemas kini dalam sistem.",
|
||||
timer: 3000,
|
||||
});
|
||||
} catch (error) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal mengemas kini rekod",
|
||||
text: "Sila cuba lagi atau hubungi pentadbir sistem.",
|
||||
timer: 3000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const submitForm = async () => {
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
$swal.fire({
|
||||
icon: "success",
|
||||
title: "Rekod telah berjaya disahkan",
|
||||
text: "Maklumat telah disahkan dan disimpan dalam sistem.",
|
||||
timer: 3000,
|
||||
});
|
||||
router.push("/dashboard");
|
||||
} catch (error) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal mengesahkan rekod",
|
||||
text: "Sila cuba lagi atau hubungi pentadbir sistem.",
|
||||
timer: 3000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
if (
|
||||
!equipmentCondition.value ||
|
||||
!officerQualification.value ||
|
||||
!methodApplicability.value ||
|
||||
!taskAcceptance.value
|
||||
) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Borang tidak lengkap",
|
||||
text: "Sila lengkapkan semua maklumat yang diperlukan.",
|
||||
timer: 3000,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (taskAcceptance.value === "Tidak" && !officerComments.value) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Komen diperlukan",
|
||||
text: "Sila berikan komen kerana tugas tidak diterima.",
|
||||
timer: 3000,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="">
|
||||
<LayoutsBreadcrumb class="mb-6" />
|
||||
|
||||
<RsCard class="mb-6 shadow-lg">
|
||||
<template #header>
|
||||
<h2 class="text-2xl">Kemaskini Daftar FR2</h2>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="space-y-8">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium mb-4 text-gray-700">
|
||||
Pengesahan ID Kes
|
||||
</h3>
|
||||
<div class="flex flex-col gap-4">
|
||||
<FormKit
|
||||
type="text"
|
||||
name="caseId"
|
||||
label="ID Kes"
|
||||
validation="required"
|
||||
:validation-messages="{
|
||||
required: 'Sila masukkan ID Kes',
|
||||
}"
|
||||
v-model="caseId"
|
||||
class="flex-grow"
|
||||
:classes="{
|
||||
outer: 'mb-0',
|
||||
}"
|
||||
/>
|
||||
<RsButton @click="verifyCase" variant="primary" class="mb-1"
|
||||
>Semak</RsButton
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FormKit
|
||||
v-if="showForm"
|
||||
type="form"
|
||||
@submit="submitForm"
|
||||
:actions="false"
|
||||
>
|
||||
<div>
|
||||
<h3 class="text-lg font-medium mb-4 text-gray-700">Borang FR2</h3>
|
||||
<div class="space-y-4">
|
||||
<FormKit
|
||||
type="checkbox"
|
||||
name="equipmentCondition"
|
||||
label="Peralatan dalam keadaan baik untuk analisis/pemeriksaan"
|
||||
v-model="equipmentCondition"
|
||||
/>
|
||||
<FormKit
|
||||
type="checkbox"
|
||||
name="officerQualification"
|
||||
label="Pegawai berkelayakan untuk analisis/pemeriksaan"
|
||||
v-model="officerQualification"
|
||||
/>
|
||||
<FormKit
|
||||
type="checkbox"
|
||||
name="methodApplicability"
|
||||
label="Kaedah boleh dilaksanakan dan diterima untuk analisis"
|
||||
v-model="methodApplicability"
|
||||
/>
|
||||
<FormKit
|
||||
type="checkbox"
|
||||
name="externalSupport"
|
||||
label="Sokongan luar diperlukan"
|
||||
v-model="externalSupport"
|
||||
/>
|
||||
<FormKit
|
||||
type="select"
|
||||
name="taskAcceptance"
|
||||
label="Penerimaan Tugas"
|
||||
:options="[
|
||||
{ label: 'Ya', value: 'Ya' },
|
||||
{ label: 'Tidak', value: 'Tidak' },
|
||||
]"
|
||||
validation="required"
|
||||
:validation-messages="{
|
||||
required: 'Sila pilih penerimaan tugas',
|
||||
}"
|
||||
v-model="taskAcceptance"
|
||||
/>
|
||||
<FormKit
|
||||
type="textarea"
|
||||
name="officerComments"
|
||||
label="Komen Pegawai"
|
||||
v-model="officerComments"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex justify-end space-x-4">
|
||||
<RsButton @click="updateForm" variant="secondary"
|
||||
>Kemas Kini</RsButton
|
||||
>
|
||||
<!-- <RsButton type="submit" variant="primary">Sahkan</RsButton> -->
|
||||
</div>
|
||||
</FormKit>
|
||||
</div>
|
||||
</template>
|
||||
</RsCard>
|
||||
</div>
|
||||
</template>
|
334
pages/prototype/for-01/permohonan/index.vue
Normal file
334
pages/prototype/for-01/permohonan/index.vue
Normal file
@ -0,0 +1,334 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Permohonan Online",
|
||||
breadcrumb: [
|
||||
{
|
||||
name: "Permohonan Online",
|
||||
type: "current",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const { $swal } = useNuxtApp();
|
||||
|
||||
const applicantRank = ref("");
|
||||
const applicantName = ref("");
|
||||
const applicantOfficerNumber = ref("");
|
||||
const sameAsSender = ref(false);
|
||||
const showConfirmationModal = ref(false);
|
||||
const showDateTimeModal = ref(false);
|
||||
const availableTimeSlots = ref([]);
|
||||
const formData = ref({});
|
||||
|
||||
onMounted(() => {
|
||||
// Fetch applicant details from the system
|
||||
fetchApplicantDetails();
|
||||
});
|
||||
|
||||
const fetchApplicantDetails = async () => {
|
||||
try {
|
||||
// Simulated fetch of applicant details
|
||||
applicantRank.value = "Inspector";
|
||||
applicantName.value = "John Doe";
|
||||
applicantOfficerNumber.value = "123456";
|
||||
} catch (error) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal mendapatkan maklumat pemohon",
|
||||
text: "Sila cuba lagi.",
|
||||
timer: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSenderDetails = (value) => {
|
||||
sameAsSender.value = value;
|
||||
};
|
||||
|
||||
const submitForm = (formData) => {
|
||||
// Store form data for later use
|
||||
formData.value = formData;
|
||||
showConfirmationModal.value = true;
|
||||
};
|
||||
|
||||
const confirmSubmission = async () => {
|
||||
showConfirmationModal.value = false;
|
||||
try {
|
||||
// Simulated form submission
|
||||
$swal.fire({
|
||||
icon: "success",
|
||||
title: "Permohonan pemeriksaan forensik telah dihantar",
|
||||
timer: 5000,
|
||||
});
|
||||
showDateTimeModal.value = true;
|
||||
fetchAvailableTimeSlots();
|
||||
} catch (error) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal menghantar permohonan",
|
||||
text: "Sila cuba lagi.",
|
||||
timer: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const fetchAvailableTimeSlots = async () => {
|
||||
try {
|
||||
// Simulated fetch of available time slots
|
||||
availableTimeSlots.value = [
|
||||
{ label: "9:00 AM", value: "09:00" },
|
||||
{ label: "10:00 AM", value: "10:00" },
|
||||
{ label: "11:00 AM", value: "11:00" },
|
||||
];
|
||||
} catch (error) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal mendapatkan slot masa yang tersedia",
|
||||
text: "Sila cuba lagi.",
|
||||
timer: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const confirmDateTimeSlot = async () => {
|
||||
try {
|
||||
// Simulated confirmation of date and time slot
|
||||
const data = { caseId: "FOR-2023-001" };
|
||||
$swal.fire({
|
||||
icon: "success",
|
||||
title: "Janji temu telah disahkan",
|
||||
text: `Nombor rujukan kes: ${data.caseId}`,
|
||||
timer: 5000,
|
||||
});
|
||||
showDateTimeModal.value = false;
|
||||
router.push("/dashboard"); // Redirect to dashboard or confirmation page
|
||||
} catch (error) {
|
||||
$swal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal mengesahkan janji temu",
|
||||
text: "Sila cuba lagi.",
|
||||
timer: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<LayoutsBreadcrumb class="mb-6" />
|
||||
|
||||
<FormKit type="form" @submit="submitForm" :actions="false">
|
||||
<!-- Butir Permohonan Card -->
|
||||
<RsCard class="mb-6">
|
||||
<template #header>
|
||||
<h2 class="text-2xl font-semibold">Butir Permohonan</h2>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium mb-3">Butir-butir Pemohon</h3>
|
||||
<div class="bg-gray-100 p-4 rounded-lg">
|
||||
<p>
|
||||
<span class="font-medium">Nama:</span> {{ applicantName }}
|
||||
</p>
|
||||
<p>
|
||||
<span class="font-medium">Pangkat:</span> {{ applicantRank }}
|
||||
</p>
|
||||
<p>
|
||||
<span class="font-medium">Nombor Pegawai:</span>
|
||||
{{ applicantOfficerNumber }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-lg font-medium mb-3">
|
||||
Butir-butir Penghantar Barang Kes
|
||||
</h3>
|
||||
<FormKit
|
||||
type="checkbox"
|
||||
name="sameAsSender"
|
||||
label="Penghantar Barang Kes Sama Dengan Pemohon"
|
||||
v-model="sameAsSender"
|
||||
/>
|
||||
|
||||
<div v-if="!sameAsSender" class="mt-4 space-y-4">
|
||||
<FormKit
|
||||
type="text"
|
||||
name="senderName"
|
||||
label="Nama Penghantar Barang Kes"
|
||||
validation="required"
|
||||
:validation-messages="{
|
||||
required: 'Sila masukkan nama penghantar barang kes',
|
||||
}"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
name="senderRank"
|
||||
label="Pangkat Penghantar Barang Kes"
|
||||
validation="required"
|
||||
:validation-messages="{
|
||||
required: 'Sila masukkan pangkat penghantar barang kes',
|
||||
}"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
name="senderOfficerNumber"
|
||||
label="Nombor Pegawai Penghantar Barang Kes"
|
||||
validation="required|number|length:6"
|
||||
:validation-messages="{
|
||||
required:
|
||||
'Sila masukkan nombor pegawai penghantar barang kes',
|
||||
number: 'Nombor pegawai mesti dalam bentuk nombor',
|
||||
length: 'Nombor pegawai mesti mempunyai 6 digit',
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</RsCard>
|
||||
|
||||
<!-- Maklumat Kes Card -->
|
||||
<RsCard class="mb-6">
|
||||
<template #header>
|
||||
<h2 class="text-2xl font-semibold">Maklumat Kes</h2>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium mb-3">Maklumat Barang Kes</h3>
|
||||
<div class="space-y-4">
|
||||
<FormKit
|
||||
type="select"
|
||||
name="jenisBarang"
|
||||
label="Jenis Barang"
|
||||
:options="[
|
||||
{ label: 'Pasport', value: 'pasport' },
|
||||
{ label: 'Malpass', value: 'malpass' },
|
||||
{ label: 'Cap Keselamatan', value: 'capKeselamatan' },
|
||||
{ label: 'Cap Jari', value: 'capJari' },
|
||||
{ label: 'Pemeriksaan Siber', value: 'pemeriksaanSiber' },
|
||||
{ label: 'Tulisan Tangan', value: 'tulisanTangan' },
|
||||
{ label: 'I-Kad', value: 'iKad' },
|
||||
{ label: 'Lain-lain', value: 'lainLain' },
|
||||
]"
|
||||
validation="required"
|
||||
:validation-messages="{
|
||||
required: 'Sila pilih jenis barang',
|
||||
}"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
name="tandaBarang"
|
||||
label="Tanda Barang"
|
||||
validation="required"
|
||||
:validation-messages="{
|
||||
required: 'Sila masukkan tanda barang',
|
||||
}"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
name="keadaanBarang"
|
||||
label="Keadaan Barang"
|
||||
validation="required"
|
||||
:validation-messages="{
|
||||
required: 'Sila masukkan keadaan barang',
|
||||
}"
|
||||
/>
|
||||
<FormKit
|
||||
type="number"
|
||||
name="kuantitiBarang"
|
||||
label="Kuantiti Barang"
|
||||
validation="required|number|min:1"
|
||||
:validation-messages="{
|
||||
required: 'Sila masukkan kuantiti barang',
|
||||
number: 'Kuantiti mesti dalam bentuk nombor',
|
||||
min: 'Kuantiti mesti sekurang-kurangnya 1',
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-lg font-medium mb-3">Maklumat Tambahan</h3>
|
||||
<div class="space-y-4">
|
||||
<FormKit
|
||||
type="textarea"
|
||||
name="ringkasanKes"
|
||||
label="Ringkasan Kenyataan Kes"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
name="nomorKertasSiasatan"
|
||||
label="Nombor Kertas Siasatan"
|
||||
/>
|
||||
<FormKit
|
||||
type="text"
|
||||
name="nomorLaporanPolis"
|
||||
label="Nombor Laporan Polis"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</RsCard>
|
||||
|
||||
<div class="text-center">
|
||||
<FormKit
|
||||
type="submit"
|
||||
label="Hantar Permohonan"
|
||||
input-class="text-white font-bold py-2 px-4 rounded w-full"
|
||||
/>
|
||||
</div>
|
||||
</FormKit>
|
||||
|
||||
<!-- Confirmation Modal -->
|
||||
<RsModal v-model="showConfirmationModal" title="Pengesahan Permohonan">
|
||||
<template #body>
|
||||
<p>Adakah anda pasti untuk menghantar permohonan ini?</p>
|
||||
</template>
|
||||
<template #footer>
|
||||
<RsButton @click="showConfirmationModal = false" variant="secondary"
|
||||
>Kembali</RsButton
|
||||
>
|
||||
<RsButton @click="confirmSubmission" variant="primary">Sahkan</RsButton>
|
||||
</template>
|
||||
</RsModal>
|
||||
|
||||
<!-- Date and Time Slot Selection Modal -->
|
||||
<RsModal v-model="showDateTimeModal" title="Pilih Tarikh dan Slot Masa">
|
||||
<template #body>
|
||||
<FormKit
|
||||
type="date"
|
||||
name="appointmentDate"
|
||||
label="Tarikh Janji Temu"
|
||||
validation="required|after:today"
|
||||
:validation-messages="{
|
||||
required: 'Sila pilih tarikh janji temu',
|
||||
after: 'Tarikh janji temu mestilah selepas tarikh hari ini',
|
||||
}"
|
||||
/>
|
||||
<FormKit
|
||||
type="select"
|
||||
name="timeSlot"
|
||||
label="Slot Masa"
|
||||
:options="availableTimeSlots"
|
||||
validation="required"
|
||||
:validation-messages="{
|
||||
required: 'Sila pilih slot masa',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<RsButton @click="showDateTimeModal = false" variant="secondary"
|
||||
>Kembali</RsButton
|
||||
>
|
||||
<RsButton @click="confirmDateTimeSlot" variant="primary"
|
||||
>Sahkan</RsButton
|
||||
>
|
||||
</template>
|
||||
</RsModal>
|
||||
</div>
|
||||
</template>
|
193
pages/senarai-mesej/index.vue
Normal file
193
pages/senarai-mesej/index.vue
Normal file
@ -0,0 +1,193 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Senarai Mesej",
|
||||
breadcrumb: [
|
||||
{
|
||||
name: "Senarai Mesej",
|
||||
type: "current",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const messages = ref([
|
||||
{
|
||||
id: 1,
|
||||
sender: "Admin",
|
||||
subject: "Selamat Datang",
|
||||
preview: "Selamat datang ke sistem mesej kami...",
|
||||
content:
|
||||
"Selamat datang ke sistem mesej kami. Kami berharap anda akan menikmati pengalaman menggunakan sistem ini. Jika ada sebarang pertanyaan, sila hubungi kami.",
|
||||
date: "1-50 daripada 528",
|
||||
isRead: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
sender: "Spotify",
|
||||
subject: "Tawaran Istimewa untuk Anda",
|
||||
preview: "Dapatkan langganan premium dengan harga istimewa...",
|
||||
content:
|
||||
"Dapatkan langganan premium dengan harga istimewa! Hanya untuk masa yang terhad, nikmati muzik tanpa had dengan harga yang sangat berpatutan.",
|
||||
date: "11:59 malam",
|
||||
isRead: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
sender: "Ahmad Zulkifli",
|
||||
subject: "Mesyuarat Projek Baru",
|
||||
preview: "Sila hadir ke mesyuarat projek baru pada...",
|
||||
content:
|
||||
"Sila hadir ke mesyuarat projek baru pada hari Isnin, 10 Julai 2023, jam 10 pagi di Bilik Mesyuarat Utama. Kita akan membincangkan perancangan dan pembahagian tugas untuk projek baru ini.",
|
||||
date: "10:21 malam",
|
||||
isRead: false,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
sender: "Syarikat ABC",
|
||||
subject: "Tawaran Kerja",
|
||||
preview: "Kami dengan sukacitanya menawarkan anda jawatan...",
|
||||
content:
|
||||
"Kami dengan sukacitanya menawarkan anda jawatan Pengaturcara Kanan di Syarikat ABC. Sila baca lampiran untuk maklumat lanjut tentang tawaran ini dan hubungi kami jika anda mempunyai sebarang pertanyaan.",
|
||||
date: "8:37 malam",
|
||||
isRead: false,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
sender: "Puan Mariam",
|
||||
subject: "Peringatan: Bayaran Bil",
|
||||
preview: "Ini adalah peringatan untuk pembayaran bil anda...",
|
||||
content:
|
||||
"Ini adalah peringatan untuk pembayaran bil anda yang telah tamat tempoh. Sila jelaskan bayaran sebelum 30 Jun 2023 untuk mengelakkan sebarang caj lewat.",
|
||||
date: "4:38 petang",
|
||||
isRead: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const selectedMessages = ref([]);
|
||||
const currentMessage = ref(null);
|
||||
|
||||
const selectMessage = (messageId) => {
|
||||
const index = selectedMessages.value.indexOf(messageId);
|
||||
if (index === -1) {
|
||||
selectedMessages.value.push(messageId);
|
||||
} else {
|
||||
selectedMessages.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteSelectedMessages = () => {
|
||||
messages.value = messages.value.filter(
|
||||
(message) => !selectedMessages.value.includes(message.id)
|
||||
);
|
||||
selectedMessages.value = [];
|
||||
currentMessage.value = null;
|
||||
};
|
||||
|
||||
const readMessage = (message) => {
|
||||
currentMessage.value = message;
|
||||
if (!message.isRead) {
|
||||
message.isRead = true;
|
||||
}
|
||||
};
|
||||
|
||||
const closeMessage = () => {
|
||||
currentMessage.value = null;
|
||||
};
|
||||
|
||||
const addNewMessage = () => {
|
||||
const newId = messages.value.length + 1;
|
||||
const newMessage = {
|
||||
id: newId,
|
||||
sender: "New Sender",
|
||||
subject: "New Subject",
|
||||
preview: "This is a new message...",
|
||||
content:
|
||||
"This is the full content of the new message. You can edit this to add more details.",
|
||||
date: new Date().toLocaleString(),
|
||||
isRead: false,
|
||||
};
|
||||
messages.value.unshift(newMessage);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-100 min-h-screen">
|
||||
<LayoutsBreadcrumb />
|
||||
|
||||
<div class="container mx-auto">
|
||||
<rs-card>
|
||||
<div class="flex items-center justify-between p-5">
|
||||
<h2 class="text-xl font-semibold text-gray-800">Senarai Mesej</h2>
|
||||
<div class="flex space-x-2">
|
||||
<rs-button @click="addNewMessage" variant="success">
|
||||
Tambah Mesej
|
||||
</rs-button>
|
||||
<rs-button
|
||||
@click="deleteSelectedMessages"
|
||||
variant="danger"
|
||||
:disabled="selectedMessages.length === 0"
|
||||
>
|
||||
Buang ({{ selectedMessages.length }})
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divide-y divide-gray-200">
|
||||
<div
|
||||
v-for="message in messages"
|
||||
:key="message.id"
|
||||
@click="readMessage(message)"
|
||||
class="flex items-center p-4 hover:bg-gray-50 transition duration-150 ease-in-out cursor-pointer"
|
||||
:class="{ 'bg-blue-50': selectedMessages.includes(message.id) }"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="selectedMessages.includes(message.id)"
|
||||
@click.stop="selectMessage(message.id)"
|
||||
class="mr-4 h-5 w-5 text-blue-600"
|
||||
/>
|
||||
<div class="flex-grow">
|
||||
<div class="flex items-center justify-between">
|
||||
<span
|
||||
class="font-medium text-gray-900"
|
||||
:class="{ 'font-bold': !message.isRead }"
|
||||
>
|
||||
{{ message.sender }}
|
||||
</span>
|
||||
<span class="text-sm text-gray-500">{{ message.date }}</span>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<span
|
||||
class="text-sm font-medium text-gray-900"
|
||||
:class="{ 'font-bold': !message.isRead }"
|
||||
>
|
||||
{{ message.subject }}
|
||||
</span>
|
||||
<span class="text-sm text-gray-600">
|
||||
- {{ message.preview }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</rs-card>
|
||||
|
||||
<!-- Message Content Modal -->
|
||||
<rs-modal v-model="currentMessage" title="Message Details">
|
||||
<template v-if="currentMessage">
|
||||
<p class="text-sm text-gray-600 mb-2">
|
||||
From: {{ currentMessage.sender }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-600 mb-4">
|
||||
Date: {{ currentMessage.date }}
|
||||
</p>
|
||||
<div class="border-t pt-4">
|
||||
<p class="text-gray-800">{{ currentMessage.content }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</rs-modal>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Add any additional styles here */
|
||||
</style>
|
284
pages/tipografi/index.vue
Normal file
284
pages/tipografi/index.vue
Normal file
@ -0,0 +1,284 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Tipografi",
|
||||
breadcrumb: [
|
||||
{
|
||||
name: "Tipografi",
|
||||
type: "current",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const showCode1 = ref(false);
|
||||
const showCode2 = ref(false);
|
||||
const showCode3 = ref(false);
|
||||
const showCode4 = ref(false);
|
||||
const tooltips = ref({});
|
||||
|
||||
const copyCode = (codeId) => {
|
||||
const codeElement = document.getElementById(codeId);
|
||||
if (codeElement) {
|
||||
const codeText = codeElement.textContent;
|
||||
navigator.clipboard.writeText(codeText).then(() => {
|
||||
tooltips.value[codeId] = "Kod disalin!";
|
||||
setTimeout(() => {
|
||||
tooltips.value[codeId] = "";
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<LayoutsBreadcrumb />
|
||||
|
||||
<rs-card>
|
||||
<template #header> Tipografi </template>
|
||||
<template #body>
|
||||
<p class="mb-4">
|
||||
Dokumentasi tipografi untuk pembangun untuk melihat dan menggunakan.
|
||||
</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- Tajuk -->
|
||||
<rs-card>
|
||||
<template #header> Tajuk </template>
|
||||
<template #body>
|
||||
<div class="space-y-2">
|
||||
<h1 class="text-4xl font-bold">Tajuk 1</h1>
|
||||
<h2 class="text-3xl font-bold">Tajuk 2</h2>
|
||||
<h3 class="text-2xl font-bold">Tajuk 3</h3>
|
||||
<h4 class="text-xl font-bold">Tajuk 4</h4>
|
||||
<h5 class="text-lg font-bold">Tajuk 5</h5>
|
||||
<h6 class="text-base font-bold">Tajuk 6</h6>
|
||||
</div>
|
||||
<div class="flex justify-end mt-2">
|
||||
<button
|
||||
class="text-sm border border-slate-200 py-1 px-3 rounded-lg"
|
||||
@click="showCode1 ? (showCode1 = false) : (showCode1 = true)"
|
||||
>
|
||||
Tunjukkan Kod
|
||||
</button>
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<transition name="fade">
|
||||
<div v-show="showCode1" v-highlight>
|
||||
<div class="relative">
|
||||
<button
|
||||
@click="copyCode('code1')"
|
||||
class="absolute top-4 right-2 text-sm bg-gray-300 hover:bg-gray-300 py-1 px-3 rounded z-10"
|
||||
>
|
||||
Salin Kod
|
||||
</button>
|
||||
<transition name="tooltip">
|
||||
<span
|
||||
v-if="tooltips['code1']"
|
||||
class="absolute top-0 right-0 mt-12 mr-2 bg-black text-white text-xs rounded py-1 px-2 z-10"
|
||||
>
|
||||
{{ tooltips["code1"] }}
|
||||
</span>
|
||||
</transition>
|
||||
<pre id="code1" class="language-html">
|
||||
<code>
|
||||
<h1 class="text-4xl font-bold">Tajuk 1</h1>
|
||||
<h2 class="text-3xl font-bold">Tajuk 2</h2>
|
||||
<h3 class="text-2xl font-bold">Tajuk 3</h3>
|
||||
<h4 class="text-xl font-bold">Tajuk 4</h4>
|
||||
<h5 class="text-lg font-bold">Tajuk 5</h5>
|
||||
<h6 class="text-base font-bold">Tajuk 6</h6>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- Perenggan -->
|
||||
<rs-card>
|
||||
<template #header> Perenggan </template>
|
||||
<template #body>
|
||||
<p class="mb-2">
|
||||
Ini adalah perenggan lalai. Ia menggunakan saiz fon asas dan
|
||||
ketinggian baris.
|
||||
</p>
|
||||
<p class="text-sm mb-2">
|
||||
Ini adalah perenggan kecil menggunakan kelas text-sm.
|
||||
</p>
|
||||
<p class="text-lg mb-2">
|
||||
Ini adalah perenggan besar menggunakan kelas text-lg.
|
||||
</p>
|
||||
<p class="font-light mb-2">Ini adalah perenggan berat ringan.</p>
|
||||
<p class="font-semibold mb-2">Ini adalah perenggan separa tebal.</p>
|
||||
<div class="flex justify-end mt-2">
|
||||
<button
|
||||
class="text-sm border border-slate-200 py-1 px-3 rounded-lg"
|
||||
@click="showCode2 ? (showCode2 = false) : (showCode2 = true)"
|
||||
>
|
||||
Tunjukkan Kod
|
||||
</button>
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<transition name="fade">
|
||||
<div v-show="showCode2" v-highlight>
|
||||
<div class="relative">
|
||||
<button
|
||||
@click="copyCode('code2')"
|
||||
class="absolute top-4 right-2 text-sm bg-gray-300 hover:bg-gray-300 py-1 px-3 rounded z-10"
|
||||
>
|
||||
Salin Kod
|
||||
</button>
|
||||
<transition name="tooltip">
|
||||
<span
|
||||
v-if="tooltips['code2']"
|
||||
class="absolute top-0 right-0 mt-12 mr-2 bg-black text-white text-xs rounded py-1 px-2 z-10"
|
||||
>
|
||||
{{ tooltips["code2"] }}
|
||||
</span>
|
||||
</transition>
|
||||
<pre id="code2" class="language-html">
|
||||
<code>
|
||||
<p>Ini adalah perenggan lalai. Ia menggunakan saiz fon asas dan ketinggian baris.</p>
|
||||
<p class="text-sm">Ini adalah perenggan kecil menggunakan kelas text-sm.</p>
|
||||
<p class="text-lg">Ini adalah perenggan besar menggunakan kelas text-lg.</p>
|
||||
<p class="font-light">Ini adalah perenggan berat ringan.</p>
|
||||
<p class="font-semibold">Ini adalah perenggan separa tebal.</p>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- Warna Teks -->
|
||||
<rs-card>
|
||||
<template #header> Warna Teks </template>
|
||||
<template #body>
|
||||
<p class="text-primary mb-2">Ini adalah teks warna utama.</p>
|
||||
<p class="text-secondary mb-2">Ini adalah teks warna sekunder.</p>
|
||||
<p class="text-gray-500 mb-2">Ini adalah teks warna kelabu.</p>
|
||||
<p class="text-red-500 mb-2">Ini adalah teks warna merah.</p>
|
||||
<p class="text-green-500 mb-2">Ini adalah teks warna hijau.</p>
|
||||
<div class="flex justify-end mt-2">
|
||||
<button
|
||||
class="text-sm border border-slate-200 py-1 px-3 rounded-lg"
|
||||
@click="showCode3 ? (showCode3 = false) : (showCode3 = true)"
|
||||
>
|
||||
Tunjukkan Kod
|
||||
</button>
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<transition name="fade">
|
||||
<div v-show="showCode3" v-highlight>
|
||||
<div class="relative">
|
||||
<button
|
||||
@click="copyCode('code3')"
|
||||
class="absolute top-4 right-2 text-sm bg-gray-300 hover:bg-gray-300 py-1 px-3 rounded z-10"
|
||||
>
|
||||
Salin Kod
|
||||
</button>
|
||||
<transition name="tooltip">
|
||||
<span
|
||||
v-if="tooltips['code3']"
|
||||
class="absolute top-0 right-0 mt-12 mr-2 bg-black text-white text-xs rounded py-1 px-2 z-10"
|
||||
>
|
||||
{{ tooltips["code3"] }}
|
||||
</span>
|
||||
</transition>
|
||||
<pre id="code3" class="language-html">
|
||||
<code>
|
||||
<p class="text-primary">Ini adalah teks warna utama.</p>
|
||||
<p class="text-secondary">Ini adalah teks warna sekunder.</p>
|
||||
<p class="text-gray-500">Ini adalah teks warna kelabu.</p>
|
||||
<p class="text-red-500">Ini adalah teks warna merah.</p>
|
||||
<p class="text-green-500">Ini adalah teks warna hijau.</p>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- Gaya Fon -->
|
||||
<rs-card>
|
||||
<template #header> Gaya Fon </template>
|
||||
<template #body>
|
||||
<p class="italic mb-2">Ini adalah teks condong.</p>
|
||||
<p class="font-bold mb-2">Ini adalah teks tebal.</p>
|
||||
<p class="underline mb-2">Ini adalah teks bergaris.</p>
|
||||
<p class="line-through mb-2">Ini adalah teks dicoret.</p>
|
||||
<p class="uppercase mb-2">Ini adalah teks huruf besar.</p>
|
||||
<p class="lowercase mb-2">Ini adalah teks huruf kecil.</p>
|
||||
<p class="capitalize mb-2">Ini adalah teks huruf awal besar.</p>
|
||||
<div class="flex justify-end mt-2">
|
||||
<button
|
||||
class="text-sm border border-slate-200 py-1 px-3 rounded-lg"
|
||||
@click="showCode4 ? (showCode4 = false) : (showCode4 = true)"
|
||||
>
|
||||
Tunjukkan Kod
|
||||
</button>
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<transition name="fade">
|
||||
<div v-show="showCode4" v-highlight>
|
||||
<div class="relative">
|
||||
<button
|
||||
@click="copyCode('code4')"
|
||||
class="absolute top-4 right-2 text-sm bg-gray-300 hover:bg-gray-300 py-1 px-3 rounded z-10"
|
||||
>
|
||||
Salin Kod
|
||||
</button>
|
||||
<transition name="tooltip">
|
||||
<span
|
||||
v-if="tooltips['code4']"
|
||||
class="absolute top-0 right-0 mt-12 mr-2 bg-black text-white text-xs rounded py-1 px-2 z-10"
|
||||
>
|
||||
{{ tooltips["code4"] }}
|
||||
</span>
|
||||
</transition>
|
||||
<pre id="code4" class="language-html">
|
||||
<code>
|
||||
<p class="italic">Ini adalah teks condong.</p>
|
||||
<p class="font-bold">Ini adalah teks tebal.</p>
|
||||
<p class="underline">Ini adalah teks bergaris.</p>
|
||||
<p class="line-through">Ini adalah teks dicoret.</p>
|
||||
<p class="uppercase">Ini adalah teks huruf besar.</p>
|
||||
<p class="lowercase">Ini adalah teks huruf kecil.</p>
|
||||
<p class="capitalize">Ini adalah teks huruf awal besar.</p>
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tooltip-enter-active,
|
||||
.tooltip-leave-active {
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
.tooltip-enter,
|
||||
.tooltip-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.8 KiB |
Loading…
x
Reference in New Issue
Block a user