crud role and type

twopenny
ajikamaludin 2 years ago
parent 427f1e9ed6
commit 3582ba5433

@ -0,0 +1,75 @@
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use Illuminate\Http\Request;
class CategoryController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return inertia('Category/Index', [
'categories' => Category::paginate(),
]);
}
/**
* 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|max:255',
'short' => 'required|string|max:255',
'duration' => 'required|numeric'
]);
Category::create([
'name' => $request->name,
'short' => $request->short,
'duration' => $request->duration,
]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Category $category)
{
$request->validate([
'name' => 'required|string|max:255',
'short' => 'required|string|max:255',
'duration' => 'required|numeric'
]);
$category->update([
'name' => $request->name,
'short' => $request->short,
'duration' => $request->duration,
]);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Category $category)
{
$category->delete();
}
}

@ -11,10 +11,10 @@ class GeneralController extends Controller
public function index()
{
return inertia('Dashboard', [
'count_active' => Document::where('status', Document::ACTIVE)->count(),
'count_update' => Document::where('status', Document::UPDATE)->count(),
'count_expired' => Document::where('status', Document::EXPIRED)->count(),
'count_total' => Document::count(),
'count_active' => 0,
'count_update' => 0,
'count_expired' => 0,
'count_total' => 0,
'events' => DocumentReminder::with('document.type')->get(),
]);
}

@ -0,0 +1,63 @@
<?php
namespace App\Http\Controllers;
use App\Models\Type;
use Illuminate\Http\Request;
class TypeController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return inertia('Type/Index', [
'types' => Type::paginate(),
]);
}
/**
* 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|max:255'
]);
Type::create(['name' => $request->name]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Type $type)
{
$request->validate([
'name' => 'required|string|max:255'
]);
$type->update(['name' => $request->name]);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Type $type)
{
$type->delete();
}
}

@ -0,0 +1,17 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
protected $fillable = [
"name",
"short",
"duration",
];
}

@ -10,53 +10,29 @@ class Document extends Model
use HasFactory;
protected $fillable = [
'no',
'no_doc',
'type_doc_id',
'company_name',
'first_person_name',
'second_person_name',
'start_date',
'end_date',
'department_id',
'pic_name',
'email',
'note',
'document',
'status',
'user_id',
'name',
"no",
"no_doc",
"name",
"company_name",
"type_id",
"category_id",
"publisher",
"description",
"publish_date",
"due_date",
"status",
"type",
"group",
"region",
"document",
"user_id",
];
protected $casts = [
'start_date' => 'datetime:Y-m-d',
'end_date' => 'datetime:Y-m-d'
'publish_date' => 'datetime:Y-m-d',
'due_date' => 'datetime:Y-m-d'
];
public const ACTIVE = 0;
public const UPDATE = 1;
public const EXPIRED = 2;
public function department()
{
return $this->belongsTo(Department::class, 'department_id');
}
public function type()
{
return $this->belongsTo(TypeDoc::class, 'type_doc_id');
}
public function reminders()
{
return $this->hasMany(DocumentReminder::class);
}
public function shares()
{
return $this->hasMany(DocumentShare::class);
}
public function creator()
{
return $this->belongsTo(User::class, 'user_id');

@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Type extends Model
{
use HasFactory;
protected $fillable = [
"name",
];
}

@ -14,22 +14,22 @@ return new class extends Migration {
{
Schema::create('documents', function (Blueprint $table) {
$table->id();
$table->integer('no', false);
$table->string('name');
$table->string('no_doc');
$table->foreignId('type_doc_id')->constrained();
$table->string('company_name');
$table->string('first_person_name');
$table->string('second_person_name');
$table->dateTime('start_date');
$table->dateTime('end_date');
$table->foreignId('department_id')->constrained();
$table->string('pic_name');
$table->string('email');
$table->text('note');
$table->string('document');
$table->foreignId('user_id')->constrained();
$table->smallInteger('status')->default(0);
$table->string("no")->nullable();
$table->string("no_doc")->nullable();
$table->string("name")->nullable();
$table->string("company_name")->nullable();
$table->foreignId("type_id")->constrained(); //select jenis
$table->foreignId("category_id")->constrained(); //select
$table->string("publisher")->nullable();
$table->text("description")->nullable();
$table->timestamp("publish_date")->nullable();
$table->timestamp("due_date")->nullable(); //for reminder
$table->smallInteger("status")->default(1); //only 1 yes/ 0no
$table->smallInteger("type")->default(1); //only 1 tetap/ 0tidak tetap
$table->string("group")->nullable();
$table->string("region")->nullable();
$table->string("document")->nullable();
$table->foreignId("user_id")->constrained();
$table->timestamps();
});
}

@ -0,0 +1,32 @@
<?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::create('types', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('types');
}
};

@ -0,0 +1,34 @@
<?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::create('categories', function (Blueprint $table) {
$table->id();
$table->string("name");
$table->string("short");
$table->integer("duration");
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categories');
}
};

@ -10,8 +10,8 @@ const rs = [
{name: "Dashboard", route: "dashboard", show: true},
{name: "Dokumen", show: true, items: [
{name: "Dokumen", route: 'docs.index', show: true, permission: 'view-document'},
{name: "Ketegori", route: 'docs.index', show: true, permission: 'view-category'},
{name: "Jenis", route: 'docs.index', show: true, permission: 'view-type'},
{name: "Ketegori", route: 'categories.index', show: true, permission: 'view-category'},
{name: "Jenis", route: 'types.index', show: true, permission: 'view-type'},
]},
{name: "User", show: true, items: [
{name:"User", route: "users.index", show: true, permission: 'view-user'},
@ -51,7 +51,7 @@ export default function Authenticated({ auth, children, flash, notify }) {
<a className="text-xl font-bold">Monitor Doc</a>
</div>
<div className="sm:flex flex-1 px-6 hidden">
<div className="flex items-stretch">
<div className="flex items-stretch gap-2">
{routes.filter(r => r.show).map((item, index) => (
<div key={index}>
{'items' in item ? (

@ -0,0 +1,153 @@
import React, { useEffect } from 'react'
import { useForm, usePage } from '@inertiajs/react'
import { toast } from 'react-toastify'
export default function FormModal(props) {
const { modalState } = props
const { data, setData, post, put, processing, errors, reset, clearErrors } = useForm({
name: '',
short: '',
duration: 0
})
const handleOnChange = (event) => {
setData(event.target.name, event.target.type === 'checkbox' ? event.target.checked : event.target.value);
}
const handleReset = () => {
reset()
clearErrors()
modalState.setData(null)
}
const handleCancel = () => {
handleReset()
modalState.toggle()
}
const handleSubmit = () => {
const category = modalState.data
if(category !== null) {
put(route('categories.update', category), {
onSuccess: () =>
Promise.all([
handleReset(),
modalState.toggle(),
toast.success('The Data has been changed'),
]),
})
return
}
post(route('categories.store'), {
onSuccess: () =>
Promise.all([
handleReset(),
modalState.toggle(),
toast.success('The Data has been saved'),
]),
})
}
useEffect(() => {
const category = modalState.data
if (category !== null) {
setData({
name: category?.name,
short: category?.short,
duration: category?.duration,
})
}
}, [modalState])
return (
<div
className="modal modal-bottom sm:modal-middle pb-10"
style={
modalState.isOpen
? {
opacity: 1,
pointerEvents: 'auto',
visibility: 'visible',
overflowY: 'initial',
}
: {}
}
>
<div className="modal-box overflow-y-auto max-h-screen">
<h1 className="font-bold text-2xl pb-8">Ketegori</h1>
<div className="form-control">
<label className="label">
<span className="label-text font-semibold">Nama</span>
</label>
<input
type="text"
placeholder="nama"
className={`input input-bordered ${
errors.name && 'input-error'
}`}
name="name"
value={data.name}
onChange={handleOnChange}
/>
<label className="label">
<span className="label-text-alt text-red-600">{errors.name}</span>
</label>
</div>
<div className="form-control">
<label className="label">
<span className="label-text font-semibold">Singkatan</span>
</label>
<input
type="text"
placeholder="singkatan"
className={`input input-bordered ${
errors.short && 'input-error'
}`}
name="short"
value={data.short}
onChange={handleOnChange}
/>
<label className="label">
<span className="label-text-alt text-red-600">{errors.short}</span>
</label>
</div>
<div className="form-control">
<label className="label">
<span className="label-text font-semibold">Durasi</span>
</label>
<input
type="number"
placeholder="nama"
className={`input input-bordered ${
errors.duration && 'input-error'
}`}
name="duration"
value={data.duration}
onChange={handleOnChange}
/>
<label className="label">
<span className="label-text-alt text-red-600">{errors.duration}</span>
</label>
</div>
<div className="modal-action">
<div
onClick={handleSubmit}
className="btn btn-primary"
disabled={processing}
>
Simpan
</div>
<div
onClick={handleCancel}
className="btn btn-secondary"
disabled={processing}
>
Batal
</div>
</div>
</div>
</div>
)
}

@ -0,0 +1,122 @@
import React from 'react'
import { Head } from '@inertiajs/react'
import { router } from '@inertiajs/react'
import { toast } from 'react-toastify'
import { useModalState } from '@/Hooks'
import { hasPermission } from '@/utils'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import ModalConfirm from '@/Components/ModalConfirm'
import FormModal from './FormModal'
export default function Categories(props) {
const { data: categories, links } = props.categories
const formModal = useModalState(false)
const toggle = (category = null) => {
formModal.setData(category)
formModal.toggle()
}
const confirmModal = useModalState(false)
const handleDelete = (category) => {
confirmModal.setData(category)
confirmModal.toggle()
}
const onDelete = () => {
const category = confirmModal.data
if(category != null) {
router.delete(route('categories.destroy', category), {
onSuccess: () => toast.success('The Data has been deleted'),
})
}
}
const canCreate = hasPermission('create-category', props.auth.user)
const canUpdate = hasPermission('update-category', props.auth.user)
const canDelete = hasPermission('delete-category', props.auth.user)
return (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
flash={props.flash}
notify={props.notify}
>
<Head title="Roles" />
<div className="flex flex-col w-full sm:px-6 lg:px-8 space-y-2">
<div className="card bg-base-100 w-full">
<div className="card-body">
<div className="flex w-full mb-4 justify-between">
{canCreate && (
<div
className="btn btn-neutral"
onClick={() => toggle()}
>
Tambah
</div>
)}
</div>
<div className="overflow-x-auto">
<table className="table w-full table-zebra">
<thead>
<tr>
<th>Id</th>
<th>Nama</th>
<th>Singkatan</th>
<th>Durasi</th>
<th></th>
</tr>
</thead>
<tbody>
{categories?.map((category) => (
<tr key={category.id}>
<th>{category.id}</th>
<td>{category.name}</td>
<td>{category.short}</td>
<td>{category.duration}</td>
<td className="text-right">
{canUpdate && (
<div
className="btn btn-primary mx-1"
onClick={() =>
toggle(category)
}
>
Edit
</div>
)}
{canDelete && (
<div
className="btn btn-secondary mx-1"
onClick={() =>
handleDelete(category)
}
>
Delete
</div>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
<Pagination links={links} />
</div>
</div>
</div>
<FormModal
modalState={formModal}
/>
<ModalConfirm
isOpen={confirmModal.isOpen}
toggle={confirmModal.toggle}
onConfirm={onDelete}
/>
</AuthenticatedLayout>
)
}

@ -0,0 +1,112 @@
import React, { useEffect } from 'react'
import { useForm, usePage } from '@inertiajs/react'
import { toast } from 'react-toastify'
export default function FormModal(props) {
const { modalState } = props
const { data, setData, post, put, processing, errors, reset, clearErrors } = useForm({
name: '',
})
const handleOnChange = (event) => {
setData(event.target.name, event.target.type === 'checkbox' ? event.target.checked : event.target.value);
}
const handleReset = () => {
reset()
clearErrors()
modalState.setData(null)
}
const handleCancel = () => {
handleReset()
modalState.toggle()
}
const handleSubmit = () => {
const type = modalState.data
if(type !== null) {
put(route('types.update', type), {
onSuccess: () =>
Promise.all([
handleReset(),
modalState.toggle(),
toast.success('The Data has been changed'),
]),
})
return
}
post(route('types.store'), {
onSuccess: () =>
Promise.all([
handleReset(),
modalState.toggle(),
toast.success('The Data has been saved'),
]),
})
}
useEffect(() => {
const type = modalState.data
if (type !== null) {
setData({
name: type?.name,
})
}
}, [modalState])
return (
<div
className="modal modal-bottom sm:modal-middle pb-10"
style={
modalState.isOpen
? {
opacity: 1,
pointerEvents: 'auto',
visibility: 'visible',
overflowY: 'initial',
}
: {}
}
>
<div className="modal-box overflow-y-auto max-h-screen">
<h1 className="font-bold text-2xl pb-8">Jenis</h1>
<div className="form-control">
<label className="label">
<span className="label-text font-semibold">Nama</span>
</label>
<input
type="text"
placeholder="nama"
className={`input input-bordered ${
errors.name && 'input-error'
}`}
name="name"
value={data.name}
onChange={handleOnChange}
/>
<label className="label">
<span className="label-text-alt text-red-600">{errors.name}</span>
</label>
</div>
<div className="modal-action">
<div
onClick={handleSubmit}
className="btn btn-primary"
disabled={processing}
>
Simpan
</div>
<div
onClick={handleCancel}
className="btn btn-secondary"
disabled={processing}
>
Batal
</div>
</div>
</div>
</div>
)
}

@ -0,0 +1,118 @@
import React from 'react'
import { Head } from '@inertiajs/react'
import { router } from '@inertiajs/react'
import { toast } from 'react-toastify'
import { useModalState } from '@/Hooks'
import { hasPermission } from '@/utils'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import ModalConfirm from '@/Components/ModalConfirm'
import FormModal from './FormModal'
export default function Types(props) {
const { data: types, links } = props.types
const formModal = useModalState(false)
const toggle = (type = null) => {
formModal.setData(type)
formModal.toggle()
}
const confirmModal = useModalState(false)
const handleDelete = (type) => {
confirmModal.setData(type)
confirmModal.toggle()
}
const onDelete = () => {
const type = confirmModal.data
if(type != null) {
router.delete(route('types.destroy', type), {
onSuccess: () => toast.success('The Data has been deleted'),
})
}
}
const canCreate = hasPermission('create-type', props.auth.user)
const canUpdate = hasPermission('update-type', props.auth.user)
const canDelete = hasPermission('delete-type', props.auth.user)
return (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
flash={props.flash}
notify={props.notify}
>
<Head title="Roles" />
<div className="flex flex-col w-full sm:px-6 lg:px-8 space-y-2">
<div className="card bg-base-100 w-full">
<div className="card-body">
<div className="flex w-full mb-4 justify-between">
{canCreate && (
<div
className="btn btn-neutral"
onClick={() => toggle()}
>
Tambah
</div>
)}
</div>
<div className="overflow-x-auto">
<table className="table w-full table-zebra">
<thead>
<tr>
<th>Id</th>
<th>Nama</th>
<th></th>
</tr>
</thead>
<tbody>
{types?.map((type) => (
<tr key={type.id}>
<th>{type.id}</th>
<td>{type.name}</td>
<td className="text-right">
{canUpdate && (
<div
className="btn btn-primary mx-1"
onClick={() =>
toggle(type)
}
>
Edit
</div>
)}
{canDelete && (
<div
className="btn btn-secondary mx-1"
onClick={() =>
handleDelete(type)
}
>
Delete
</div>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
<Pagination links={links} />
</div>
</div>
</div>
<FormModal
modalState={formModal}
/>
<ModalConfirm
isOpen={confirmModal.isOpen}
toggle={confirmModal.toggle}
onConfirm={onDelete}
/>
</AuthenticatedLayout>
)
}

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" data-theme="corporate">
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" data-theme="garden">
<!-- corporate -->
<head>
<meta charset="utf-8">

@ -1,9 +1,11 @@
<?php
use App\Http\Controllers\CategoryController;
use App\Http\Controllers\DocumentController;
use App\Http\Controllers\GeneralController;
use App\Http\Controllers\NotificationController;
use App\Http\Controllers\RoleController;
use App\Http\Controllers\TypeController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
@ -37,6 +39,16 @@ Route::middleware(['auth'])->group(function () {
Route::put('/roles/{role}', [RoleController::class, 'update'])->name('roles.update');
Route::delete('/roles/{role}', [RoleController::class, 'destroy'])->name('roles.destroy');
Route::get('/types', [TypeController::class, 'index'])->name('types.index');
Route::post('/types', [TypeController::class, 'store'])->name('types.store');
Route::put('/types/{type}', [TypeController::class, 'update'])->name('types.update');
Route::delete('/types/{type}', [TypeController::class, 'destroy'])->name('types.destroy');
Route::get('/categories', [CategoryController::class, 'index'])->name('categories.index');
Route::post('/categories', [CategoryController::class, 'store'])->name('categories.store');
Route::put('/categories/{category}', [CategoryController::class, 'update'])->name('categories.update');
Route::delete('/categories/{category}', [CategoryController::class, 'destroy'])->name('categories.destroy');
Route::get('/docs', [DocumentController::class, 'index'])->name('docs.index');
Route::get('/docs/export', [DocumentController::class, 'export'])->name('docs.export');
Route::get('/docs/create', [DocumentController::class, 'create'])->name('docs.create');

@ -23,6 +23,6 @@ module.exports = {
],
daisyui: {
themes: ["light", "dark", "corporate"],
themes: ["light", "dark", "corporate", "garden"],
},
};

Loading…
Cancel
Save