feat : middleware and refresh token
This commit is contained in:
@@ -1,70 +1,138 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import api from '@/utils/api';
|
||||
import { Icon } from "@iconify/vue";
|
||||
|
||||
|
||||
interface RolePage {
|
||||
id: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
url: string;
|
||||
level: number;
|
||||
sort: number;
|
||||
active: boolean;
|
||||
children?: RolePage[];
|
||||
}
|
||||
import PageRow from '@/components/apps/setting/pages/PageRow.vue';
|
||||
import AddPageModal from '@/components/apps/setting/pages/AddPageModal.vue';
|
||||
import DialogConfrim from '~/components/shared/DialogConfrim.vue';
|
||||
import type { RolePage } from '~/types/setting/menu';
|
||||
import { useSnackbarStore } from '~/store/snackbar';
|
||||
|
||||
const pages = ref<RolePage[]>([]);
|
||||
const loading = ref(false);
|
||||
const isModalOpen = ref(false);
|
||||
|
||||
const modalMode = ref<'create' | 'detail' | 'edit'>('create');
|
||||
const selectedPageId = ref<number | null>(null);
|
||||
const snackbarStore = useSnackbarStore();
|
||||
|
||||
const isDeleteDialogOpen = ref(false);
|
||||
const isDeleting = ref(false);
|
||||
const deleteTargetId = ref<number | null>(null);
|
||||
const deleteTargetName = ref<string>('');
|
||||
|
||||
|
||||
const fetchPages = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await api.get('/api/v1/roles/pages/tree');
|
||||
pages.value = response.data?.data || [];
|
||||
console.log('Pages loaded:', pages.value);
|
||||
} catch (error) {
|
||||
console.error('Error fetching pages:', error);
|
||||
snackbarStore.showSnackbar('Gagal memuat data menu', 'error');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleStatusChange = (page: RolePage) => {
|
||||
console.log(`Page ${page.name} status changed to: ${page.active}`);
|
||||
// Here you can add the API call to update the status on the server
|
||||
// For example:
|
||||
// api.put(`/api/v1/roles/pages/${page.id}`, { active: page.active })
|
||||
// .catch(error => console.error('Error updating status:', error));
|
||||
const handlePageSaved = async () => {
|
||||
await fetchPages();
|
||||
};
|
||||
|
||||
const openCreateModal = () => {
|
||||
modalMode.value = 'create';
|
||||
selectedPageId.value = null;
|
||||
isModalOpen.value = true;
|
||||
};
|
||||
|
||||
const openDetailModal = (page: RolePage) => {
|
||||
modalMode.value = 'detail';
|
||||
selectedPageId.value = page.id;
|
||||
isModalOpen.value = true;
|
||||
};
|
||||
|
||||
const openEditModal = (page: RolePage) => {
|
||||
modalMode.value = 'edit';
|
||||
selectedPageId.value = page.id;
|
||||
isModalOpen.value = true;
|
||||
};
|
||||
|
||||
const openDeleteDialog = (page: RolePage) => {
|
||||
deleteTargetId.value = page.id;
|
||||
deleteTargetName.value = page.name;
|
||||
isDeleteDialogOpen.value = true;
|
||||
};
|
||||
|
||||
const handleCancelDelete = () => {
|
||||
deleteTargetId.value = null;
|
||||
deleteTargetName.value = '';
|
||||
};
|
||||
|
||||
const handleConfirmDelete = async () => {
|
||||
if (!deleteTargetId.value) return;
|
||||
if (isDeleting.value) return;
|
||||
|
||||
isDeleting.value = true;
|
||||
try {
|
||||
await api.delete(`/api/v1/roles/pages/${deleteTargetId.value}`);
|
||||
snackbarStore.showSnackbar('Menu berhasil dihapus', 'success');
|
||||
isDeleteDialogOpen.value = false;
|
||||
await fetchPages();
|
||||
} catch (error: any) {
|
||||
const message = error?.response?.data?.message || error?.message || 'Gagal menghapus menu';
|
||||
snackbarStore.showSnackbar(message, 'error');
|
||||
} finally {
|
||||
isDeleting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
fetchPages();
|
||||
});
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="loading" class="text-center py-4">
|
||||
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
||||
</div>
|
||||
|
||||
<div v-else-if="pages.length === 0" class="text-center py-4">
|
||||
<p class="text-muted">No pages found</p>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<v-card v-for="page in pages" :key="page.id" class="mb-2" elevation="10">
|
||||
<v-card-text class="px-4 pb-0 pt-4">
|
||||
<div class="d-flex align-top">
|
||||
<Icon :icon="'solar:' + page.icon" width="18" />
|
||||
<div class="flex-grow-1 ms-2">
|
||||
<h4 class="mb-1">{{ page.name }}</h4>
|
||||
<p class="text-muted mb-0">{{ page.url }}</p>
|
||||
<v-card elevation="0" class="mb-5">
|
||||
<v-card-title class="pa-4">
|
||||
<div class="d-flex align-center">
|
||||
<div>
|
||||
<h4 class="text-h4">Menu</h4>
|
||||
</div>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" flat @click="openCreateModal">
|
||||
<v-icon class="mr-2">mdi-plus</v-icon>
|
||||
Tambah Menu
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-switch v-model="page.active" @change="handleStatusChange(page)"></v-switch>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
|
||||
<LoadingState :loading="loading" :empty="pages.length === 0 && !loading" loading-text="Memuat data pages..."
|
||||
empty-text="Tidak ada data pages">
|
||||
<div>
|
||||
<PageRow
|
||||
v-for="page in pages"
|
||||
:key="page.id"
|
||||
:page="page"
|
||||
:level="0"
|
||||
@detail="openDetailModal"
|
||||
@edit="openEditModal"
|
||||
@delete="openDeleteDialog"
|
||||
/>
|
||||
</div>
|
||||
</LoadingState>
|
||||
<AddPageModal v-model="isModalOpen" :mode="modalMode" :page-id="selectedPageId" @saved="handlePageSaved" />
|
||||
|
||||
<DialogConfrim
|
||||
v-model="isDeleteDialogOpen"
|
||||
title="Hapus menu"
|
||||
:message="`Apakah anda yakin menghapus menu ${deleteTargetName} ?`"
|
||||
:loading="isDeleting"
|
||||
:close-on-confirm="false"
|
||||
@confirm="handleConfirmDelete"
|
||||
@cancel="handleCancelDelete"
|
||||
/>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user