adding counter on menu
This commit is contained in:
@@ -3,13 +3,18 @@
|
|||||||
namespace App\Http\Controllers\Superuser;
|
namespace App\Http\Controllers\Superuser;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Menus\Menu as MenusMenu;
|
||||||
use App\Models\Menu;
|
use App\Models\Menu;
|
||||||
use App\Models\Permission;
|
use App\Models\Permission;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
use RecursiveDirectoryIterator;
|
||||||
|
use RecursiveIteratorIterator;
|
||||||
use SplFileInfo;
|
use SplFileInfo;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
@@ -29,13 +34,55 @@ class MenuController extends Controller
|
|||||||
'routes' => array_values($routes),
|
'routes' => array_values($routes),
|
||||||
'icons' => $this->icons(),
|
'icons' => $this->icons(),
|
||||||
'menus' => $this->get(),
|
'menus' => $this->get(),
|
||||||
|
'handlers' => $this->handlers(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function icons()
|
private function handlers() : array
|
||||||
|
{
|
||||||
|
$handlers = [];
|
||||||
|
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(
|
||||||
|
app_path('Menus')
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($iterator as $splFileInfo) {
|
||||||
|
/**
|
||||||
|
* @var \SplFileInfo $splFileInfo
|
||||||
|
*/
|
||||||
|
if ($splFileInfo->isDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($splFileInfo->getFilename() === 'Menu.php') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$class = str_replace(dirname(app_path()), '', $splFileInfo->getRealPath());
|
||||||
|
$class = mb_substr(trim($class, DIRECTORY_SEPARATOR), 0, -4);
|
||||||
|
$class = str_replace(DIRECTORY_SEPARATOR, '\\', ucfirst($class));
|
||||||
|
|
||||||
|
if (!class_exists($class) && !is_subclass_of($class, MenusMenu::class)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = str_replace(['App\\Menus\\', '\\'], ['', ' '], $class);
|
||||||
|
|
||||||
|
$handlers[] = [
|
||||||
|
'class' => $class,
|
||||||
|
'name' => ucwords(Str::snake($name, ' ')),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function icons() : array
|
||||||
{
|
{
|
||||||
return array_map(
|
return array_map(
|
||||||
callback: fn (string $name) => mb_substr(
|
callback: fn (string $name) => mb_substr(
|
||||||
@@ -78,14 +125,24 @@ class MenuController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$post = $request->validate([
|
Validator::make($request->all(), [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'icon' => 'nullable|string',
|
'icon' => 'nullable|string',
|
||||||
'route_or_url' => 'nullable|string',
|
'route_or_url' => 'nullable|string',
|
||||||
|
'counter_handler' => 'nullable|string',
|
||||||
'actives.*' => 'nullable|string',
|
'actives.*' => 'nullable|string',
|
||||||
'permissions.*' => 'nullable|integer|exists:permissions,id',
|
'permissions.*' => 'nullable|integer|exists:permissions,id',
|
||||||
]);
|
])->after(function ($validator) use ($request) {
|
||||||
|
if (($class = $request->counter_handler) && !class_exists($class)) {
|
||||||
|
$validator->errors()->add('counter_handler', __(
|
||||||
|
'class :name doesn\'t exists', [
|
||||||
|
'name' => $class,
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
})->validate();
|
||||||
|
|
||||||
|
$post = $request->all();
|
||||||
$post['position'] = Menu::whereNull('parent_id')->count() + 1;
|
$post['position'] = Menu::whereNull('parent_id')->count() + 1;
|
||||||
|
|
||||||
if ($menu = Menu::create($post)) {
|
if ($menu = Menu::create($post)) {
|
||||||
@@ -112,13 +169,22 @@ class MenuController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function update(Request $request, Menu $menu)
|
public function update(Request $request, Menu $menu)
|
||||||
{
|
{
|
||||||
$request->validate([
|
Validator::make($request->all(), [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'icon' => 'nullable|string',
|
'icon' => 'nullable|string',
|
||||||
'route_or_url' => 'nullable|string',
|
'route_or_url' => 'nullable|string',
|
||||||
|
'counter_handler' => 'nullable|string',
|
||||||
'actives.*' => 'nullable|string',
|
'actives.*' => 'nullable|string',
|
||||||
'permissions.*' => 'nullable|integer|exists:permissions,id',
|
'permissions.*' => 'nullable|integer|exists:permissions,id',
|
||||||
]);
|
])->after(function ($validator) use ($request) {
|
||||||
|
if (($class = $request->counter_handler) && !class_exists($class)) {
|
||||||
|
$validator->errors()->add('counter_handler', __(
|
||||||
|
'class :name doesn\'t exists', [
|
||||||
|
'name' => $class,
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
})->validate();
|
||||||
|
|
||||||
if ($menu->update($request->all())) {
|
if ($menu->update($request->all())) {
|
||||||
$menu->permissions()->sync($request->input('permissions', []));
|
$menu->permissions()->sync($request->input('permissions', []));
|
||||||
@@ -251,4 +317,27 @@ class MenuController extends Controller
|
|||||||
return $new;
|
return $new;
|
||||||
}, $menus);
|
}, $menus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \App\Models\Menu $menu
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function counter(Menu $menu)
|
||||||
|
{
|
||||||
|
$menu = Menu::with('childs')->find($menu->id);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'count' => $this->counters(0, $menu),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $total
|
||||||
|
* @param \App\Models\Menu $menu
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function counters(int $total, Menu $menu)
|
||||||
|
{
|
||||||
|
return $total + $menu->counter?->count() + $menu->childs->reduce([$this, 'counters'], 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
app/Menus/Menu.php
Normal file
13
app/Menus/Menu.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Menus;
|
||||||
|
|
||||||
|
use Countable;
|
||||||
|
|
||||||
|
abstract class Menu implements Countable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
abstract public function count() : int;
|
||||||
|
}
|
||||||
16
app/Menus/UserMenu.php
Normal file
16
app/Menus/UserMenu.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Menus;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UserMenu extends Menu
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function count() : int
|
||||||
|
{
|
||||||
|
return User::count();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ class Menu extends Model
|
|||||||
'name',
|
'name',
|
||||||
'icon',
|
'icon',
|
||||||
'route_or_url',
|
'route_or_url',
|
||||||
|
'counter_handler',
|
||||||
'position',
|
'position',
|
||||||
'enable',
|
'enable',
|
||||||
'actives',
|
'actives',
|
||||||
@@ -34,6 +35,13 @@ class Menu extends Model
|
|||||||
'permissions',
|
'permissions',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $appends = [
|
||||||
|
'counter',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||||
*/
|
*/
|
||||||
@@ -88,4 +96,14 @@ class Menu extends Model
|
|||||||
set: fn ($value) => is_array($value) || $value instanceof JsonSerializable || $value instanceof stdClass ? json_encode($value) : $value,
|
set: fn ($value) => is_array($value) || $value instanceof JsonSerializable || $value instanceof stdClass ? json_encode($value) : $value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Casts\Attribute
|
||||||
|
*/
|
||||||
|
public function counter() : Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn () => $this->counter_handler ? new $this->counter_handler : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('menus', function (Blueprint $table) {
|
||||||
|
$table->after('route_or_url', function (Blueprint $table) {
|
||||||
|
$table->string('counter_handler', 512)
|
||||||
|
->nullable()
|
||||||
|
->default(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('menus', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('counter_handler');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,15 +1,51 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { getCurrentInstance } from 'vue'
|
import { getCurrentInstance, nextTick, onMounted, onUpdated, ref } from 'vue'
|
||||||
import { Link } from '@inertiajs/inertia-vue3'
|
import { Link } from '@inertiajs/inertia-vue3'
|
||||||
import Icon from '@/Components/Icon.vue'
|
import Icon from '@/Components/Icon.vue'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
const { menu } = defineProps({
|
const { menu } = defineProps({
|
||||||
menu: Object,
|
menu: Object,
|
||||||
padding: Number,
|
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 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 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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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="`${themes().get('sidebar', 'bg-slate-700 text-gray-200')} ${active && 'bg-slate-800'} pl-${padding !== 0 && padding}`"
|
||||||
class="w-full px-4 py-3"
|
class="w-full px-4 py-3"
|
||||||
>
|
>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<Icon :name="menu.icon" />
|
<Icon :name="menu.icon" />
|
||||||
<p class="uppercase font-semibold">{{ __(menu.name) }}</p>
|
<p class="uppercase font-semibold">{{ __(menu.name) }}</p>
|
||||||
</div>
|
</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>
|
</Link>
|
||||||
</template>
|
</template>
|
||||||
@@ -8,6 +8,8 @@ const { menu, childs } = defineProps({
|
|||||||
padding: Number,
|
padding: Number,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const counter = ref(0)
|
||||||
|
const element = ref(null)
|
||||||
const trace = menu => {
|
const trace = menu => {
|
||||||
if (menu.childs?.length) {
|
if (menu.childs?.length) {
|
||||||
for (const child of menu.childs) {
|
for (const child of menu.childs) {
|
||||||
@@ -23,6 +25,34 @@ const trace = menu => {
|
|||||||
const active = childs.find(trace)
|
const active = childs.find(trace)
|
||||||
const self = getCurrentInstance()
|
const self = getCurrentInstance()
|
||||||
const open = ref(active ? true : false)
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -51,8 +81,16 @@ const open = ref(active ? true : false)
|
|||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<Icon :name="menu.icon" />
|
<Icon :name="menu.icon" />
|
||||||
<p class="uppercase font-semibold w-full text-left">{{ __(menu.name) }}</p>
|
<p class="uppercase font-semibold w-full text-left">{{ __(menu.name) }}</p>
|
||||||
|
|
||||||
|
<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'" />
|
<Icon name="caret-left" class="transition-all ease-in-out duration-150" :class="open && '-rotate-90'" />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Transition name="slide-fade" mode="in-out">
|
<Transition name="slide-fade" mode="in-out">
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const props = defineProps({
|
|||||||
routes: Array,
|
routes: Array,
|
||||||
icons: Array,
|
icons: Array,
|
||||||
menus: Array,
|
menus: Array,
|
||||||
|
handlers: Array,
|
||||||
})
|
})
|
||||||
|
|
||||||
const menus = ref(props.menus || [])
|
const menus = ref(props.menus || [])
|
||||||
@@ -31,6 +32,7 @@ const form = useForm({
|
|||||||
name: '',
|
name: '',
|
||||||
icon: 'circle',
|
icon: 'circle',
|
||||||
route_or_url: '',
|
route_or_url: '',
|
||||||
|
counter_handler: null,
|
||||||
actives: [],
|
actives: [],
|
||||||
permissions: [],
|
permissions: [],
|
||||||
})
|
})
|
||||||
@@ -83,6 +85,7 @@ const edit = menu => {
|
|||||||
form.name = menu.name
|
form.name = menu.name
|
||||||
form.icon = menu.icon
|
form.icon = menu.icon
|
||||||
form.route_or_url = menu.route_or_url
|
form.route_or_url = menu.route_or_url
|
||||||
|
form.counter_handler = menu.counter_handler
|
||||||
form.actives = menu.actives
|
form.actives = menu.actives
|
||||||
form.permissions = menu.permissions.map(p => p.id)
|
form.permissions = menu.permissions.map(p => p.id)
|
||||||
|
|
||||||
@@ -230,6 +233,29 @@ onUnmounted(() => window.removeEventListener('keydown', esc))
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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 flex-col space-y-1">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<label for="actives" class="lowercase first-letter:capitalize w-1/3">
|
<label for="actives" class="lowercase first-letter:capitalize w-1/3">
|
||||||
@@ -324,25 +350,23 @@ onUnmounted(() => window.removeEventListener('keydown', esc))
|
|||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal :show="icon">
|
<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>
|
<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">
|
<div class="flex items-center space-x-2 p-2 justify-end bg-gray-200 dark:bg-gray-800">
|
||||||
<input
|
<Input
|
||||||
v-model="search"
|
v-model="search"
|
||||||
:placeholder="__('search something')"
|
:placeholder="__('search something')"
|
||||||
type="search"
|
type="search"
|
||||||
class="py-1 w-full bg-white dark:bg-transparent rounded-md text-sm uppercase"
|
class="py-1 w-full bg-white dark:bg-transparent rounded-md text-sm uppercase"
|
||||||
>
|
autofocus
|
||||||
<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"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Close @click.prevent="icon = false" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex-wrap p-4">
|
<div class="flex-wrap p-4 max-h-96 overflow-auto">
|
||||||
<Icon
|
<Icon
|
||||||
v-for="(icx, i) in icons.filter(icx => icx.includes(search.trim().toLocaleLowerCase()))"
|
v-for="(icx, i) in icons.filter(icx => icx.includes(search.trim().toLocaleLowerCase()))"
|
||||||
:key="i"
|
:key="i"
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified']
|
|||||||
Route::resource('menu', App\Http\Controllers\Superuser\MenuController::class)->only([
|
Route::resource('menu', App\Http\Controllers\Superuser\MenuController::class)->only([
|
||||||
'index', 'store', 'update', 'destroy',
|
'index', 'store', 'update', 'destroy',
|
||||||
])->middleware(['permission:read menu']);
|
])->middleware(['permission:read menu']);
|
||||||
|
Route::get('/menu/{menu}/counter', [App\Http\Controllers\Superuser\MenuController::class, 'counter'])->name('menu.counter');
|
||||||
|
|
||||||
Route::prefix('/translation')->name('translation.')->controller(App\Http\Controllers\TranslationController::class)->group(function () {
|
Route::prefix('/translation')->name('translation.')->controller(App\Http\Controllers\TranslationController::class)->group(function () {
|
||||||
Route::get('/', 'index')->name('index');
|
Route::get('/', 'index')->name('index');
|
||||||
|
|||||||
Reference in New Issue
Block a user