fix(filters): change code structure and data flow in filter type 1

This commit is contained in:
kur0nek-o 2024-02-09 14:24:09 +07:00
parent 62cbcc9c28
commit cb8e11b1e9
12 changed files with 838 additions and 560 deletions

6
package-lock.json generated
View File

@ -9257,9 +9257,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "4.5.0", "version": "4.5.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
"integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
"dependencies": { "dependencies": {
"esbuild": "^0.18.10", "esbuild": "^0.18.10",
"postcss": "^8.4.27", "postcss": "^8.4.27",

View File

@ -1502,6 +1502,10 @@ body {
gap: 0.25rem; gap: 0.25rem;
} }
.gap-2 {
gap: 0.5rem;
}
.gap-3 { .gap-3 {
gap: 0.75rem; gap: 0.75rem;
} }
@ -1719,6 +1723,11 @@ body {
background-color: rgb(0 0 0 / var(--tw-bg-opacity)); background-color: rgb(0 0 0 / var(--tw-bg-opacity));
} }
.bg-blue-50 {
--tw-bg-opacity: 1;
background-color: rgb(239 246 255 / var(--tw-bg-opacity));
}
.bg-gray-100 { .bg-gray-100 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity)); background-color: rgb(243 244 246 / var(--tw-bg-opacity));
@ -1894,6 +1903,14 @@ body {
object-fit: contain; object-fit: contain;
} }
.p-0 {
padding: 0px;
}
.p-0\.5 {
padding: 0.125rem;
}
.p-1 { .p-1 {
padding: 0.25rem; padding: 0.25rem;
} }
@ -2191,6 +2208,11 @@ body {
color: rgb(96 165 250 / var(--tw-text-opacity)); color: rgb(96 165 250 / var(--tw-text-opacity));
} }
.text-blue-700 {
--tw-text-opacity: 1;
color: rgb(29 78 216 / var(--tw-text-opacity));
}
.text-dark { .text-dark {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(21 22 23 / var(--tw-text-opacity)); color: rgb(21 22 23 / var(--tw-text-opacity));
@ -2433,6 +2455,10 @@ body {
--tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity)); --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity));
} }
.ring-blue-700\/10 {
--tw-ring-color: rgb(29 78 216 / 0.1);
}
.ring-gray-300 { .ring-gray-300 {
--tw-ring-opacity: 1; --tw-ring-opacity: 1;
--tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity));

View File

@ -6,10 +6,6 @@ const props = defineProps({
type: String as () => 'button' | 'submit' | 'reset', type: String as () => 'button' | 'submit' | 'reset',
default: 'button' default: 'button'
}, },
onClick: {
type: Function as unknown as () => (payload: MouseEvent) => void,
default: () => {}
},
label: { label: {
type: String, type: String,
default: '' default: ''
@ -47,12 +43,14 @@ const buttonStyle = computed(() => {
return 'bg-none text-primary-500 border border-transparent hover:border-primary-500 rounded-lg disabled:border-primary-300 disabled:text-primary-300' return 'bg-none text-primary-500 border border-transparent hover:border-primary-500 rounded-lg disabled:border-primary-300 disabled:text-primary-300'
} }
}) })
const emit = defineEmits(['on:click'])
</script> </script>
<template> <template>
<button <button
:type="type" :type="type"
@click="onClick" @click="emit('on:click')"
:disabled="isLoading ? true : disabled" :disabled="isLoading ? true : disabled"
:class="['px-3 py-2 text-sm font-semibold', buttonStyle, className]" :class="['px-3 py-2 text-sm font-semibold', buttonStyle, className]"
> >

View File

@ -1,27 +1,41 @@
<script setup lang="ts"> <script setup lang="ts">
import { useDateStore } from '@/stores/date'; // import { useDateStore } from '@/stores/date';
import { PhCalendarBlank } from '@phosphor-icons/vue'; import { PhCalendarBlank } from '@phosphor-icons/vue'
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import VueTailwindDatepicker from 'vue-tailwind-datepicker' import VueTailwindDatepicker from 'vue-tailwind-datepicker'
const dateValue = ref('') const dateValue = ref('')
const formatter = ref({ const formatter = ref({
date: 'DD-MM-YYYY', date: 'DD-MM-YYYY',
month: 'MMMM' month: 'MMMM'
}) })
watch(dateValue, (newValue) => {
useDateStore().setDateValue(newValue);
const emit = defineEmits(['update:dateValue'])
watch(dateValue, (newValue) => {
emit('update:dateValue', newValue)
}) })
</script> </script>
<template> <template>
<div class="flex"> <div class="flex">
<vue-tailwind-datepicker v-model="dateValue" :formatter="formatter" separator=" s/d " :shortcuts="false" <vue-tailwind-datepicker
:auto-apply="false" as-single use-range v-slot="{ value, placeholder }"> v-model="dateValue"
:formatter="formatter"
separator=" s/d "
:shortcuts="false"
:auto-apply="false"
as-single
use-range
v-slot="{ value, placeholder }"
>
<div class="flex"> <div class="flex">
<div class="flex-1"> <div class="flex-1">
<button type="button" <button
class="w-full flex items-center justify-between px-4 py-2 text-sm leading-6 placeholder:text-gray-400 text-gray-900 border-0 border-transparent rounded-lg outline-0 bg-gray-200 focus:outline-0 focus:border-0 focus:ring-0"> type="button"
class="w-full flex items-center justify-between px-4 py-2 text-sm leading-6 placeholder:text-gray-400 text-gray-900 border-0 border-transparent rounded-lg outline-0 bg-gray-200 focus:outline-0 focus:border-0 focus:ring-0"
>
<span class="text-gray-900"> <span class="text-gray-900">
{{ value || placeholder }} {{ value || placeholder }}
</span> </span>

View File

@ -1,44 +1,51 @@
<script setup lang="ts"> <script setup lang="ts">
// components // components
import Button from '@/components/Button.vue'; import Button from '@/components/Button.vue'
import { useSearchStore } from '@/stores/filtersAction'; import { useSearchStore } from '@/stores/filtersAction'
// icons
import { // icons
PhArrowsCounterClockwise, import { PhArrowsCounterClockwise, PhFileText, PhMagnifyingGlass } from '@phosphor-icons/vue'
PhFileText, const cariButton = useSearchStore()
PhMagnifyingGlass defineProps({
} from '@phosphor-icons/vue'; reportButton: {
const cariButton = useSearchStore() type: Boolean,
defineProps({ default: false
reportButton: { }
type: Boolean, })
default: false
} const emit = defineEmits(['runSearch'])
})
</script> </script>
<template> <template>
<div class="filters rounded-2xl"> <div class="filters rounded-2xl">
<form class="filter-body bg-gray-50 mx-auto space-y-3 p-4 rounded-t-2xl"> <form class="filter-body bg-gray-50 mx-auto space-y-3 p-4 rounded-t-2xl">
<slot></slot> <slot></slot>
</form> </form>
<div class="filter-footer rounded-b-2xl px-4 py-3 bg-primary-50 flex justify-end"> <div class="filter-footer rounded-b-2xl px-4 py-3 bg-primary-50 flex justify-end">
<div class="filter-buttons flex gap-3 flex-wrap"> <div class="filter-buttons flex gap-3 flex-wrap">
<Button @click="()=>cariButton.isTriggerChange = !cariButton.isTriggerChange" label="Ulangi" style-type="outline" class-name="bg-white"> <Button
@click="() => (cariButton.isTriggerChange = !cariButton.isTriggerChange)"
label="Ulangi"
style-type="outline"
class-name="bg-white"
>
<PhArrowsCounterClockwise size="18" class="ml-1" weight="regular" /> <PhArrowsCounterClockwise size="18" class="ml-1" weight="regular" />
</Button> </Button>
<Button v-if="reportButton" label="Lihat Laporan" style-type="outline" class-name="bg-white"> <Button
v-if="reportButton"
label="Lihat Laporan"
style-type="outline"
class-name="bg-white"
>
<PhFileText size="18" class="ml-1" weight="regular" /> <PhFileText size="18" class="ml-1" weight="regular" />
</Button> </Button>
<Button @click="()=>cariButton.isTriggerChange = !cariButton.isTriggerChange" label="Cari Data"> <Button @on:click="() => emit('runSearch')" label="Cari Data">
<PhMagnifyingGlass size="18" class="ml-1" weight="regular" /> <PhMagnifyingGlass size="18" class="ml-1" weight="regular" />
</Button> </Button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,58 +1,109 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import {
selectedUid,
selectedUp3Posko,
selectedPosko,
fetchData,
items,
itemsUp3,
itemsPosko
} from './reference'
import Select from '@/components/Select.vue' import Select from '@/components/Select.vue'
import DatePicker from '@/components/DatePicker.vue' import DatePicker from '@/components/DatePicker.vue'
import { selectedUid, selectedUp3Posko, selectedPosko ,fetchData, items,itemsUp3, itemsPosko} from './reference'; // fetchData();
import { ref } from 'vue'; const uidPlaceholder = 'Semua Unit Induk Distribusi/Wilayah'
fetchData(); const uppPlaceholder = 'Semua Unit Pelaksanaan Pelayanan Pelanggan'
const uidPlaceholder = 'Semua Unit Induk Distribusi/Wilayah'; const poskoPlaceholder = 'Semua Posko'
const uppPlaceholder = 'Semua Unit Pelaksanaan Pelayanan Pelanggan'; const uppp = ref({ id: 0, name: uppPlaceholder })
const poskoPlaceholder = 'Semua Posko'; const uid = ref({ id: 0, name: uidPlaceholder })
const uppp = ref({ id: 0, name: uppPlaceholder }); const posko = ref({ id: 0, name: poskoPlaceholder })
const uid = ref({ id: 0, name:uidPlaceholder });
const posko = ref({ id: 0, name: poskoPlaceholder }); const emit = defineEmits(['update:filters'])
const data = ref({
uid: uid.value,
up3: uppp.value,
posko: posko.value,
periode: ''
})
watch(data.value, (value) => {
emit('update:filters', value)
})
const setUid = (value: any) => { const setUid = (value: any) => {
uid.value = value; uid.value = value
selectedUid(value); selectedUid(value)
uppp.value = { id: 0, name: uppPlaceholder }; uppp.value = { id: 0, name: uppPlaceholder }
}; data.value.uid = value
}
const setUp3 = (value: any) => { const setUp3 = (value: any) => {
uppp.value = value; uppp.value = value
selectedUp3Posko(value); selectedUp3Posko(value)
posko.value = { id: 0, name: poskoPlaceholder }; posko.value = { id: 0, name: poskoPlaceholder }
}; data.value.up3 = value
}
const setPosko = (value: any) => { const setPosko = (value: any) => {
posko.value = value; posko.value = value
selectedPosko(value); selectedPosko(value)
}; data.value.posko = value
}
onMounted(() => {
emit('update:filters', data.value)
})
</script> </script>
<template> <template>
<div class="sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:items-center"> <div class="sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:items-center">
<label class="text-gray-800 font-semibold mb-2 sm:mb-0 block">Unit Induk Distribusi/Wilayah:</label> <label class="text-gray-800 font-semibold mb-2 sm:mb-0 block"
>Unit Induk Distribusi/Wilayah:</label
>
<Select @update:selected="setUid($event)" :data="items" <Select
:placeholder="uidPlaceholder" /> :data="items"
@update:selected="setUid($event)"
:placeholder="uidPlaceholder"
:selected="uid"
/>
</div> </div>
<div class="sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:items-center"> <div class="sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:items-center">
<label class="text-gray-800 font-semibold mb-2 sm:mb-0 block">Unit Pelaksanaan Pelayanan Pelanggan:</label> <label class="text-gray-800 font-semibold mb-2 sm:mb-0 block"
>Unit Pelaksanaan Pelayanan Pelanggan:</label
>
<Select @update:selected="setUp3($event)" :data="itemsUp3" :selected="uppp" <Select
:placeholder="uppPlaceholder" /> @update:selected="setUp3($event)"
:data="itemsUp3"
:selected="uppp"
:placeholder="uppPlaceholder"
/>
</div> </div>
<div class="sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:items-center"> <div class="sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:items-center">
<label class="text-gray-800 font-semibold mb-2 sm:mb-0 block">Posko:</label> <label class="text-gray-800 font-semibold mb-2 sm:mb-0 block">Posko:</label>
<Select @update:selected="setPosko($event)" <Select
:data="itemsPosko" :selected="posko" :placeholder="poskoPlaceholder" /> @update:selected="setPosko($event)"
:data="itemsPosko"
:selected="posko"
:placeholder="poskoPlaceholder"
/>
</div> </div>
<div class="sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:items-center"> <div class="sm:grid sm:grid-cols-2 lg:grid-cols-3 sm:items-center">
<label class="text-gray-800 font-semibold mb-2 sm:mb-0 block">Periode Tanggal:</label> <label class="text-gray-800 font-semibold mb-2 sm:mb-0 block">Periode Tanggal:</label>
<DatePicker /> <DatePicker
@update:date-value="
(value) => {
data.periode = value
}
"
/>
</div> </div>
</template> </template>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <!-- <script setup lang="ts">
import Select from '@/components/Select.vue' import Select from '@/components/Select.vue'
import SelectMulti from '@/components/SelectMulti.vue' import SelectMulti from '@/components/SelectMulti.vue'
@ -44,4 +44,6 @@ const departments = [
<label class="text-gray-800 font-semibold mb-2 sm:mb-0 block">Periode Tanggal:</label> <label class="text-gray-800 font-semibold mb-2 sm:mb-0 block">Periode Tanggal:</label>
<DatePicker /> <DatePicker />
</div> </div>
</template> </template> -->
<template></template>

View File

@ -1,4 +1,4 @@
<script setup lang="ts"> <!-- <script setup lang="ts">
import InputNumber from '@/components/Form/InputNumber.vue' import InputNumber from '@/components/Form/InputNumber.vue'
import Select from '@/components/Select.vue' import Select from '@/components/Select.vue'
import DatePicker from '@/components/DatePicker.vue' import DatePicker from '@/components/DatePicker.vue'
@ -49,4 +49,6 @@ import { useTotalReport } from '@/stores/totalReport';
<InputNumber :value="1" @change="changeMaxReport($event.target.value)"/> <InputNumber :value="1" @change="changeMaxReport($event.target.value)"/>
</div> </div>
</div> </div>
</template> </template> -->
<template></template>

View File

@ -1,205 +1,339 @@
<template> <template>
<div id="data"> <Filters @run-search="() => console.log(filters)" class="mb-4">
<DxDataGrid class="max-h-[calc(100vh-140px)]" :remote-operations="true" :data-source="data" key-expr="no_laporan" <Type1
:show-column-lines="true" :show-row-lines="false" :show-borders="true" :row-alternation-enabled="true" @update:filters="
:hover-state-enabled="true" @selection-changed="onSelectionChanged" :column-width="100" @exporting="onExporting" (value) => {
:allow-column-resizing="true" column-resizing-mode="widget"> filters = value
<DxPaging :page-size="5" :enabled="true" /> }
<DxPager :visible="true" :allowed-page-sizes="[5, 10, 20, 'all']" display-mode="full" "
:show-page-size-selector="true" :show-info="true" :show-navigation-buttons="true" /> />
<DxSelection mode="single" /> </Filters>
<!-- <DxScrolling column-rendering-mode="virtual" mode="virtual" row-rendering-mode="virtual" /> -->
<DxLoadPanel :position="position"
:show-indicator="showIndicator"
:show-pane="showPane" :shading="shading"
v-if="loading" v-model:visible="loading" :enabled="true" />
<DxSearchPanel :visible="true" :highlight-case-sensitive="true" />
<DxExport :enabled="true" :formats="['pdf', 'xlsx', 'document']" :allow-export-selected-data="false" />
<DxColumn css-class="custom-table-column" :width="50" alignment="center" <div id="data">
:calculateCellValue="(item: any) => data.findIndex((i) => i == item) + 1" data-type="number" caption="No" /> <DxDataGrid
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="no_laporan" class="max-h-[calc(100vh-140px)]"
caption="No Laporan" cell-template="data" /> :remote-operations="true"
:data-source="data"
key-expr="no_laporan"
:show-column-lines="true"
:show-row-lines="false"
:show-borders="true"
:row-alternation-enabled="true"
:hover-state-enabled="true"
@selection-changed="onSelectionChanged"
:column-width="100"
@exporting="onExporting"
:allow-column-resizing="true"
column-resizing-mode="widget"
>
<DxPaging :page-size="5" :enabled="true" />
<DxPager
:visible="true"
:allowed-page-sizes="[5, 10, 20, 'all']"
display-mode="full"
:show-page-size-selector="true"
:show-info="true"
:show-navigation-buttons="true"
/>
<DxSelection mode="single" />
<!-- <DxScrolling column-rendering-mode="virtual" mode="virtual" row-rendering-mode="virtual" /> -->
<DxLoadPanel
:position="position"
:show-indicator="showIndicator"
:show-pane="showPane"
:shading="shading"
v-if="loading"
v-model:visible="loading"
:enabled="true"
/>
<DxSearchPanel :visible="true" :highlight-case-sensitive="true" />
<DxExport
:enabled="true"
:formats="['pdf', 'xlsx', 'document']"
:allow-export-selected-data="false"
/>
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="pembuat_laporan" <DxColumn
caption="Pembuat Laporan" cell-template="data" /> css-class="custom-table-column"
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="tanggal_laporan" :width="50"
caption="Tgl Lapor" cell-template="data" /> alignment="center"
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="tanggal_dialihkan" :calculateCellValue="(item: any) => data.findIndex((i) => i == item) + 1"
caption="Tgl Dialihkan" cell-template="data" /> data-type="number"
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="tanggal_respon" caption="No"
caption="Tgl Response" cell-template="data" /> />
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="tanggal_recovery" <DxColumn
caption="Tgl Recovery" cell-template="data" /> css-class="custom-table-column"
<DxColumn css-class="custom-table-column" :width="170" alignment="center" data-field="durasi_respon_time" :width="150"
caption="Durasi Response Time" cell-template="data" /> alignment="center"
<DxColumn css-class="custom-table-column" :width="170" alignment="center" data-field="durasi_recovery_time" data-field="no_laporan"
caption="Durasi Recovery Time" cell-template="data" /> caption="No Laporan"
<DxColumn css-class="custom-table-column" :width="170" alignment="center" data-field="posko_asal" cell-template="data"
caption="Posko Awal" cell-template="data" /> />
<DxColumn css-class="custom-table-column" :width="170" alignment="center" data-field="posko_tujuan"
caption="Posko Tujuan" cell-template="data" /> <DxColumn
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="status_akhir" css-class="custom-table-column"
caption="Status" cell-template="data" /> :width="150"
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="idpel_nometer" alignment="center"
caption="IDPEL/NO METER" cell-template="data" /> data-field="pembuat_laporan"
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="nama_pelapor" caption="Pembuat Laporan"
caption="Nama Pelapor" cell-template="data" /> cell-template="data"
<DxColumn css-class="custom-table-column" :width="170" alignment="center" data-field="alamat_pelapor" />
caption="Alamat Pelapor" cell-template="data" /> <DxColumn
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="no_telp_pelapor" css-class="custom-table-column"
caption="No Telp Pelapor" cell-template="data" /> :width="150"
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="keterangan_pelapor" alignment="center"
caption="Keterangan Pelapor" cell-template="data" /> data-field="tanggal_laporan"
<DxColumn css-class="custom-table-column" :width="150" alignment="center" data-field="media" caption="Tgl Lapor"
caption="Sumber Lapor" cell-template="data" /> cell-template="data"
<DxColumn css-class="custom-table-column" :width="170" alignment="center" data-field="posko" caption="Posko" />
cell-template="data" /> <DxColumn
<template #data="{ data }"> css-class="custom-table-column"
<span class="cursor-pointer" @click="showData()"> :width="150"
{{ data.text }} alignment="center"
</span> data-field="tanggal_dialihkan"
</template> caption="Tgl Dialihkan"
</DxDataGrid> cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="150"
alignment="center"
data-field="tanggal_respon"
caption="Tgl Response"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="150"
alignment="center"
data-field="tanggal_recovery"
caption="Tgl Recovery"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="170"
alignment="center"
data-field="durasi_respon_time"
caption="Durasi Response Time"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="170"
alignment="center"
data-field="durasi_recovery_time"
caption="Durasi Recovery Time"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="170"
alignment="center"
data-field="posko_asal"
caption="Posko Awal"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="170"
alignment="center"
data-field="posko_tujuan"
caption="Posko Tujuan"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="150"
alignment="center"
data-field="status_akhir"
caption="Status"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="150"
alignment="center"
data-field="idpel_nometer"
caption="IDPEL/NO METER"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="150"
alignment="center"
data-field="nama_pelapor"
caption="Nama Pelapor"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="170"
alignment="center"
data-field="alamat_pelapor"
caption="Alamat Pelapor"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="150"
alignment="center"
data-field="no_telp_pelapor"
caption="No Telp Pelapor"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="150"
alignment="center"
data-field="keterangan_pelapor"
caption="Keterangan Pelapor"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="150"
alignment="center"
data-field="media"
caption="Sumber Lapor"
cell-template="data"
/>
<DxColumn
css-class="custom-table-column"
:width="170"
alignment="center"
data-field="posko"
caption="Posko"
cell-template="data"
/>
<template #data="{ data }">
<span class="cursor-pointer" @click="showData()">
{{ data.text }}
</span>
</template>
</DxDataGrid>
</div>
<DetailDialog
:open="showDetail"
title="Daftar Gangguan Dialihkan ke Posko Lain"
@on-close="closeDetail"
>
<div class="w-full p-4 space-y-2 bg-white rounded-xl">
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">No Laporan:</h3>
<InputText :readonly="true" :value="dataDetail?.no_laporan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Pembuat Laporan:</h3>
<InputText :readonly="true" :value="dataDetail?.pembuat_laporan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Tanggal Laporan:</h3>
<InputText :readonly="true" :value="dataDetail?.tanggal_laporan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Tanggal Dialihkan:</h3>
<InputText :readonly="true" :value="dataDetail?.tanggal_dialihkan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Tanggal Respon:</h3>
<InputText :readonly="true" :value="dataDetail?.tanggal_respon" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Tanggal Recovery:</h3>
<InputText :readonly="true" :value="dataDetail?.tanggal_recovery" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Durasi Response Time:</h3>
<InputText :readonly="true" :value="dataDetail?.durasi_respon" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Durasi Recovery Time:</h3>
<InputText :readonly="true" :value="dataDetail?.durasi_recovery" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Posko Awal:</h3>
<InputText :readonly="true" :value="dataDetail?.posko_awal" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Posko Tujuan:</h3>
<InputText :readonly="true" :value="dataDetail?.posko_tujuan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Status:</h3>
<InputText :readonly="true" :value="dataDetail?.status" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">IDPEL/NO METER:</h3>
<InputText :readonly="true" :value="dataDetail?.id_pelanggan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Nama Pelapor:</h3>
<InputText :readonly="true" :value="dataDetail?.nama_pelapor" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Alamat Pelapor:</h3>
<InputText
:readonly="true"
type="textarea"
:value="dataDetail?.alamat_pelapor"
class-name="flex-1 h-[56px]"
/>
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Pembuat Laporan:</h3>
<InputText :readonly="true" :value="dataDetail?.no_telp_pelapor" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Keterangan Pelapor:</h3>
<InputText :readonly="true" :value="dataDetail?.keterangan_pelapor" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Sumber Laporan:</h3>
<InputText :readonly="true" :value="dataDetail?.sumber_laporan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">Posko:</h3>
<InputText :readonly="true" :value="dataDetail?.posko" class-name="flex-1" />
</div>
</div> </div>
</DetailDialog>
<DetailDialog :open="showDetail" title="Daftar Gangguan Dialihkan ke Posko Lain" @on-close="closeDetail">
<div class="w-full p-4 space-y-2 bg-white rounded-xl">
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
No Laporan:
</h3>
<InputText :readonly="true" :value="dataDetail?.no_laporan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Pembuat Laporan:
</h3>
<InputText :readonly="true" :value="dataDetail?.pembuat_laporan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Tanggal Laporan:
</h3>
<InputText :readonly="true" :value="dataDetail?.tanggal_laporan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Tanggal Dialihkan:
</h3>
<InputText :readonly="true" :value="dataDetail?.tanggal_dialihkan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Tanggal Respon:
</h3>
<InputText :readonly="true" :value="dataDetail?.tanggal_respon" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Tanggal Recovery:
</h3>
<InputText :readonly="true" :value="dataDetail?.tanggal_recovery" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Durasi Response Time:
</h3>
<InputText :readonly="true" :value="dataDetail?.durasi_respon" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Durasi Recovery Time:
</h3>
<InputText :readonly="true" :value="dataDetail?.durasi_recovery" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Posko Awal:
</h3>
<InputText :readonly="true" :value="dataDetail?.posko_awal" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Posko Tujuan:
</h3>
<InputText :readonly="true" :value="dataDetail?.posko_tujuan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Status:
</h3>
<InputText :readonly="true" :value="dataDetail?.status" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
IDPEL/NO METER:
</h3>
<InputText :readonly="true" :value="dataDetail?.id_pelanggan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Nama Pelapor:
</h3>
<InputText :readonly="true" :value="dataDetail?.nama_pelapor" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Alamat Pelapor:
</h3>
<InputText :readonly="true" type="textarea" :value="dataDetail?.alamat_pelapor"
class-name="flex-1 h-[56px]" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Pembuat Laporan:
</h3>
<InputText :readonly="true" :value="dataDetail?.no_telp_pelapor" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Keterangan Pelapor:
</h3>
<InputText :readonly="true" :value="dataDetail?.keterangan_pelapor" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Sumber Laporan:
</h3>
<InputText :readonly="true" :value="dataDetail?.sumber_laporan" class-name="flex-1" />
</div>
<div class="flex flex-row items-center justify-between w-full">
<h3 class="text-sm font-medium w-[170px] text-gray-800">
Posko:
</h3>
<InputText :readonly="true" :value="dataDetail?.posko" class-name="flex-1" />
</div>
</div>
</DetailDialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// filters components
import Filters from '@/components/Form/Filters.vue'
import Type1 from '@/components/Form/FiltersType/Type1.vue'
import { DxDataGrid } from 'devextreme-vue' import { DxDataGrid } from 'devextreme-vue'
import { DxColumn, DxExport, DxLoadPanel, DxPager, DxPaging, DxScrolling, DxSearchPanel, DxSelection } from 'devextreme-vue/data-grid' import {
DxColumn,
DxExport,
DxLoadPanel,
DxPager,
DxPaging,
DxScrolling,
DxSearchPanel,
DxSelection
} from 'devextreme-vue/data-grid'
import { computed, onMounted, ref, watch } from 'vue' import { computed, onMounted, ref, watch } from 'vue'
import { jsPDF } from 'jspdf' import { jsPDF } from 'jspdf'
import { exportDataGrid as exportToPdf } from 'devextreme/pdf_exporter' import { exportDataGrid as exportToPdf } from 'devextreme/pdf_exporter'
@ -208,55 +342,54 @@ import { saveAs } from 'file-saver'
import { Workbook } from 'exceljs' import { Workbook } from 'exceljs'
import { useDialogStore } from '@/stores/dialog' import { useDialogStore } from '@/stores/dialog'
import { useFiltersStore } from '@/stores/filters'
import { useDateStore } from '@/stores/date' import { useDateStore } from '@/stores/date'
import DetailDialog from '@/components/Dialogs/DetailDialog.vue' import DetailDialog from '@/components/Dialogs/DetailDialog.vue'
import InputText from '@/components/InputText.vue' import InputText from '@/components/InputText.vue'
import { useQuery } from '@vue/apollo-composable' import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import { useSearchStore } from '@/stores/filtersAction'; import { useSearchStore } from '@/stores/filtersAction'
import { usePostsStore } from '@/stores/posts'; import { usePostsStore } from '@/stores/posts'
import { useUp3Store } from '@/stores/up3'; import { useUp3Store } from '@/stores/up3'
import { useRegionStore } from '@/stores/region'; import { useRegionStore } from '@/stores/region'
const position = { of: '#data' }; const position = { of: '#data' }
const showIndicator = ref(true); const showIndicator = ref(true)
const shading = ref(true); const shading = ref(true)
const showPane = ref(true); const showPane = ref(true)
const dialog = useDialogStore() const dialog = useDialogStore()
const data = ref<any[]>([]) const data = ref<any[]>([])
const dataDetail = ref<any>() const dataDetail = ref<any>()
const showDetail = ref(false) const showDetail = ref(false)
const closeDetail = () => { const closeDetail = () => {
showDetail.value = false showDetail.value = false
} }
const onExporting = (e: any) => { const onExporting = (e: any) => {
if (e.format === 'pdf') { if (e.format === 'pdf') {
const doc = new jsPDF() const doc = new jsPDF()
exportToPdf({ exportToPdf({
jsPDFDocument: doc, jsPDFDocument: doc,
component: e.component, component: e.component,
indent: 5, indent: 5
}).then(() => { }).then(() => {
doc.save(`.pdf`) doc.save(`.pdf`)
}) })
} else { } else {
const workbook = new Workbook() const workbook = new Workbook()
const worksheet = workbook.addWorksheet('Employees') const worksheet = workbook.addWorksheet('Employees')
exportToExcel({ exportToExcel({
component: e.component, component: e.component,
worksheet, worksheet,
autoFilterEnabled: true, autoFilterEnabled: true
}).then(() => { }).then(() => {
workbook.xlsx.writeBuffer().then((buffer: any) => { workbook.xlsx.writeBuffer().then((buffer: any) => {
saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx') saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx')
}) })
}) })
e.cancel = true e.cancel = true
} }
} }
const GET_GANGGUAN_DATA_DI_ALIHAN_KE_POSKO_LAIN = gql` const GET_GANGGUAN_DATA_DI_ALIHAN_KE_POSKO_LAIN = gql`
@ -285,59 +418,61 @@ const GET_GANGGUAN_DATA_DI_ALIHAN_KE_POSKO_LAIN = gql`
waktu_response waktu_response
} }
} }
`; `
const { onResult, onError, loading, refetch } = useQuery(GET_GANGGUAN_DATA_DI_ALIHAN_KE_POSKO_LAIN, { const { onResult, onError, loading, refetch } = useQuery(
GET_GANGGUAN_DATA_DI_ALIHAN_KE_POSKO_LAIN,
{
dateFrom: new Date().toISOString().slice(0, 10), dateFrom: new Date().toISOString().slice(0, 10),
dateTo: new Date().toISOString().slice(0, 10), dateTo: new Date().toISOString().slice(0, 10),
posko: "", posko: '',
idUid: 0, idUid: 0,
idUp3: 0, idUp3: 0
}) }
const reportButton = useSearchStore() )
const detected = computed(() => reportButton.isTriggerChange) // const reportButton = useSearchStore()
watch(detected, () => { // const detected = computed(() => reportButton.isTriggerChange)
// watch(detected, () => {
const dateValue = useDateStore().getDateValue().split(' s/d '); // const dateValue = useDateStore().getDateValue().split(' s/d ')
const posko = usePostsStore().getData() ? usePostsStore().getData() : "" // const posko = usePostsStore().getData() ? usePostsStore().getData() : ''
const up3 = useUp3Store().getData() ? useUp3Store().getData() : 0 // const up3 = useUp3Store().getData() ? useUp3Store().getData() : 0
const uid = useRegionStore().getData() ? useRegionStore().getData() : 0 // const uid = useRegionStore().getData() ? useRegionStore().getData() : 0
refetch({ // refetch({
dateFrom: dateValue[0].split('-').reverse().join('-'), // dateFrom: dateValue[0].split('-').reverse().join('-'),
dateTo: dateValue[1].split('-').reverse().join('-'), // dateTo: dateValue[1].split('-').reverse().join('-'),
posko: posko, // posko: posko,
idUid: uid, // idUid: uid,
idUp3: up3, // idUp3: up3
}) // })
onResult(queryResult => { // onResult((queryResult) => {
if (queryResult.data != undefined) { // if (queryResult.data != undefined) {
queryResult.data.daftarGangguanDialihkanKePoskoLain.forEach((item: any) => { // queryResult.data.daftarGangguanDialihkanKePoskoLain.forEach((item: any) => {
data.value = [...data.value, { // data.value = [
...item, // ...data.value,
pembuat_laporan: '-' // {
}]; // ...item,
}); // pembuat_laporan: '-'
} // }
console.log(queryResult.data) // ]
console.log(queryResult.loading) // })
console.log(queryResult.networkStatus) // }
}) // console.log(queryResult.data)
onError((error) => { // console.log(queryResult.loading)
console.log(error) // console.log(queryResult.networkStatus)
}) // })
}) // onError((error) => {
// console.log(error)
// })
// })
const onSelectionChanged = ({ selectedRowsData }: any) => { const onSelectionChanged = ({ selectedRowsData }: any) => {
const data = selectedRowsData[0] const data = selectedRowsData[0]
dataDetail.value = data dataDetail.value = data
console.log(data) console.log(data)
} }
const showData = () => { const showData = () => {
showDetail.value = true showDetail.value = true
} }
onMounted(() => {
const filters = useFiltersStore() // filters handler
filters.setConfig({ const filters = ref()
type: 'type-1',
});
})
</script> </script>

View File

@ -1,25 +1,20 @@
<template> <template>
<main <main
class="flex flex-col justify-between flex-1 px-4 overflow-y-auto bg-white md:mr-3 sm:px-3 md:px-6 rounded-t-2xl md:rounded-t-3xl no-scroll-bar"> class="flex flex-col justify-between flex-1 px-4 overflow-y-auto bg-white md:mr-3 sm:px-3 md:px-6 rounded-t-2xl md:rounded-t-3xl no-scroll-bar"
<div v-if="route.path !== '/home'" class="mt-4 lg:mt-6 max-w-7xl"> >
<h1 class="text-xl font-semibold md:text-3xl text-dark">{{ pageTitle }}</h1> <div v-if="route.path !== '/home'" class="mt-4 lg:mt-6 max-w-7xl">
</div> <h1 class="text-xl font-semibold md:text-3xl text-dark">{{ pageTitle }}</h1>
<div class="flex-1 mt-2 sm:mt-4 dx-viewport"> </div>
<Filters :report-button="filters.config.reportButton" class="mb-4" v-if="filters.config.component"> <div class="flex-1 mt-2 sm:mt-4 dx-viewport">
<component :is="filters.config.component" /> <slot></slot>
</Filters> </div>
</main>
<slot></slot>
</div>
</main>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useFiltersStore } from '@/stores/filters'; import { useFiltersStore } from '@/stores/filters'
import Filters from '../Form/Filters.vue'
// Dapatkan objek route dari vue-router // Dapatkan objek route dari vue-router
const route = useRoute() const route = useRoute()

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
interface DataItem { interface DataItem {
id: number; id: number
name: string; name: string
} }
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
@ -29,25 +29,26 @@ const props = defineProps({
default: () => ({ id: 0, name: '' }) default: () => ({ id: 0, name: '' })
} }
}) })
const emit = defineEmits(["update:selected"]) const emit = defineEmits(['update:selected'])
const data = computed(() => [{ id:0, name: props.placeholder }, ...props.data]); const data = computed(() => [{ id: 0, name: props.placeholder }, ...props.data])
computed(() => { computed(() => {
if (props.selected.id === 0) { if (props.selected.id === 0) {
selected.value = { id:0, name: props.placeholder } selected.value = { id: 0, name: props.placeholder }
} }
console.log('selected', selected.value.name); // console.log('selected', selected.value.name)
}) })
watch(() => props.selected, (value) => { watch(
if (value.id === 0) { () => props.selected,
selected.value = { id:0, name: props.placeholder } (value) => {
if (value.id === 0) {
selected.value = { id: 0, name: props.placeholder }
}
} }
}) )
let selected = ref<DataItem>(data.value[0]) let selected = ref<DataItem>(data.value[0])
let query = ref('') let query = ref('')
@ -55,56 +56,86 @@ let filteredData = computed(() =>
query.value === '' query.value === ''
? data.value ? data.value
: data.value.filter((item: DataItem) => : data.value.filter((item: DataItem) =>
item.name item.name
.toLowerCase() .toLowerCase()
.replace(/\s+/g, '') .replace(/\s+/g, '')
.includes(query.value.toLowerCase().replace(/\s+/g, '')) .includes(query.value.toLowerCase().replace(/\s+/g, ''))
) )
) )
watch(selected, (value) => {
emit('update:selected', value)
})
const show = ref(false) const show = ref(false)
</script> </script>
<template> <template>
<Combobox <Combobox
@update:modelValue="value => emit('update:selected', value)" @update:modelValue="(value) => emit('update:selected', value)"
v-model="selected" v-slot="{ open }"> v-model="selected"
v-slot="{ open }"
>
<div class="relative mt-1"> <div class="relative mt-1">
<div <div
class="relative w-full cursor-default overflow-hidden rounded-lg bg-gray-200 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm"> class="relative w-full cursor-default overflow-hidden rounded-lg bg-gray-200 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm"
<ComboboxInput class="w-full border-none py-2 pl-3 pr-10 bg-gray-200 text-sm leading-5 text-gray-900 focus:ring-0" >
:displayValue="(item: any) => (show || open ? '' : item.name)" @change="query = $event.target.value" <ComboboxInput
@click="show = true" @blur="show = false" defaultValue="" /> class="w-full border-none py-2 pl-3 pr-10 bg-gray-200 text-sm leading-5 text-gray-900 focus:ring-0"
:displayValue="(item: any) => (show || open ? '' : item.name)"
@change="query = $event.target.value"
@click="show = true"
@blur="show = false"
defaultValue=""
/>
<ComboboxButton class="absolute inset-y-0 right-0 flex items-center pr-2"> <ComboboxButton class="absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" /> <ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
</ComboboxButton> </ComboboxButton>
</div> </div>
<TransitionRoot :show="show || open" <TransitionRoot
:show="show || open"
leave="transition ease-in duration-100" leaveFrom="opacity-100" leave="transition ease-in duration-100"
leaveTo="opacity-0" @after-leave="query = ''"> leaveFrom="opacity-100"
<ComboboxOptions static leaveTo="opacity-0"
class="absolute mt-1 z-10 max-h-60 w-full overflow-auto rounded-md bg-gray-200 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> @after-leave="query = ''"
<div v-if="filteredData.length === 0 && query !== ''" >
class="relative cursor-default select-none py-2 px-4 text-gray-700"> <ComboboxOptions
static
class="absolute mt-1 z-10 max-h-60 w-full overflow-auto rounded-md bg-gray-200 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
>
<div
v-if="filteredData.length === 0 && query !== ''"
class="relative cursor-default select-none py-2 px-4 text-gray-700"
>
Nothing found. Nothing found.
</div> </div>
<ComboboxOption v-for="item in filteredData" <ComboboxOption
as="template" :key="item.id" :value="item" v-for="item in filteredData"
v-slot="{ selected, active }"> as="template"
<li class="relative cursor-default select-none py-2 pl-10 pr-4" :class="{ :key="item.id"
'bg-teal-600 text-white': active, :value="item"
'text-gray-900': !active v-slot="{ selected, active }"
}"> >
<span class="block truncate" :class="{ 'font-medium': selected, 'font-normal': !selected }"> <li
class="relative cursor-default select-none py-2 pl-10 pr-4"
:class="{
'bg-teal-600 text-white': active,
'text-gray-900': !active
}"
>
<span
class="block truncate"
:class="{ 'font-medium': selected, 'font-normal': !selected }"
>
{{ item.name }} {{ item.name }}
</span> </span>
<span v-if="selected" class="absolute inset-y-0 left-0 flex items-center pl-3" <span
:class="{ 'text-white': active, 'text-teal-600': !active }"> v-if="selected"
class="absolute inset-y-0 left-0 flex items-center pl-3"
:class="{ 'text-white': active, 'text-teal-600': !active }"
>
<CheckIcon class="h-5 w-5" aria-hidden="true" /> <CheckIcon class="h-5 w-5" aria-hidden="true" />
</span> </span>
</li> </li>

View File

@ -1,4 +1,3 @@
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router' import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import HomeView from '@/views/HomeView.vue' import HomeView from '@/views/HomeView.vue'
@ -28,7 +27,7 @@ import {
GangguanTable17, GangguanTable17,
GangguanTable18, GangguanTable18,
GangguanTable19, GangguanTable19,
GangguanTable20, GangguanTable20
} from '@/components/Pages/Gangguan' } from '@/components/Pages/Gangguan'
import { import {
KeluhanTable1, KeluhanTable1,
@ -45,7 +44,7 @@ import {
KeluhanTable12, KeluhanTable12,
KeluhanTable13, KeluhanTable13,
KeluhanTable14, KeluhanTable14,
KeluhanTable15, KeluhanTable15
} from '@/components/Pages/Keluhan' } from '@/components/Pages/Keluhan'
import { import {
MonalisaTable1, MonalisaTable1,
@ -74,7 +73,12 @@ import {
MonalisaTable9 MonalisaTable9
} from '@/components/Pages/Monalisa' } from '@/components/Pages/Monalisa'
import { CicoTable1 } from '@/components/Pages/Cico' import { CicoTable1 } from '@/components/Pages/Cico'
import { AnomaliTable1, AnomaliTable2, AnomaliTable3, AnomaliTable5 } from '@/components/Pages/Anomali' import {
AnomaliTable1,
AnomaliTable2,
AnomaliTable3,
AnomaliTable5
} from '@/components/Pages/Anomali'
import { CttTable1 } from '@/components/Pages/Ctt' import { CttTable1 } from '@/components/Pages/Ctt'
import { MaterialTable1, MaterialTable2 } from '@/components/Pages/Material' import { MaterialTable1, MaterialTable2 } from '@/components/Pages/Material'
import { TransaksiTable1 } from '@/components/Pages/Transaksi' import { TransaksiTable1 } from '@/components/Pages/Transaksi'
@ -89,7 +93,7 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '', path: '',
name: 'Home Page', name: 'Home Page',
component: WelcomePage, component: WelcomePage
}, },
{ {
path: 'gangguan', path: 'gangguan',
@ -102,43 +106,43 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Daftar Gangguan Dialihkan Ke Posko Lain', name: 'Daftar Gangguan Dialihkan Ke Posko Lain',
component: GangguanTable1, component: GangguanTable1
}, },
{ {
path: '2', path: '2',
name: 'Daftar Gangguan Melapor Lebih Dari 1 Kali', name: 'Daftar Gangguan Melapor Lebih Dari 1 Kali',
component: GangguanTable2, component: GangguanTable2
}, },
{ {
path: '3', path: '3',
name: 'Daftar Gangguan Response Time', name: 'Daftar Gangguan Response Time',
component: GangguanTable3, component: GangguanTable3
}, },
{ {
path: '4', path: '4',
name: 'Daftar Gangguan Recovery Time', name: 'Daftar Gangguan Recovery Time',
component: GangguanTable4, component: GangguanTable4
}, },
{ {
path: '5', path: '5',
name: 'Daftar Gangguan Selesai Tanpa ID Pelanggan', name: 'Daftar Gangguan Selesai Tanpa ID Pelanggan',
component: GangguanTable5, component: GangguanTable5
}, },
{ {
path: '6', path: '6',
name: 'Daftar Gangguan Berdasarkan Media', name: 'Daftar Gangguan Berdasarkan Media',
component: GangguanTable6, component: GangguanTable6
}, },
{ {
path: '7', path: '7',
name: 'Daftar Gangguan Diselesaikan Mobile APKT', name: 'Daftar Gangguan Diselesaikan Mobile APKT',
component: GangguanTable7, component: GangguanTable7
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'rekapitulasi', path: 'rekapitulasi',
@ -147,77 +151,77 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Rekapitulasi Gangguan All', name: 'Rekapitulasi Gangguan All',
component: GangguanTable8, component: GangguanTable8
}, },
{ {
path: '2', path: '2',
name: 'Rekapitulasi Gangguan/Jenis Gangguan', name: 'Rekapitulasi Gangguan/Jenis Gangguan',
component: GangguanTable9, component: GangguanTable9
}, },
{ {
path: '3', path: '3',
name: 'Rekapitulasi Gangguan/Jenis Gangguan SE 004', name: 'Rekapitulasi Gangguan/Jenis Gangguan SE 004',
component: GangguanTable10, component: GangguanTable10
}, },
{ {
path: '4', path: '4',
name: 'Rekapitulasi Gangguan Per Posko', name: 'Rekapitulasi Gangguan Per Posko',
component: GangguanTable11, component: GangguanTable11
}, },
{ {
path: '5', path: '5',
name: 'Rekapitulasi Gangguan Per Regu', name: 'Rekapitulasi Gangguan Per Regu',
component: GangguanTable12, component: GangguanTable12
}, },
{ {
path: '6', path: '6',
name: 'Rekapitulasi Gangguan Per Tanggal', name: 'Rekapitulasi Gangguan Per Tanggal',
component: GangguanTable13, component: GangguanTable13
}, },
{ {
path: '7', path: '7',
name: 'Rekapitulasi Gangguan Berdasarkan Media', name: 'Rekapitulasi Gangguan Berdasarkan Media',
component: GangguanTable14, component: GangguanTable14
}, },
{ {
path: '8', path: '8',
name: 'Rekapitulasi Gangguan Alih Posko', name: 'Rekapitulasi Gangguan Alih Posko',
component: GangguanTable15, component: GangguanTable15
}, },
{ {
path: '9', path: '9',
name: 'Rekapitulasi Gangguan Diselesaikan Mobile APKT', name: 'Rekapitulasi Gangguan Diselesaikan Mobile APKT',
component: GangguanTable16, component: GangguanTable16
}, },
{ {
path: '10', path: '10',
name: 'Rekapitulasi Rating Per Posko', name: 'Rekapitulasi Rating Per Posko',
component: GangguanTable17, component: GangguanTable17
}, },
{ {
path: '11', path: '11',
name: 'Rekapitulasi Rating Per Regu', name: 'Rekapitulasi Rating Per Regu',
component: GangguanTable18, component: GangguanTable18
}, },
{ {
path: '12', path: '12',
name: 'Rekapitulasi Koreksi Transaksi Individual', name: 'Rekapitulasi Koreksi Transaksi Individual',
component: GangguanTable19, component: GangguanTable19
}, },
{ {
path: '13', path: '13',
name: 'Rekapitulasi Cleansing Transaksi TM', name: 'Rekapitulasi Cleansing Transaksi TM',
component: GangguanTable20, component: GangguanTable20
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
] ]
}, },
@ -232,43 +236,43 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Daftar Keluhan Dialihkan Ke Unit Lain', name: 'Daftar Keluhan Dialihkan Ke Unit Lain',
component: KeluhanTable1, component: KeluhanTable1
}, },
{ {
path: '2', path: '2',
name: 'Daftar Keluhan Pelanggan Lebih Dari 1 Kali', name: 'Daftar Keluhan Pelanggan Lebih Dari 1 Kali',
component: KeluhanTable2, component: KeluhanTable2
}, },
{ {
path: '3', path: '3',
name: 'Daftar Keluhan Response Time', name: 'Daftar Keluhan Response Time',
component: KeluhanTable3, component: KeluhanTable3
}, },
{ {
path: '4', path: '4',
name: 'Daftar Keluhan Recovery Time', name: 'Daftar Keluhan Recovery Time',
component: KeluhanTable4, component: KeluhanTable4
}, },
{ {
path: '5', path: '5',
name: 'Daftar Keluhan Selesai Tanpa ID Pelanggan', name: 'Daftar Keluhan Selesai Tanpa ID Pelanggan',
component: KeluhanTable5, component: KeluhanTable5
}, },
{ {
path: '6', path: '6',
name: 'Daftar Keluhan Berdasarkan Media', name: 'Daftar Keluhan Berdasarkan Media',
component: KeluhanTable6, component: KeluhanTable6
}, },
{ {
path: '7', path: '7',
name: 'Daftar Keluhan Selesai di CC123', name: 'Daftar Keluhan Selesai di CC123',
component: KeluhanTable7, component: KeluhanTable7
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'rekapitulasi', path: 'rekapitulasi',
@ -277,54 +281,54 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Rekapitulasi Keluhan All', name: 'Rekapitulasi Keluhan All',
component: KeluhanTable8, component: KeluhanTable8
}, },
{ {
path: '2', path: '2',
name: 'Rekapitulasi Keluhan Per Fungsi Bidang', name: 'Rekapitulasi Keluhan Per Fungsi Bidang',
component: KeluhanTable9, component: KeluhanTable9
}, },
{ {
path: '3', path: '3',
name: 'Rekapitulasi Keluhan Per Jenis Keluhan', name: 'Rekapitulasi Keluhan Per Jenis Keluhan',
component: KeluhanTable10, component: KeluhanTable10
}, },
{ {
path: '4', path: '4',
name: 'Rekapitulasi Keluhan Per Kelompok Keluhan', name: 'Rekapitulasi Keluhan Per Kelompok Keluhan',
component: KeluhanTable11, component: KeluhanTable11
}, },
{ {
path: '5', path: '5',
name: 'Rekapitulasi Keluhan Per Tanggal', name: 'Rekapitulasi Keluhan Per Tanggal',
component: KeluhanTable12, component: KeluhanTable12
}, },
{ {
path: '6', path: '6',
name: 'Rekapitulasi Keluhan Per Unit', name: 'Rekapitulasi Keluhan Per Unit',
component: KeluhanTable13, component: KeluhanTable13
}, },
{ {
path: '7', path: '7',
name: 'Rekapitulasi Keluhan Berdasarkan Media', name: 'Rekapitulasi Keluhan Berdasarkan Media',
component: KeluhanTable14, component: KeluhanTable14
}, },
{ {
path: '8', path: '8',
name: 'Rekapitulasi Rating Per Unit', name: 'Rekapitulasi Rating Per Unit',
component: KeluhanTable15, component: KeluhanTable15
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'monalisa', path: 'monalisa',
@ -341,59 +345,59 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Jumlah Kali Gangguan', name: 'Jumlah Kali Gangguan',
component: MonalisaTable1, component: MonalisaTable1
}, },
{ {
path: '2', path: '2',
name: 'Dispacthing Time (DT) Gangguan', name: 'Dispacthing Time (DT) Gangguan',
component: MonalisaTable2, component: MonalisaTable2
}, },
{ {
path: '3', path: '3',
name: 'Recovery Time (RCT) Gangguan', name: 'Recovery Time (RCT) Gangguan',
component: MonalisaTable3, component: MonalisaTable3
}, },
{ {
path: '4', path: '4',
name: 'Response Time (RPT) Gangguan', name: 'Response Time (RPT) Gangguan',
component: MonalisaTable4, component: MonalisaTable4
}, },
{ {
path: '5', path: '5',
name: 'Jumlah dan Durasi RPT RCT Gangguan', name: 'Jumlah dan Durasi RPT RCT Gangguan',
component: MonalisaTable5, component: MonalisaTable5
}, },
{ {
path: '6', path: '6',
name: 'Rekapitulasi Gangguan Per Jenis Gangguan', name: 'Rekapitulasi Gangguan Per Jenis Gangguan',
component: MonalisaTable6, component: MonalisaTable6
}, },
{ {
path: '7', path: '7',
name: 'Rekapitulasi Lapor Ulang Gangguan', name: 'Rekapitulasi Lapor Ulang Gangguan',
component: MonalisaTable7, component: MonalisaTable7
}, },
{ {
path: '8', path: '8',
name: 'Rekapitulasi ENS Gangguan', name: 'Rekapitulasi ENS Gangguan',
component: MonalisaTable8, component: MonalisaTable8
}, },
{ {
path: '9', path: '9',
name: 'Rekapitulasi Gangguan Belum Selesai', name: 'Rekapitulasi Gangguan Belum Selesai',
component: MonalisaTable9, component: MonalisaTable9
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'keluhan', path: 'keluhan',
@ -406,49 +410,49 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Jumlah Kali Keluhan', name: 'Jumlah Kali Keluhan',
component: MonalisaTable10, component: MonalisaTable10
}, },
{ {
path: '2', path: '2',
name: 'Recovery Time (RCT) Keluhan', name: 'Recovery Time (RCT) Keluhan',
component: MonalisaTable11, component: MonalisaTable11
}, },
{ {
path: '3', path: '3',
name: 'Response Time (RPT) Keluhan', name: 'Response Time (RPT) Keluhan',
component: MonalisaTable12, component: MonalisaTable12
}, },
{ {
path: '4', path: '4',
name: 'Jumlah dan Durasi RPT RCT Keluhan', name: 'Jumlah dan Durasi RPT RCT Keluhan',
component: MonalisaTable13, component: MonalisaTable13
}, },
{ {
path: '5', path: '5',
name: 'Rekapitulasi Gangguan Per Jenis Keluhan', name: 'Rekapitulasi Gangguan Per Jenis Keluhan',
component: MonalisaTable14, component: MonalisaTable14
}, },
{ {
path: '6', path: '6',
name: 'Rekapitulasi Lapor Ulang Keluhan', name: 'Rekapitulasi Lapor Ulang Keluhan',
component: MonalisaTable15, component: MonalisaTable15
}, },
{ {
path: '7', path: '7',
name: 'Rekapitulasi Keluhan Belum Selesai', name: 'Rekapitulasi Keluhan Belum Selesai',
component: MonalisaTable16, component: MonalisaTable16
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'laporan-kpi', path: 'laporan-kpi',
@ -461,28 +465,28 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: '(Monalisa) Penurunan Jumlah Komplain', name: '(Monalisa) Penurunan Jumlah Komplain',
component: MonalisaTable17, component: MonalisaTable17
}, },
{ {
path: '2', path: '2',
name: '(Monalisa) Aging Complaint', name: '(Monalisa) Aging Complaint',
component: MonalisaTable18, component: MonalisaTable18
}, },
{ {
path: '3', path: '3',
name: '(Monalisa) Energy Not Sales (ENS)', name: '(Monalisa) Energy Not Sales (ENS)',
component: MonalisaTable19, component: MonalisaTable19
}, },
{ {
path: '4', path: '4',
name: '(Monalisa) Kepatuhan dan Akurasi Dalam Pelaporan', name: '(Monalisa) Kepatuhan dan Akurasi Dalam Pelaporan',
component: MonalisaTable20, component: MonalisaTable20
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'kumulatif', path: 'kumulatif',
@ -491,40 +495,40 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Penurunan Jumlah Komplain', name: 'Penurunan Jumlah Komplain',
component: MonalisaTable21, component: MonalisaTable21
}, },
{ {
path: '2', path: '2',
name: 'Aging Complaint', name: 'Aging Complaint',
component: MonalisaTable22, component: MonalisaTable22
}, },
{ {
path: '3', path: '3',
name: 'Energy Not Sales (ENS)', name: 'Energy Not Sales (ENS)',
component: MonalisaTable23, component: MonalisaTable23
}, },
{ {
path: '4', path: '4',
name: 'Kepatuhan dan Akurasi Dalam Pelaporan', name: 'Kepatuhan dan Akurasi Dalam Pelaporan',
component: MonalisaTable24, component: MonalisaTable24
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
}, }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
}, }
], ]
}, },
{ {
path: 'check-in-out', path: 'check-in-out',
@ -537,19 +541,19 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Laporan Check In /Check Out (CICO)', name: 'Laporan Check In /Check Out (CICO)',
component: CicoTable1, component: CicoTable1
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'anomali-pengaduan', path: 'anomali-pengaduan',
@ -562,18 +566,18 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Laporan Anomali Penangan Pengaduan Gangguan Unit', name: 'Laporan Anomali Penangan Pengaduan Gangguan Unit',
component: AnomaliTable1, component: AnomaliTable1
}, },
{ {
path: '2', path: '2',
name: 'Laporan Anomali Penangan Pengaduan Gangguan Petugas', name: 'Laporan Anomali Penangan Pengaduan Gangguan Petugas',
component: AnomaliTable3, component: AnomaliTable3
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'keluhan', path: 'keluhan',
@ -582,19 +586,19 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Laporan Anomali Penangan Pengaduan Keluhan Unit', name: 'Laporan Anomali Penangan Pengaduan Keluhan Unit',
component: AnomaliTable5, component: AnomaliTable5
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'ctt-kwh-periksa', path: 'ctt-kwh-periksa',
@ -603,13 +607,13 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Laporan CTT & KWH Periksa', name: 'Laporan CTT & KWH Periksa',
component: CttTable1, component: CttTable1
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'material', path: 'material',
@ -622,13 +626,13 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Daftar Gangguan Dan Material Yang Dipakai', name: 'Daftar Gangguan Dan Material Yang Dipakai',
component: MaterialTable1, component: MaterialTable1
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'rekapitulasi', path: 'rekapitulasi',
@ -637,19 +641,19 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Rekapitulasi Pemakaian Material', name: 'Rekapitulasi Pemakaian Material',
component: MaterialTable2, component: MaterialTable2
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'transaksi', path: 'transaksi',
@ -658,13 +662,13 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Transaksi APKT', name: 'Transaksi APKT',
component: TransaksiTable1, component: TransaksiTable1
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
], ]
}, },
{ {
path: 'pencarian-data', path: 'pencarian-data',
@ -673,17 +677,17 @@ export const routes: RouteRecordRaw[] = [
{ {
path: '1', path: '1',
name: 'Pencarian Report Number', name: 'Pencarian Report Number',
component: PencarianDataTable1, component: PencarianDataTable1
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
] ]
}, },
{ {
path: ':pathMatch(.*)*', path: ':pathMatch(.*)*',
component: EmptyPage, component: EmptyPage
} }
] ]
}, },
@ -711,46 +715,45 @@ export const routes: RouteRecordRaw[] = [
] ]
export const mergeChildrenRoutes = (routes: RouteRecordRaw[]): RouteRecordRaw[] => { export const mergeChildrenRoutes = (routes: RouteRecordRaw[]): RouteRecordRaw[] => {
const mergedRoutes: RouteRecordRaw[] = []; const mergedRoutes: RouteRecordRaw[] = []
for (const route of routes) { for (const route of routes) {
if (route.children && route.children.length > 0) { if (route.children && route.children.length > 0) {
// Buat salinan route tanpa children // Buat salinan route tanpa children
const topLevelRoute: RouteRecordRaw = { ...route, children: [] }; const topLevelRoute: RouteRecordRaw = { ...route, children: [] }
// Salin setiap route children ke children route paling atas // Salin setiap route children ke children route paling atas
for (const childRoute of route.children) { for (const childRoute of route.children) {
const fullPath = `${route.path}/${childRoute.path}`.replace(/\/+/g, '/'); const fullPath = `${route.path}/${childRoute.path}`.replace(/\/+/g, '/')
const mergedChildRoute: RouteRecordRaw = { ...childRoute, path: fullPath }; const mergedChildRoute: RouteRecordRaw = { ...childRoute, path: fullPath }
topLevelRoute.children?.push(mergedChildRoute); topLevelRoute.children?.push(mergedChildRoute)
} }
mergedRoutes.push(topLevelRoute); mergedRoutes.push(topLevelRoute)
} else { } else {
// Jika route tidak memiliki children, tambahkan as is // Jika route tidak memiliki children, tambahkan as is
mergedRoutes.push(route); mergedRoutes.push(route)
} }
} }
return mergedRoutes; return mergedRoutes
} }
export const extractLeafRoutes = (routes: RouteRecordRaw[], parentPath = ''): RouteRecordRaw[] => { export const extractLeafRoutes = (routes: RouteRecordRaw[], parentPath = ''): RouteRecordRaw[] => {
const leafRoutes: RouteRecordRaw[] = []; const leafRoutes: RouteRecordRaw[] = []
for (const route of routes) { for (const route of routes) {
const fullPath = `${parentPath}/${route.path}`.replace(/\/+/g, '/')
const fullPath = `${parentPath}/${route.path}`.replace(/\/+/g, '/');
if (route.children && route.children.length > 0) { if (route.children && route.children.length > 0) {
leafRoutes.push(...extractLeafRoutes(route.children, fullPath)); leafRoutes.push(...extractLeafRoutes(route.children, fullPath))
} else { } else {
const leafRoute = { ...route, path: fullPath }; const leafRoute = { ...route, path: fullPath }
leafRoutes.push(leafRoute); leafRoutes.push(leafRoute)
} }
} }
return leafRoutes; return leafRoutes
} }
export const fixRoute = (route: RouteRecordRaw[]): RouteRecordRaw[] => { export const fixRoute = (route: RouteRecordRaw[]): RouteRecordRaw[] => {
@ -770,14 +773,27 @@ export const fixRoute = (route: RouteRecordRaw[]): RouteRecordRaw[] => {
getRoute(r.children, `${parent}/${r.path}`) getRoute(r.children, `${parent}/${r.path}`)
} else { } else {
const fullPath = `${parent.replace('/', '')}/${r.path.replace('/', '')}` const fullPath = `${parent.replace('/', '')}/${r.path.replace('/', '')}`
if (newRoute.find((nr) => nr.path !== '/' && nr.path === '/home' && fullPath.includes(nr.path))) { if (
newRoute.find(
(nr) => nr.path !== '/' && nr.path === '/home' && fullPath.includes(nr.path)
)
) {
const index = newRoute.findIndex((nr) => nr.path !== '/' && fullPath.includes(nr.path)) const index = newRoute.findIndex((nr) => nr.path !== '/' && fullPath.includes(nr.path))
newRoute[index] = { newRoute[index] = {
...newRoute[index], children: [...newRoute[index].children ?? [], { ...newRoute[index],
path: fullPath === '/home/' ? '' : fullPath.includes('/home/') ? fullPath.replace('/home/', '') : fullPath, children: [
name: r.name, ...(newRoute[index].children ?? []),
component: r.component {
} as RouteRecordRaw] path:
fullPath === '/home/'
? ''
: fullPath.includes('/home/')
? fullPath.replace('/home/', '')
: fullPath,
name: r.name,
component: r.component
} as RouteRecordRaw
]
} }
} else { } else {
newRoute.push({ newRoute.push({
@ -792,25 +808,26 @@ export const fixRoute = (route: RouteRecordRaw[]): RouteRecordRaw[] => {
getRoute(route) getRoute(route)
// remove duplicate route path and sort by path length // remove duplicate route path and sort by path length
const uniqueRoute = newRoute.filter((nr, index, self) => self.findIndex((n) => n.path === nr.path) === index).sort((a, b) => b.path.length - a.path.length) const uniqueRoute = newRoute
.filter((nr, index, self) => self.findIndex((n) => n.path === nr.path) === index)
.sort((a, b) => b.path.length - a.path.length)
// console.log('route', newRoute); // console.log('route', newRoute);
// return uniqueRoute // return uniqueRoute
return newRoute return newRoute
} }
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
linkActiveClass: 'active', linkActiveClass: 'active',
stringifyQuery: qs.stringify, stringifyQuery: qs.stringify,
routes: fixRoute(routes), routes: fixRoute(routes),
strict: true, strict: true
}) })
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page // redirect to login page if not logged in and trying to access a restricted page
const auth = useAuthStore(); const auth = useAuthStore()
// if to is not found, redirect to 404 // if to is not found, redirect to 404
if (to.matched.length === 0) { if (to.matched.length === 0) {