feat : middleware and refresh token
This commit is contained in:
@@ -5,6 +5,8 @@ import sidebarItems from "~/components/layout/full/vertical-sidebar/sidebarItem"
|
||||
import { Menu2Icon } from "vue-tabler-icons";
|
||||
import { useCustomizerStore } from "~/store/customizer";
|
||||
import AppSnackbar from '~/components/shared/AppSnackbar.vue';
|
||||
import Customizer from "~/components/layout/full/customizer/Customizer.vue";
|
||||
|
||||
|
||||
const sidebarMenu = shallowRef(sidebarItems);
|
||||
const customizer = useCustomizerStore();
|
||||
@@ -21,6 +23,7 @@ watch(mdAndDown, (val) => {
|
||||
<template>
|
||||
<!------Sidebar-------->
|
||||
<v-locale-provider>
|
||||
<AppSnackbar />
|
||||
<v-app
|
||||
:theme="customizer.actTheme"
|
||||
class="bg-containerBg"
|
||||
@@ -47,14 +50,16 @@ watch(mdAndDown, (val) => {
|
||||
<div :class="customizer.boxed ? 'maxWidth' : 'full-header'">
|
||||
<LazyLayoutFullVerticalHeader v-if="!customizer.setHorizontalLayout" />
|
||||
</div>
|
||||
|
||||
<v-main>
|
||||
<v-container fluid class="page-wrapper">
|
||||
<div :class="customizer.boxed ? 'maxWidth' : ''">
|
||||
<RouterView />
|
||||
<AppSnackbar />
|
||||
</div>
|
||||
</v-container>
|
||||
<v-main class="ml-md-4">
|
||||
<div class="rtl-lyt mb-3 hr-layout bg-containerBg">
|
||||
<v-container fluid class="page-wrapper bg-background pt-md-8 rounded-xl">
|
||||
<div>
|
||||
<div class="">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</v-locale-provider>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<script setup lang="ts">
|
||||
import { MailIcon } from "vue-tabler-icons";
|
||||
import { useAuth } from "~/composables/useAuth";
|
||||
import { computed, onMounted } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import DialogConfrim from "~/components/shared/DialogConfrim.vue";
|
||||
|
||||
const auth = useAuth();
|
||||
|
||||
@@ -12,29 +13,39 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Enhanced logout with proper error handling
|
||||
const logout = async () => {
|
||||
await auth.logout();
|
||||
const isLogoutDialogOpen = ref(false);
|
||||
const isFullLogoutDialogOpen = ref(false);
|
||||
const isLoggingOut = ref(false);
|
||||
const isFullLoggingOut = ref(false);
|
||||
|
||||
const openLogoutDialog = () => {
|
||||
isLogoutDialogOpen.value = true;
|
||||
};
|
||||
|
||||
// **TAMBAHAN: Full logout function dengan konfirmasi**
|
||||
const fullLogout = async () => {
|
||||
const confirmed = confirm(
|
||||
"Apakah Anda yakin ingin keluar dari semua sesi? Ini akan menghapus semua data lokal dan sesi Keycloak."
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
await auth.fullLogout();
|
||||
const openFullLogoutDialog = () => {
|
||||
isFullLogoutDialogOpen.value = true;
|
||||
};
|
||||
|
||||
// **TAMBAHAN: Logout dengan konfirmasi untuk UX yang lebih baik**
|
||||
const logoutWithConfirmation = async () => {
|
||||
const confirmed = confirm("Apakah Anda yakin ingin keluar?");
|
||||
const confirmLogout = async () => {
|
||||
if (isLoggingOut.value) return;
|
||||
isLoggingOut.value = true;
|
||||
try {
|
||||
await auth.logout();
|
||||
isLogoutDialogOpen.value = false;
|
||||
} finally {
|
||||
isLoggingOut.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
await logout();
|
||||
const confirmFullLogout = async () => {
|
||||
if (isFullLoggingOut.value) return;
|
||||
isFullLoggingOut.value = true;
|
||||
try {
|
||||
await auth.fullLogout();
|
||||
isFullLogoutDialogOpen.value = false;
|
||||
} finally {
|
||||
isFullLoggingOut.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Get user display info from session
|
||||
@@ -121,7 +132,7 @@ const sessionInfo = computed(() => {
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
block
|
||||
@click="logoutWithConfirmation"
|
||||
@click="openLogoutDialog"
|
||||
prepend-icon="mdi-logout"
|
||||
class="mb-2"
|
||||
>
|
||||
@@ -134,13 +145,35 @@ const sessionInfo = computed(() => {
|
||||
color="error"
|
||||
variant="outlined"
|
||||
block
|
||||
@click="fullLogout"
|
||||
@click="openFullLogoutDialog"
|
||||
prepend-icon="mdi-logout-variant"
|
||||
class="mb-2"
|
||||
>
|
||||
Full Logout
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<DialogConfrim
|
||||
v-model="isLogoutDialogOpen"
|
||||
title="Logout"
|
||||
message="Apakah Anda yakin ingin keluar?"
|
||||
confirm-text="Ya"
|
||||
cancel-text="Tidak"
|
||||
:loading="isLoggingOut"
|
||||
:close-on-confirm="false"
|
||||
@confirm="confirmLogout"
|
||||
/>
|
||||
|
||||
<DialogConfrim
|
||||
v-model="isFullLogoutDialogOpen"
|
||||
title="Full Logout"
|
||||
message="Apakah Anda yakin ingin keluar dari semua sesi? Ini akan menghapus semua data lokal dan sesi Keycloak."
|
||||
confirm-text="Ya"
|
||||
cancel-text="Tidak"
|
||||
:loading="isFullLoggingOut"
|
||||
:close-on-confirm="false"
|
||||
@confirm="confirmFullLogout"
|
||||
/>
|
||||
</v-sheet>
|
||||
</v-menu>
|
||||
</template>
|
||||
@@ -44,14 +44,14 @@ watch(priority, (newPriority) => {
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Search part -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<Searchbar />
|
||||
<!-- <Searchbar /> -->
|
||||
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Mega menu -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<div class="hidden-sm-and-down">
|
||||
<!-- <div class="hidden-sm-and-down">
|
||||
<Navigations />
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<v-spacer class="hidden-sm-and-down" />
|
||||
|
||||
@@ -63,14 +63,14 @@ watch(priority, (newPriority) => {
|
||||
</div>
|
||||
|
||||
|
||||
<ThemeToggler />
|
||||
<!-- <ThemeToggler /> -->
|
||||
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- translate -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<div class="hidden-sm-and-down">
|
||||
<!-- <div class="hidden-sm-and-down">
|
||||
<LanguageDD />
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- ShoppingCart -->
|
||||
@@ -84,9 +84,9 @@ watch(priority, (newPriority) => {
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- Notification -->
|
||||
<!-- ---------------------------------------------- -->
|
||||
<div class="hidden-sm-and-down">
|
||||
<!-- <div class="hidden-sm-and-down">
|
||||
<NotificationDD />
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- ---------------------------------------------- -->
|
||||
<!-- User Profile -->
|
||||
|
||||
@@ -17,349 +17,27 @@ export interface menu {
|
||||
}
|
||||
const sidebarItem: menu[] = [
|
||||
{
|
||||
header: 'dashboards',
|
||||
header: 'Menu',
|
||||
id: 1,
|
||||
children: [
|
||||
{
|
||||
title: 'Dashboard1',
|
||||
title: 'Dashboard',
|
||||
icon: 'widget-add-line-duotone',
|
||||
to: '/dashboards/dashboard1'
|
||||
to: '/apps/dashboard'
|
||||
},
|
||||
{
|
||||
title: 'Dashboard2',
|
||||
icon: 'chart-line-duotone',
|
||||
to: '/dashboards/dashboard2'
|
||||
},
|
||||
{
|
||||
title: 'Dashboard3',
|
||||
icon: 'screencast-2-line-duotone',
|
||||
to: '/dashboards/dashboard3'
|
||||
},
|
||||
{
|
||||
title: 'Front Pages',
|
||||
icon: 'home-angle-linear',
|
||||
title: 'Settings',
|
||||
icon: 'settings-outline',
|
||||
to: '/',
|
||||
children: [
|
||||
{
|
||||
title: 'Homepage',
|
||||
to: '/front-page/homepage'
|
||||
},
|
||||
{
|
||||
title: 'About Us',
|
||||
to: '/front-page/about-us'
|
||||
},
|
||||
{
|
||||
title: 'Blog',
|
||||
to: '/front-page/blog/posts'
|
||||
},
|
||||
{
|
||||
title: 'Blog Details',
|
||||
to: '/front-page/blog/early-black-friday-amazon-deals-cheap-tvs-headphones'
|
||||
},
|
||||
{
|
||||
title: 'Contact Us',
|
||||
to: '/front-page/contact-us'
|
||||
},
|
||||
{
|
||||
title: 'Portfolio',
|
||||
to: '/front-page/portfolio'
|
||||
},
|
||||
{
|
||||
title: 'Pricing',
|
||||
to: '/front-page/pricing'
|
||||
title: 'Menu',
|
||||
to: '/apps/setting/pages',
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
header: 'apps',
|
||||
id: 1,
|
||||
children: [
|
||||
{
|
||||
title: 'ECommerce',
|
||||
icon: 'cart-3-line-duotone',
|
||||
to: '/ecommerce/',
|
||||
children: [
|
||||
{
|
||||
title: 'Shop',
|
||||
to: '/ecommerce/products'
|
||||
},
|
||||
{
|
||||
title: 'Detail',
|
||||
to: '/ecommerce/product/detail/1'
|
||||
},
|
||||
{
|
||||
title: 'List',
|
||||
to: '/ecommerce/productlist'
|
||||
},
|
||||
{
|
||||
title: 'Checkout',
|
||||
to: '/ecommerce/checkout'
|
||||
},
|
||||
{
|
||||
title: 'Add Product',
|
||||
to: '/ecommerce/add-product'
|
||||
},
|
||||
{
|
||||
title: 'Edit Product',
|
||||
to: '/ecommerce/edit-product'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Blog',
|
||||
icon: 'widget-4-line-duotone',
|
||||
to: '/',
|
||||
children: [
|
||||
{
|
||||
title: 'Blog Posts',
|
||||
to: '/apps/blog/posts'
|
||||
},
|
||||
{
|
||||
title: 'Blog Details',
|
||||
to: '/apps/blog/early-black-friday-amazon-deals-cheap-tvs-headphones'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'User Profile',
|
||||
icon: 'shield-user-line-duotone',
|
||||
to: '/',
|
||||
children: [
|
||||
{
|
||||
title: 'Profile',
|
||||
to: '/apps/user/profile'
|
||||
},
|
||||
{
|
||||
title: 'Followers',
|
||||
to: '/apps/user/profile/followers'
|
||||
},
|
||||
{
|
||||
title: 'Friends',
|
||||
to: '/apps/user/profile/friends'
|
||||
},
|
||||
{
|
||||
title: 'Gallery',
|
||||
to: '/apps/user/profile/gallery'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Invoice',
|
||||
icon: 'bill-check-outline',
|
||||
to: '/',
|
||||
children: [
|
||||
{
|
||||
title: 'List',
|
||||
to: '/apps/invoice'
|
||||
},
|
||||
{
|
||||
title: 'Details',
|
||||
to: '/apps/invoice/details/102'
|
||||
},
|
||||
{
|
||||
title: 'Create',
|
||||
to: '/apps/invoice/create'
|
||||
},
|
||||
{
|
||||
title: 'Edit',
|
||||
to: '/apps/invoice/edit/102'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Calendar',
|
||||
icon: 'calendar-mark-line-duotone',
|
||||
to: '/apps/calendar'
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
icon: 'letter-linear',
|
||||
to: '/apps/email'
|
||||
},
|
||||
{
|
||||
title: 'Chats',
|
||||
icon: 'chat-round-line-line-duotone',
|
||||
to: '/apps/chats'
|
||||
},
|
||||
{
|
||||
title: 'Notes',
|
||||
icon: 'document-text-line-duotone',
|
||||
to: '/apps/notes'
|
||||
},
|
||||
{
|
||||
title: 'Kanban',
|
||||
icon: 'airbuds-case-minimalistic-line-duotone',
|
||||
to: '/apps/kanban'
|
||||
},
|
||||
{
|
||||
title: 'Contact',
|
||||
icon: 'iphone-line-duotone',
|
||||
to: '/apps/contacts'
|
||||
},
|
||||
{
|
||||
title: 'Tickets',
|
||||
icon: 'ticker-star-outline',
|
||||
to: '/apps/tickets'
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
header: 'Widgets',
|
||||
id: 2,
|
||||
children: [
|
||||
{
|
||||
title: 'Banners',
|
||||
icon: 'gallery-wide-line-duotone',
|
||||
to: '/template/widgets/banners'
|
||||
},
|
||||
{
|
||||
title: 'Cards',
|
||||
icon: 'layers-minimalistic-line-duotone',
|
||||
to: '/template/widgets/cards'
|
||||
},
|
||||
{
|
||||
title: 'Charts',
|
||||
icon: 'chart-line-duotone',
|
||||
to: '/template/widgets/charts'
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
header: 'UI Components',
|
||||
id: 3,
|
||||
children: [
|
||||
{
|
||||
title: 'Alerts',
|
||||
icon: 'danger-triangle-line-duotone',
|
||||
to: '/template/ui-components/alerts'
|
||||
},
|
||||
{
|
||||
title: 'Avatar',
|
||||
icon: 'user-circle-line-duotone',
|
||||
to: '/template/ui-components/avatar'
|
||||
},
|
||||
{
|
||||
title: 'Buttons',
|
||||
icon: 'ghost-line-duotone',
|
||||
to: '/template/ui-components/buttons'
|
||||
},
|
||||
{
|
||||
title: 'Cards',
|
||||
icon: 'layers-minimalistic-line-duotone',
|
||||
to: '/template/ui-components/cards'
|
||||
},
|
||||
{
|
||||
title: 'Chip',
|
||||
icon: 'tag-horizontal-line-duotone',
|
||||
to: '/template/ui-components/chip'
|
||||
},
|
||||
{
|
||||
title: 'Dialogs',
|
||||
icon: 'window-frame-line-duotone',
|
||||
to: '/template/ui-components/dialogs'
|
||||
},
|
||||
{
|
||||
title: 'Expansion Panel',
|
||||
icon: 'hamburger-menu-line-duotone',
|
||||
to: '/template/ui-components/expansionPanel'
|
||||
},
|
||||
{
|
||||
title: 'List',
|
||||
icon: 'list-line-duotone',
|
||||
to: '/template/ui-components/list'
|
||||
},
|
||||
{
|
||||
title: 'Menus',
|
||||
icon: 'menu-dots-line-duotone',
|
||||
to: '/template/ui-components/menus'
|
||||
},
|
||||
{
|
||||
title: 'Ratting',
|
||||
icon: 'star-line-duotone',
|
||||
to: '/template/ui-components/ratting'
|
||||
},
|
||||
{
|
||||
title: 'Tables',
|
||||
icon: 'tablet-line-duotone',
|
||||
to: '/template/ui-components/tables'
|
||||
},
|
||||
{
|
||||
title: 'Tabs',
|
||||
icon: 'notebook-line-duotone',
|
||||
to: '/template/ui-components/tabs'
|
||||
},
|
||||
{
|
||||
title: 'Tooltip',
|
||||
icon: 'chat-round-dots-line-duotone',
|
||||
to: '/template/ui-components/tooltip'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
header: 'Style Components',
|
||||
id: 3,
|
||||
children: [
|
||||
{
|
||||
title: 'Shadow',
|
||||
icon: 'copy-line-duotone',
|
||||
to: '/template/style-components/shadow'
|
||||
},
|
||||
{
|
||||
title: 'Typography',
|
||||
icon: 'text-bold-circle-line-duotone',
|
||||
to: '/template/style-components/typography'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
header: 'Shared Components',
|
||||
id: 3,
|
||||
children: [
|
||||
{
|
||||
title: 'Overview',
|
||||
icon: 'widget-5-line-duotone',
|
||||
to: '/template/shared-components'
|
||||
},
|
||||
{
|
||||
title: 'UiParentCard & UiChildCard',
|
||||
icon: 'layers-minimalistic-line-duotone',
|
||||
to: '/template/shared-components/UiParentCard'
|
||||
},
|
||||
{
|
||||
title: 'WidgetCard & WidgetCardv2',
|
||||
icon: 'chart-square-line-duotone',
|
||||
to: '/template/shared-components/WidgetCards'
|
||||
},
|
||||
{
|
||||
title: 'Card Components',
|
||||
icon: 'card-2-line-duotone',
|
||||
to: '/template/shared-components/CardComponents'
|
||||
},
|
||||
{
|
||||
title: 'BaseBreadcrumb',
|
||||
icon: 'route-line-duotone',
|
||||
to: '/template/shared-components/BaseBreadcrumb'
|
||||
},
|
||||
{
|
||||
title: 'UiTextfieldPrimary',
|
||||
icon: 'text-field-line-duotone',
|
||||
to: '/template/shared-components/UiTextfieldPrimary'
|
||||
},
|
||||
{
|
||||
title: 'AppBaseCard',
|
||||
icon: 'sidebar-minimalistic-line-duotone',
|
||||
to: '/template/shared-components/AppBaseCard'
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
];
|
||||
|
||||
export default sidebarItem;
|
||||
Reference in New Issue
Block a user