create permission crud

This commit is contained in:
Geriano
2022-07-17 11:00:11 +07:00
parent d8bd54b4cb
commit 10c1fa687a
7 changed files with 362 additions and 18 deletions

View File

@@ -0,0 +1,98 @@
<?php
namespace App\Http\Controllers\Superuser;
use App\Http\Controllers\Controller;
use App\Models\Permission;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Inertia\Inertia;
class PermissionController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return Inertia::render('Superuser/Permission/Index');
}
/**
* @return \Illuminate\Http\Response
*/
public function get()
{
return Permission::get();
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|unique:permissions',
]);
if ($permission = Permission::create([ 'name' => $request->name, 'guard_name' => 'web' ])) {
return redirect()->back()->with('success', __(
'permission `:name` has been created', [
'name' => $permission->name,
]
));
}
return redirect()->back()->with('error', __(
'can\'t create permission'
));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Permission $permission
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Permission $permission)
{
$request->validate([
'name' => ['required', 'string', Rule::unique('permissions')->ignore($permission->id)]
]);
if ($permission->update(['name' => $request->name])) {
return redirect()->back()->with('success', __(
'permission has been updated',
));
}
return redirect()->back()->with('error', __(
'can\'t update permission'
));
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Permission $permission
* @return \Illuminate\Http\Response
*/
public function destroy(Permission $permission)
{
if ($permission->delete()) {
return redirect()->back()->with('success', __(
'permission has been deleted'
));
}
return redirect()->back()->with('error', __(
'can\'t delete permission'
));
}
}

View File

@@ -64,5 +64,7 @@ class Kernel extends HttpKernel
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
];
}

View File

@@ -1,6 +1,6 @@
<script setup>
import { getCurrentInstance, onMounted, ref } from 'vue'
import { Head } from '@inertiajs/inertia-vue3'
import { Head, usePage } from '@inertiajs/inertia-vue3'
import Toggler from '@/Components/DashboardLayout/SidebarToggler.vue'
import TopbarDropdown from '@/Components/DashboardLayout/TopbarDropdown.vue'
import Sidebar from '@/Components/DashboardLayout/Sidebar.vue';
@@ -15,6 +15,17 @@ const open = ref(window.innerWidth > 669)
onMounted(() => window.addEventListener('resize', () => open.value = window.innerWidth > 669))
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: all 300ms ease-in-out;
opacity: 1;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>
<template>
<div class="flex bg-gray-200 dark:bg-gray-900 w-full h-screen font-nunito">
<Head :title="title" />

View File

@@ -0,0 +1,183 @@
<script setup>
import { getCurrentInstance, nextTick, onMounted, onUnmounted, ref } from 'vue'
import { useForm } from '@inertiajs/inertia-vue3'
import axios from 'axios'
import Swal from 'sweetalert2'
import DashboardLayout from '@/Layouts/DashboardLayout.vue'
import Card from '@/Components/Card.vue'
import Icon from '@/Components/Icon.vue'
import { Inertia } from '@inertiajs/inertia'
const self = getCurrentInstance()
const permissions = ref([])
const search = ref('')
const open = ref(false)
const form = useForm({
id: null,
name: '',
})
const fetch = async () => {
try {
const response = await axios.get(route('api.v1.superuser.permission'))
permissions.value = response.data
} catch (e) {
Swal.fire({
title: 'error',
text: `${e}`,
icon: 'error',
})
setTimeout(fetch, 2500)
}
}
fetch()
const show = () => {
open.value = true
nextTick(() => self.refs.name?.focus())
}
const close = () => {
open.value = false
form.reset()
form.clearErrors()
}
const esc = e => {
if (e.key === 'Escape') close()
}
const store = () => form.post(route('superuser.permission.store'), {
onSuccess: () => close() || fetch(),
onError: show,
})
const edit = permission => {
form.id = permission.id
form.name = permission.name
show()
}
const update = () => form.patch(route('superuser.permission.update', form.id), {
onSuccess: () => close() || fetch(),
onError: show,
})
const destroy = async permission => {
const response = await Swal.fire({
title: 'Are you sure?',
text: 'You can\'t restore it after deleted',
icon: 'warning',
showCancelButton: true,
})
if (response.isConfirmed) {
return Inertia.delete(route('superuser.permission.destroy', permission.id), {
onSuccess: fetch,
})
}
}
const submit = () => form.id ? update() : store()
onMounted(() => {
window.addEventListener('keydown', esc)
document.querySelector('[type=search]')?.focus()
})
onUnmounted(() => window.removeEventListener('keydown', esc))
</script>
<style>
.opacity-enter-active, .opacity-leave-active {
transition: all 100ms ease-in-out;
opacity: 1;
}
.opacity-enter-from, .opacity-enter-to {
opacity: 0;
}
</style>
<template>
<DashboardLayout title="Permission">
<Card class="bg-gray-100 dark:bg-slate-700 shadow-md">
<template #header>
<div class="flex items-center space-x-2 bg-gray-800 p-2">
<button @click.prevent="show()" class="bg-green-600 rounded-md px-3 py-1 text-sm text-white">
<div class="flex items-center space-x-1">
<Icon name="plus" />
<p class="font-semibold uppercase">create</p>
</div>
</button>
</div>
</template>
<template #body>
<div class="flex flex-col">
<div class="flex items-center space-x-2 text-sm dark:text-gray-100 px-4 py-2">
<input v-model="search" type="search" class="bg-transparent w-full max-w-sm rounded-md placeholder:capitalize" placeholder="search">
</div>
<div class="flex-wrap px-4 pb-2 dark:bg-gray-700 dark:text-gray-100 rounded-b-md">
<transition-group name="opacity">
<div v-for="(permission, i) in permissions.filter(p => p.name?.toLowerCase().includes(search?.trim().toLowerCase()))" :key="i" class="inline-block dark:bg-gray-800 dark:hover:bg-gray-900 transition-all border dark:border-gray-800 rounded-md m-[2px] px-3 py-1">
<div class="flex items-center space-x-2 text-sm">
<p class="uppercase">{{ permission.name }}</p>
<div class="flex items-center space-x-1">
<Icon @click.prevent="edit(permission)" name="pen" class="px-2 py-1 rounded cursor-pointer bg-blue-600 hover:bg-blue-700 transition-all" />
<Icon @click.prevent="destroy(permission)" name="trash" class="px-2 py-1 rounded cursor-pointer bg-red-600 hover:bg-red-700 transition-all" />
</div>
</div>
</div>
</transition-group>
</div>
</div>
</template>
</Card>
</DashboardLayout>
<transition name="fade">
<div v-if="open" class="fixed top-0 left-0 w-full h-screen flex items-center justify-center bg-black bg-opacity-40">
<form @submit.prevent="submit" class="w-full max-w-xl shadow-xl">
<Card class="dark:bg-gray-700 dark:text-gray-100 border dark:border-gray-700">
<template #header>
<div class="flex items-center space-x-2 justify-end dark:bg-gray-800 dark:text-gray-50 p-2">
<Icon @click.prevent="close" name="times" class="border border-transparent dark:bg-gray-700 px-2 py-1 rounded-md cursor-pointer transition-all dark:hover:bg-gray-800 dark:hover:border-gray-600" />
</div>
</template>
<template #body>
<div class="flex flex-col space-y-4 p-4">
<div class="flex flex-col space-y-2">
<div class="flex items-center space-x-2">
<label for="name" class="lowercase first-letter:capitalize flex-none w-1/4">name</label>
<input ref="name" type="text" v-model="form.name" class="bg-transparent w-full rounded-md px-3 py-1 text-sm placeholder:capitalize" placeholder="name">
</div>
<transition name="fade">
<div v-if="form.errors.name" class="text-red-400 text-sm text-right">{{ form.errors.name }}</div>
</transition>
</div>
</div>
</template>
<template #footer>
<div class="flex items-center space-x-2 justify-end dark:bg-gray-800 dark:text-gray-50 p-2">
<button type="submit" class="bg-green-600 rounded-md px-3 py-1 text-sm transition-all hover:bg-green-700">
<div class="flex items-center space-x-1">
<Icon name="check" />
<p class="uppercase font-semibold">submit</p>
</div>
</button>
</div>
</template>
</Card>
</form>
</div>
</transition>
</template>

View File

@@ -2,11 +2,13 @@ import './bootstrap';
import '../css/app.css';
import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { createInertiaApp, usePage } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import Themes from './themes'
import Swal from 'sweetalert2';
import { Inertia } from '@inertiajs/inertia';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
@@ -27,3 +29,54 @@ createInertiaApp({
});
InertiaProgress.init({ color: '#4B5563' });
window.Swal = Swal
const Toast = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
showCloseButton: true,
timerProgressBar: true,
didOpen: (toast) => {
toast.addEventListener('mouseenter', Swal.stopTimer)
toast.addEventListener('mouseleave', Swal.resumeTimer)
}
})
window.Toast = Toast
Inertia.on('finish', () => {
const { $flash } = usePage().props.value
const { success, error, info, warning } = $flash
if (success) {
Toast.fire({
text: success,
timer: 3000,
icon: 'success',
})
}
if (error) {
Toast.fire({
text: error,
icon: 'error',
})
}
if (info) {
Toast.fire({
text: info,
timer: 3000,
icon: 'info',
})
}
if (warning) {
Toast.fire({
text: warning,
timer: 3000,
icon: 'warning',
})
}
})

View File

@@ -16,4 +16,8 @@ use Illuminate\Support\Facades\Route;
Route::prefix('/v1')->name('api.v1.')->group(function () {
Route::get('/user/{user}/menu', fn (App\Models\User $user) => $user->menus())->name('user.menu');
Route::name('superuser.')->group(function () {
Route::get('/superuser/permission', [App\Http\Controllers\Superuser\PermissionController::class, 'get'])->name('permission');
});
});

View File

@@ -15,21 +15,14 @@ use Inertia\Inertia;
|
*/
Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified'])->group(function () {
Route::get('/', function () {
return Inertia::render('Welcome', [
'canLogin' => Route::has('login'),
'canRegister' => Route::has('register'),
'laravelVersion' => Application::VERSION,
'phpVersion' => PHP_VERSION,
]);
});
Route::middleware([
'auth:sanctum',
config('jetstream.auth_session'),
'verified',
])->group(function () {
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->name('dashboard');
Route::name('superuser.')->group(function () {
Route::resource('permission', App\Http\Controllers\Superuser\PermissionController::class)->only([
'index', 'store', 'update', 'destroy',
]);
});
});