feat: crud barbershop

This commit is contained in:
Yoga Pangestu 2024-11-23 10:34:04 +07:00
parent 90761a349c
commit 56edf0c680
7 changed files with 590 additions and 1 deletions

View File

@ -0,0 +1,116 @@
<?php
namespace App\Http\Controllers\Admin\Master;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Master\BarbershopRequest;
use App\Models\Barbershop;
use App\Traits\UploadAttachment;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class BarbershopController extends Controller
{
use UploadAttachment;
public function index(): View
{
return view('pages.admin.master.barbershop.index', [
'pageTitle' => 'Barbershop',
'barbershop' => Barbershop::latest()->get(),
]);
}
public function create(): View
{
return view('pages.admin.master.barbershop.create', [
'pageTitle' => 'Tambah Barbershop',
]);
}
public function store(BarbershopRequest $request): RedirectResponse
{
$validatedData = $request->validated();
DB::transaction(function () use ($validatedData) {
$newBarbershop = Barbershop::create($validatedData);
$this->uploadAttachment($validatedData['image'], 'barbershop', Barbershop::class, $newBarbershop->id);
});
notify()->success('Data berhasil ditambahkan', 'Berhasil');
return redirect('dashboard/master/barbershop');
}
public function edit(Barbershop $barbershop): View
{
return view('pages.admin.master.barbershop.edit', [
'pageTitle' => 'Edit Barbershop',
'barbershop' => $barbershop,
]);
}
public function update(Barbershop $barbershop, BarbershopRequest $request): RedirectResponse
{
$validatedData = $request->validated();
DB::transaction(function () use ($barbershop, $validatedData) {
$barbershop->update($validatedData);
$this->uploadAttachment($validatedData['image'], 'barbershop', Barbershop::class, $barbershop->id);
});
notify()->success('Data berhasil ditambahkan', 'Berhasil');
return redirect('dashboard/master/barbershop');
}
public function delete(Barbershop $barbershop): RedirectResponse
{
if (Auth::user()->barbershop_id === $barbershop->id) {
notify()->info('Anda sedang berada di barbershop ini, silakan pilih barbershop lain lalu Anda dapat menghapusnya', 'Informasi');
return back();
}
$barbershop->delete();
notify()->success('Data berhasil dihapus', 'Berhasil');
return redirect('dashboard/master/barbershop');
}
public function generateActionButtons(Barbershop $barbershop): string
{
$editUrl = route('master.barbershop.edit', encryptId($barbershop->id));
$deleteUrl = route('master.barbershop.delete', encryptId($barbershop->id));
return '
<div class="nk-tb-col nk-tb-col-tools">
<ul class="nk-tb-actions gx-1 my-n1">
<li>
<div class="drodown">
<a href="#" class="dropdown-toggle btn btn-icon btn-trigger me-n1" data-bs-toggle="dropdown"><em class="icon ni ni-more-h"></em></a>
<div class="dropdown-menu dropdown-menu-end">
<ul class="link-list-opt no-bdr">
<li>
<a href="'.$editUrl.'">
<em class="icon ni ni-edit"></em>
<span>Edit</span>
</a>
</li>
<li>
<form action="'.$deleteUrl.'" method="post">
'.csrf_field().'
'.method_field('delete').'
<a href="javascript:void(0)" class="delete"><em class="icon ni ni-trash"></em><span>Hapus</span></a>
</form>
</li>
</ul>
</div>
</div>
</li>
</ul>
</div>';
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Http\Requests\Admin\Master;
use App\Traits\NotificationTrait;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
class BarbershopRequest extends FormRequest
{
use NotificationTrait;
public function authorize(): bool
{
return Auth::check();
}
protected function prepareForValidation(): void
{
$this->merge([
'cash' => removeRupiahFormatting($this->cash),
]);
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:50'],
'contact_number' => ['required', 'string', 'regex:/^\+?\d{10,15}$/'],
'landmark' => ['required', 'string', 'max:30'],
'address' => ['required', 'string', 'max:500'],
'opened' => ['required', 'date'],
'closed' => ['nullable', 'date'],
'cash' => ['required', 'numeric', 'min:0', 'max:99999999.99'],
'image' => ['required', 'image', 'mimes:jpeg,png,jpg,gif,svg', 'max:2048'],
];
}
protected function failedValidation(Validator $validator): void
{
$this->handleValidationFailure($validator);
}
}

View File

@ -2,15 +2,19 @@
namespace App\Models;
use App\Observers\BarbershopObserver;
use Dyrynda\Database\Support\CascadeSoftDeletes;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\SoftDeletes;
#[ObservedBy([BarbershopObserver::class])]
class Barbershop extends Model
{
use HasFactory, SoftDeletes;
use CascadeSoftDeletes, HasFactory, SoftDeletes;
protected $table = 'barbershop';
@ -24,6 +28,23 @@ class Barbershop extends Model
'cash',
];
protected $with = [
'attachment',
];
protected $cascadeDeletes = [
'users',
'customers',
'services',
'attendances',
'expenses',
'inventory',
'transactions',
'payrolls',
'cash',
'attachment',
];
public function users(): HasMany
{
return $this->hasMany(User::class);

View File

@ -0,0 +1,51 @@
<?php
namespace App\Observers;
use App\Models\Barbershop;
use Illuminate\Support\Facades\Storage;
class BarbershopObserver
{
/**
* Handle the Barbershop "created" event.
*/
public function created(Barbershop $barbershop): void
{
//
}
/**
* Handle the Barbershop "updated" event.
*/
public function updated(Barbershop $barbershop): void
{
//
}
/**
* Handle the Barbershop "deleting" event.
*/
public function deleting(Barbershop $barbershop): void
{
if ($barbershop->attachment && $barbershop->attachment->formatted_attachment) {
Storage::delete("barbershop/{$barbershop->attachment->formatted_attachment}");
}
}
/**
* Handle the Barbershop "restored" event.
*/
public function restored(Barbershop $barbershop): void
{
//
}
/**
* Handle the Barbershop "force deleted" event.
*/
public function forceDeleted(Barbershop $barbershop): void
{
//
}
}

View File

@ -0,0 +1,127 @@
@extends('layouts.admin')
@section('styles')
<link rel="stylesheet" type="text/css" href="{{ asset('assets/admin/css/vendors/flatpickr/flatpickr.min.css') }}">
@endsection
@section('app')
<div class="container-fluid">
<div class="list-product-header">
<div>
<a class="btn btn-dark" href="{{ route('master.barbershop') }}">Kembali</a>
</div>
</div>
<div class="card">
<div class="card-body">
<form class="row g-3 custom-input" action="{{ route('master.barbershop.store') }}" method="post"
enctype="multipart/form-data">
@csrf
<div class="col-lg-6 position-relative">
<label class="form-label" for="name">Nama</label>
<input name="name" class="form-control @error('name') is-invalid @enderror" id="name"
type="text" placeholder="Masukan Nama" value="{{ old('name') }}">
@error('name')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-6 position-relative">
<label class="form-label" for="contact_number">Kontak
<span class="form-span"><i>(format : 08xxxxxxxx)</i></span>
</label>
<input name="contact_number" class="form-control @error('contact_number') is-invalid @enderror"
id="contact_number" type="number" placeholder="Masukan Kontak"
value="{{ old('contact_number') }}">
@error('contact_number')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-6 position-relative">
<label class="form-label" for="landmark">Patokan</label>
<input name="landmark" class="form-control @error('landmark') is-invalid @enderror" id="landmark"
type="text" placeholder="Masukan Patokan" value="{{ old('landmark') }}">
@error('landmark')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-6 position-relative">
<label class="form-label" for="opened">Tanggal Buka</label>
<div class="input-group flatpicker-calender">
<input name="opened" class="form-control @error('opened') is-invalid @enderror human-friendly"
id="opened" type="date" placeholder="Masukan Tanggal Buka"
value="{{ old('opened') }}">
</div>
@error('opened')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-12 position-relative">
<label class="form-label" for="address">Alamat</label>
<textarea name="address" class="form-control @error('address') is-invalid @enderror" id="address" rows="3"
placeholder="Masukan Alamat">{{ old('address') }}</textarea>
@error('address')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-4 position-relative">
<label class="form-label" for="cash">Kas</label>
<input name="cash" class="form-control rupiah-format @error('cash') is-invalid @enderror"
id="cash" type="text" placeholder="Masukan Kas" value="{{ old('cash') }}"
inputmode="numeric">
@error('cash')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-4 position-relative">
<label class="form-label" for="closed">Tanggal Tutup
<span class="form-span"><i>(Opsional)</i></span>
</label>
<div class="input-group flatpicker-calender">
<input name="closed" class="form-control @error('closed') is-invalid @enderror human-friendly"
id="closed" type="date" placeholder="Masukan Tanggal Tutup"
value="{{ old('closed') }}">
</div>
@error('closed')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-4 position-relative">
<label class="form-label" for="contact_number">Ganbar
<span class="form-span"><i>(ekstensi : .jpeg,.png,.jpg,.gif,.svg)</i></span>
</label>
<input name="image" class="form-control @error('image') is-invalid @enderror" id="image"
type="file" value="{{ old('image') }}"
accept="image/jpeg,image/png,image/jpg,image/gif,image/svg">
@error('image')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-12">
<button class="btn btn-primary button-disable" type="submit">Simpan</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('beforeScripts')
<script src="{{ asset('assets/admin/js/flat-pickr/flatpickr.js?ver=1.0.0') }}"></script>
<script src="{{ asset('assets/admin/js/flat-pickr/custom-flatpickr.js?ver=1.0.0') }}"></script>
@endsection
@section('afterScripts')
<script src="{{ asset('assets/js/forms/button-disable.js?ver=1.0.0') }}"></script>
<script src="{{ asset('assets/js/forms/rupiah-format.js?ver=1.0.0') }}"></script>
@endsection

View File

@ -0,0 +1,130 @@
@extends('layouts.admin')
@section('styles')
<link rel="stylesheet" type="text/css" href="{{ asset('assets/admin/css/vendors/flatpickr/flatpickr.min.css') }}">
@endsection
@section('app')
<div class="container-fluid">
<div class="list-product-header">
<div>
<a class="btn btn-dark" href="{{ route('master.barbershop') }}">Kembali</a>
</div>
</div>
<div class="card">
<div class="card-body">
<form class="row g-3 custom-input"
action="{{ route('master.barbershop.update', encryptId($barbershop->id)) }}" method="post"
enctype="multipart/form-data">
@csrf
@method('put')
<div class="col-lg-6 position-relative">
<label class="form-label" for="name">Nama</label>
<input name="name" class="form-control @error('name') is-invalid @enderror" id="name"
type="text" placeholder="Masukan Nama" value="{{ old('name', $barbershop->name) }}">
@error('name')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-6 position-relative">
<label class="form-label" for="contact_number">Kontak
<span class="form-span"><i>(format : 08xxxxxxxx)</i></span>
</label>
<input name="contact_number" class="form-control @error('contact_number') is-invalid @enderror"
id="contact_number" type="number" placeholder="Masukan Kontak"
value="{{ old('contact_number', $barbershop->contact_number) }}">
@error('contact_number')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-6 position-relative">
<label class="form-label" for="landmark">Patokan</label>
<input name="landmark" class="form-control @error('landmark') is-invalid @enderror" id="landmark"
type="text" placeholder="Masukan Patokan"
value="{{ old('landmark', $barbershop->landmark) }}">
@error('landmark')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-6 position-relative">
<label class="form-label" for="opened">Tanggal Buka</label>
<div class="input-group flatpicker-calender">
<input name="opened" class="form-control @error('opened') is-invalid @enderror human-friendly"
id="opened" type="date" placeholder="Masukan Tanggal Buka"
value="{{ old('opened', $barbershop->opened) }}">
</div>
@error('opened')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-12 position-relative">
<label class="form-label" for="address">Alamat</label>
<textarea name="address" class="form-control @error('address') is-invalid @enderror" id="address" rows="3"
placeholder="Masukan Alamat">{{ old('address', $barbershop->address) }}</textarea>
@error('address')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-4 position-relative">
<label class="form-label" for="cash">Kas</label>
<input name="cash" class="form-control rupiah-format @error('cash') is-invalid @enderror"
id="cash" type="text" placeholder="Masukan Kas"
value="{{ old('cash', formatToRupiah($barbershop->cash)) }}" inputmode="numeric">
@error('cash')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-4 position-relative">
<label class="form-label" for="closed">Tanggal Tutup
<span class="form-span"><i>(Opsional)</i></span>
</label>
<div class="input-group flatpicker-calender">
<input name="closed" class="form-control @error('closed') is-invalid @enderror human-friendly"
id="closed" type="date" placeholder="Masukan Tanggal Tutup"
value="{{ old('closed', $barbershop->closed) }}">
</div>
@error('closed')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-lg-4 position-relative">
<label class="form-label" for="contact_number">Ganbar
<span class="form-span"><i>(ekstensi : .jpeg,.png,.jpg,.gif,.svg)</i></span>
</label>
<input name="image" class="form-control @error('image') is-invalid @enderror" id="image"
type="file" value="{{ old('image') }}"
accept="image/jpeg,image/png,image/jpg,image/gif,image/svg">
@error('image')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="col-12">
<button class="btn btn-primary button-disable" type="submit">Simpan</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('beforeScripts')
<script src="{{ asset('assets/admin/js/flat-pickr/flatpickr.js?ver=1.0.0') }}"></script>
<script src="{{ asset('assets/admin/js/flat-pickr/custom-flatpickr.js?ver=1.0.0') }}"></script>
@endsection
@section('afterScripts')
<script src="{{ asset('assets/js/forms/button-disable.js?ver=1.0.0') }}"></script>
<script src="{{ asset('assets/js/forms/rupiah-format.js?ver=1.0.0') }}"></script>
@endsection

View File

@ -0,0 +1,100 @@
@extends('layouts.admin')
@section('styles')
<link rel="stylesheet" type="text/css" href="{{ asset('assets/admin/css/vendors/datatables.css') }}">
@endsection
@section('app')
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="list-product-header">
<div>
<a class="btn btn-primary" href="{{ route('master.barbershop.create') }}">Tambah</a>
</div>
</div>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="display" id="datatable">
<thead>
<tr>
<th>No</th>
<th>Nama</th>
<th>Kontak</th>
<th>Patokan</th>
<th>Alamat</th>
<th>Dibuka</th>
<th>Tutup</th>
<th>Kas</th>
<th>Gambar</th>
<th>Tanggal</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($barbershop as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $item->name }}</td>
<td>{{ $item->contact_number }}</td>
<td>{{ $item->landmark }}</td>
<td>{{ $item->address }}</td>
<td>{{ formatDateIndo($item->opened) }}</td>
<td>{{ $item->closed ? formatDateIndo($item->closed) : 'Masih Beroperasi' }}
</td>
<td>{{ formatToRupiah($item->cash) }}</td>
<td>
@if ($item->attachment && $item->attachment->formatted_attachment)
<a
href="{{ Storage::url("barbershop/{$item->attachment->formatted_attachment}") }}">
<img src="{{ Storage::url("barbershop/{$item->attachment->formatted_attachment}") }}"
alt="{{ $item->name }}" width="77" height="77">
</a>
@else
<a href="{{ asset('assets/images/no-imge.png') }}">
<img src="{{ asset('assets/images/no-imge.png') }}"
alt="Default Image" style="width: 77px; height: 77px">
</a>
@endif
</td>
<td>{{ formatDateIndo($item->created_at) }}</td>
<td>
<ul class="action">
<li class="edit">
<a href="{{ route('master.barbershop.edit', encryptId($item->id)) }}"
data-bs-toggle="tooltip" data-bs-placement="left"
data-bs-title="Edit">
<i data-feather="edit"></i>
</a>
</li>
<li class="delete">
<form
action="{{ route('master.barbershop.delete', encryptId($item->id)) }}"
method="post">
@csrf
@method('delete')
<a href="javascript:void(0)" class="delete-data"
data-bs-toggle="tooltip" data-bs-placement="left"
data-bs-title="Hapus">
<i data-feather="trash-2"></i>
</a>
</form>
</li>
</ul>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('afterScripts')
<script src="{{ asset('assets/admin/js/datatable/jquery.dataTables.min.js?ver=1.0.0') }}"></script>
<script src="{{ asset('assets/admin/js/datatable/client-side.js?ver=1.0.0') }}"></script>
<script src="{{ asset('assets/js/interactions/confirmation.js?ver=1.0.0') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
@endsection