Commit f46013e1 by Samuel Taniel Mulyadi

Merge branch 'sam' into 'staging'

Sam

See merge request !22
2 parents e5f2a991 753145f4
<script setup lang="ts">
import { useKeycloakStore } from "@/@core/stores/keycloakStore";
import { ref } from "vue";
import { ref, computed, onMounted } from "vue";
// Store Keycloak
const keycloakStore = useKeycloakStore();
......@@ -10,6 +11,7 @@ const items = ref<any[]>([]);
const loading = ref(false);
const searchQuery = ref("");
// Header tabel
const logHeaders = [
{ title: "KOLEKSI", key: "koleksi" },
{ title: "TANGGAL PEMINJAMAN", key: "tglpinjam" },
......@@ -17,13 +19,12 @@ const logHeaders = [
{ title: "DIPINJAM", key: "dipinjam" },
{ title: "PERPANJANGAN", key: "perpanjangan" },
{ title: "DENDA", key: "denda" },
{ title: "DENDA TOTAL", key: "dendatotoal" },
{ title: "DENDA TOTAL", key: "dendatotal" },
];
// Fungsi ambil data
async function getData() {
loading.value = true;
items.value = [];
try {
const apiEndpoint = "https://api.ui.ac.id/my/lib";
const response = await fetch(apiEndpoint, {
......@@ -31,16 +32,24 @@ async function getData() {
Authorization: `Bearer ${keycloakStore.accessToken}`,
},
});
if (!response.ok) throw new Error("Gagal mengambil data");
const dataku = await response.json();
// Tambahkan properti tanggal, namaHari, mulai aktual, dan selesai aktual ke setiap item
items.value = dataku.map((item: any) => {
return {
...item,
};
});
// dataku adalah array, tiap elemen punya properti root-level
const requiredKeys = [
"denda",
"dendatotal",
"dipinjam",
"koleksi",
"perpanjangan",
"tglkembali",
"tglpinjam",
"username",
];
// ambil hanya objek yang memiliki semua key di root
items.value = Array.isArray(dataku)
? dataku.filter((r: any) => requiredKeys.every(k => k in r))
: [];
} catch (err) {
console.error("Gagal mengambil data:", err);
} finally {
......@@ -48,22 +57,32 @@ async function getData() {
}
}
// Panggil saat komponen mount
onMounted(() => {
getData();
});
// Filter hasil pencarian
const filteredItems = computed(() => {
if (!searchQuery.value) return items.value;
const query = searchQuery.value.toLowerCase();
return items.value.filter((item) =>
const q = searchQuery.value.toLowerCase();
return items.value.filter(item =>
logHeaders.some(
(header) => item[header.key] && String(item[header.key]).toLowerCase().includes(query)
h => item[h.key] && String(item[h.key]).toLowerCase().includes(q)
)
);
});
</script>
<template>
<VCard title="Status Peminjaman Buku" class="recentnamaHariCard">
<div class="search-container mb-4 pl-2 pr-2">
<AppCardActions
:title="`Status Peminjaman Buku`"
class="jadwalShift"
action-collapsed
action-remove
>
<!-- Search Input -->
<div class="search-container mb-4 pl-2 pr-2">
<VTextField
v-model="searchQuery"
label="Search"
......@@ -76,6 +95,7 @@ const filteredItems = computed(() => {
outlined
/>
</div>
<VDataTable
:headers="logHeaders"
:items="filteredItems"
......@@ -85,5 +105,52 @@ const filteredItems = computed(() => {
:sort-by="['tglpinjam']"
:sort-asc="[true]"
></VDataTable>
</VCard>
</AppCardActions>
</template>
<style lang="scss">
.jadwalShift {
.v-table {
&--density-default {
.v-table__wrapper {
table {
thead {
th {
background-color: #f5f5f5;
border-block-end: 2px solid #ddd;
color: #2c2c2c;
font-weight: 600;
padding-block: 12px;
padding-inline: 1.5em;
}
}
tbody {
tr {
td {
border-block-end: 1px solid #eee;
min-block-size: auto;
padding-block: 8px;
padding-inline: 1em;
vertical-align: top;
&.vti-table__td--Jadwal {
color: #4a4a4a;
font-weight: 500;
}
}
}
}
}
}
}
}
}
.search-container {
display: flex;
justify-content: flex-end;
}
</style>
\ No newline at end of file
<script lang="ts" setup>
import { HorizontalNavLayout } from '@layouts'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useKeycloakStore } from '@/@core/stores/keycloakStore'
import keycloakInstance from '@/keycloak'
import Footer from '@/layouts/components/Footer.vue'
import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue'
import NavbarTokenExpiredTime from '@/layouts/components/NavbarTokenExpiredTime.vue'
import UserProfile from '@/layouts/components/UserProfile.vue'
import navItems from '@/navigation/horizontal'
import { HorizontalNavLayout } from '@layouts'
// Keycloak & state
const keycloakStore = useKeycloakStore()
const authenticated = computed(() => keycloakStore.authenticated)
const now = ref(Math.floor(Date.now() / 1000))
const tokenLifetime = ref(0)
let timer: ReturnType<typeof setInterval>
onMounted(() => {
// Set tokenLifetime only once on mount
if (keycloakInstance.tokenParsed?.exp && keycloakInstance.tokenParsed?.iat)
tokenLifetime.value = keycloakInstance.tokenParsed.exp - keycloakInstance.tokenParsed.iat
// Start timer
timer = setInterval(() => {
now.value = Math.floor(Date.now() / 1000)
}, 1000)
})
onUnmounted(() => {
clearInterval(timer)
})
// Computed expiration values
const computedExpIn = computed(() => {
return authenticated.value && keycloakInstance.tokenParsed?.exp
? Math.max(keycloakInstance.tokenParsed?.exp - now.value, 0)
: 0
})
</script>
<template>
......@@ -77,23 +44,7 @@ const computedExpIn = computed(() => {
<NavbarThemeSwitcher />
<!-- <NavbarShortcuts /> -->
<!-- <NavBarNotifications class="me-2" /> -->
<!--
<div class="pa-4">
<VAlert
v-if="authenticated"
type="info"
border="start"
variant="outlined"
class="mb-4"
>
<h3 class="text-h6 font-weight-bold">
Access token expires in {{ computedExpIn }} sec,
Refresh token in {{ computedRefExpIn }} sec,
Session: {{ computedSessionTimer }}
</h3>
</VAlert>
</div>
-->
<UserProfile />
</template>
......
import { defineNuxtRouteMiddleware, navigateTo } from "nuxt/app"
import { useKeycloakStore } from "~/@core/stores/keycloakStore"
import { defineNuxtRouteMiddleware, navigateTo } from 'nuxt/app'
import { useKeycloakStore } from '~/@core/stores/keycloakStore'
export default defineNuxtRouteMiddleware((to) => {
export default defineNuxtRouteMiddleware(to => {
const keycloakStore = useKeycloakStore()
if (process.client) {
const isLoginPage = to.path === '/login'
// Jika belum login dan bukan sedang menuju halaman login, redirect ke login
if (!keycloakStore.authenticated && !isLoginPage) {
if (!keycloakStore.authenticated && !isLoginPage)
return navigateTo('/login')
}
// Kalau sudah login dan mencoba ke /login, bisa arahkan ke dashboard atau home
if (keycloakStore.authenticated && isLoginPage) {
return navigateTo('/') // atau ke '/dashboard' misalnya
}
if (keycloakStore.authenticated && isLoginPage)
return navigateTo('/profile') // atau ke '/dashboard' misalnya
}
})
\ No newline at end of file
})
......@@ -4,9 +4,18 @@ import keycloakInstance from '@/keycloak'
export default defineNuxtPlugin(async nuxtApp => {
const keycloakStore = useKeycloakStore()
/* `const { } = nuxtApp` is a destructuring assignment in JavaScript/TypeScript. In this
case, it is extracting the `` property from the `nuxtApp` object and assigning it to a new
constant named ``. This allows you to access the `` object directly without having
to use `nuxtApp.` every time. It's a shorthand way of accessing nested properties in
objects. */
// const { $router } = nuxtApp
try {
const authenticated = await keycloakInstance.init({
onLoad: 'check-sso',
checkLoginIframe: true,
checkLoginIframeInterval: 5, // in seconds, default is 5
})
keycloakStore.authenticated = authenticated
......@@ -14,7 +23,9 @@ export default defineNuxtPlugin(async nuxtApp => {
if (authenticated) {
keycloakStore.refresh()
console.log('User is authenticated')
navigateTo('/profile')
// $router.push('/profile')
// console.log('Suppose to navigate To')
setInterval(() => {
const now = Math.floor(Date.now() / 1000)
......@@ -25,13 +36,14 @@ export default defineNuxtPlugin(async nuxtApp => {
keycloakInstance.logout({ redirectUri: `${window.location.origin}/login` })
}
else {
console.log('Token expires in:', tokenExp - now, 'seconds')
// console.log('Token expires in:', tokenExp - now, 'seconds')
}
}, 10_000)
}
else {
console.log('User is not authenticated')
navigateTo('/login')
// $router.push('/login')
}
}
catch (error) {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!