adding counter on menu
This commit is contained in:
@@ -1,15 +1,51 @@
|
||||
<script setup>
|
||||
import { getCurrentInstance } from 'vue'
|
||||
import { getCurrentInstance, nextTick, onMounted, onUpdated, ref } from 'vue'
|
||||
import { Link } from '@inertiajs/inertia-vue3'
|
||||
import Icon from '@/Components/Icon.vue'
|
||||
import axios from 'axios'
|
||||
|
||||
const { menu } = defineProps({
|
||||
menu: Object,
|
||||
padding: Number,
|
||||
})
|
||||
|
||||
const element = ref(null)
|
||||
|
||||
const counter = ref(null)
|
||||
const active = route().current(menu.route_or_url) || menu.actives.filter(active => route().current(active)).length > 0
|
||||
const link = route().has(menu.route_or_url) ? route(menu.route_or_url) : menu.route_or_url
|
||||
const resize = () => {
|
||||
if (!element.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const width = element.value.clientWidth
|
||||
const height = element.value.clientHeight
|
||||
|
||||
if (width != height) {
|
||||
const max = Math.max(width, height)
|
||||
element.value.style.width = `${max}px`
|
||||
element.value.style.height = `${max}px`
|
||||
}
|
||||
}
|
||||
|
||||
const fetch = async () => {
|
||||
if (!menu.counter_handler) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(route(`superuser.menu.counter`, menu.id))
|
||||
counter.value = data.count
|
||||
} catch (e) {
|
||||
element.value.innerHTML = `<i class="fa fa-times"></i>`
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(resize)
|
||||
onUpdated(resize)
|
||||
onMounted(fetch)
|
||||
onUpdated(fetch)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -18,9 +54,15 @@ const link = route().has(menu.route_or_url) ? route(menu.route_or_url) : menu.ro
|
||||
:class="`${themes().get('sidebar', 'bg-slate-700 text-gray-200')} ${active && 'bg-slate-800'} pl-${padding !== 0 && padding}`"
|
||||
class="w-full px-4 py-3"
|
||||
>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Icon :name="menu.icon" />
|
||||
<p class="uppercase font-semibold">{{ __(menu.name) }}</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Icon :name="menu.icon" />
|
||||
<p class="uppercase font-semibold">{{ __(menu.name) }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="menu.counter_handler && counter !== null" ref="element" class="flex-none flex items-center justify-center min-w-[1.5rem] min-h-[1.5rem] rounded-full bg-red-500 text-white text-xs p-1 transition-all">
|
||||
<p>{{ counter }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</template>
|
||||
@@ -8,6 +8,8 @@ const { menu, childs } = defineProps({
|
||||
padding: Number,
|
||||
})
|
||||
|
||||
const counter = ref(0)
|
||||
const element = ref(null)
|
||||
const trace = menu => {
|
||||
if (menu.childs?.length) {
|
||||
for (const child of menu.childs) {
|
||||
@@ -23,6 +25,34 @@ const trace = menu => {
|
||||
const active = childs.find(trace)
|
||||
const self = getCurrentInstance()
|
||||
const open = ref(active ? true : false)
|
||||
const fetch = async () => {
|
||||
try {
|
||||
const { data } = await axios.get(route(`superuser.menu.counter`, menu.id))
|
||||
counter.value = data.count
|
||||
} catch (e) {
|
||||
element.value.innerHTML = `<i class="fa fa-times"></i>`
|
||||
}
|
||||
}
|
||||
|
||||
const resize = () => {
|
||||
if (!element.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const width = element.value.clientWidth
|
||||
const height = element.value.clientHeight
|
||||
|
||||
if (width != height) {
|
||||
const max = Math.max(width, height)
|
||||
element.value.style.width = `${max}px`
|
||||
element.value.style.height = `${max}px`
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(resize)
|
||||
onMounted(fetch)
|
||||
onUpdated(resize)
|
||||
onUpdated(fetch)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -51,7 +81,15 @@ const open = ref(active ? true : false)
|
||||
<div class="flex items-center space-x-2">
|
||||
<Icon :name="menu.icon" />
|
||||
<p class="uppercase font-semibold w-full text-left">{{ __(menu.name) }}</p>
|
||||
<Icon name="caret-left" class="transition-all ease-in-out duration-150" :class="open && '-rotate-90'" />
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<div v-if="counter > 0" ref="element" class="flex items-center justify-center bg-red-500 text-white text-center rounded-full p-1">
|
||||
<p class="text-xs">
|
||||
{{ counter }}
|
||||
</p>
|
||||
</div>
|
||||
<Icon name="caret-left" class="transition-all ease-in-out duration-150" :class="open && '-rotate-90'" />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ const props = defineProps({
|
||||
routes: Array,
|
||||
icons: Array,
|
||||
menus: Array,
|
||||
handlers: Array,
|
||||
})
|
||||
|
||||
const menus = ref(props.menus || [])
|
||||
@@ -31,6 +32,7 @@ const form = useForm({
|
||||
name: '',
|
||||
icon: 'circle',
|
||||
route_or_url: '',
|
||||
counter_handler: null,
|
||||
actives: [],
|
||||
permissions: [],
|
||||
})
|
||||
@@ -83,6 +85,7 @@ const edit = menu => {
|
||||
form.name = menu.name
|
||||
form.icon = menu.icon
|
||||
form.route_or_url = menu.route_or_url
|
||||
form.counter_handler = menu.counter_handler
|
||||
form.actives = menu.actives
|
||||
form.permissions = menu.permissions.map(p => p.id)
|
||||
|
||||
@@ -230,6 +233,29 @@ onUnmounted(() => window.removeEventListener('keydown', esc))
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<label for="counter_handler" class="lowercase first-letter:capitalize w-1/3">
|
||||
{{ __('counter handler') }}
|
||||
</label>
|
||||
|
||||
<Select
|
||||
v-model="form.counter_handler"
|
||||
:options="handlers.map(handler => ({
|
||||
label: handler.name,
|
||||
value: handler.class,
|
||||
}))"
|
||||
:searchable="true"
|
||||
:placeholder="__('Counter Handler')"
|
||||
ref="counter_handler"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<InputError
|
||||
:error="form.errors.counter_handler"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<label for="actives" class="lowercase first-letter:capitalize w-1/3">
|
||||
@@ -324,25 +350,23 @@ onUnmounted(() => window.removeEventListener('keydown', esc))
|
||||
</Modal>
|
||||
|
||||
<Modal :show="icon">
|
||||
<Card class="bg-gray-50 dark:bg-gray-700 dark:text-gray-100 w-full max-w-xl sm:max-w-5xl max-h-96 overflow-auto">
|
||||
<Card class="bg-gray-50 dark:bg-gray-700 dark:text-gray-100 w-full max-w-xl sm:max-w-5xl h-fit">
|
||||
<template #header>
|
||||
<div class="flex items-center space-x-2 p-2 justify-end bg-gray-200 dark:bg-gray-800 sticky top-0 left-0">
|
||||
<input
|
||||
<div class="flex items-center space-x-2 p-2 justify-end bg-gray-200 dark:bg-gray-800">
|
||||
<Input
|
||||
v-model="search"
|
||||
:placeholder="__('search something')"
|
||||
type="search"
|
||||
class="py-1 w-full bg-white dark:bg-transparent rounded-md text-sm uppercase"
|
||||
>
|
||||
<Icon
|
||||
@click.prevent="icon = false"
|
||||
name="times"
|
||||
class="px-2 py-1 bg-gray-300 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 rounded-md transition-all cursor-pointer"
|
||||
autofocus
|
||||
/>
|
||||
|
||||
<Close @click.prevent="icon = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<div class="flex-wrap p-4">
|
||||
<div class="flex-wrap p-4 max-h-96 overflow-auto">
|
||||
<Icon
|
||||
v-for="(icx, i) in icons.filter(icx => icx.includes(search.trim().toLocaleLowerCase()))"
|
||||
:key="i"
|
||||
|
||||
Reference in New Issue
Block a user