feat(role & permission): membuat trait untuk otorisasi

-menambahkan otorisasi semua komponen. tabel, view dan lainnya
This commit is contained in:
Yoga Pangestu 2025-10-02 21:42:08 +07:00
parent 8d27efd4c2
commit e905f98b4f
54 changed files with 888 additions and 430 deletions

17
app/Enums/UserRole.php Normal file
View File

@ -0,0 +1,17 @@
<?php
namespace App\Enums;
use App\Traits\WithValueEnum;
enum UserRole: string
{
use WithValueEnum;
case Developer = 'Developer';
case Owner = 'Owner';
case Leader = 'Leader';
case Admin = 'Admin';
case Partner = 'Partner';
case Customer = 'Customer';
}

View File

@ -63,15 +63,19 @@ public function columns(): array
->label(function ($row) { ->label(function ($row) {
$actions = ''; $actions = '';
if (auth()->user()->can('update bottle')) {
$actions .= view('components.datatables.edit', [ $actions .= view('components.datatables.edit', [
'id' => $row->id, 'id' => $row->id,
'editRoute' => route('studio.catalog.bottle.edit', $row->id), 'editRoute' => route('studio.catalog.bottle.edit', $row->id),
])->render(); ])->render();
}
if (auth()->user()->can('delete bottle')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.catalog.bottle.delete', $row->id), 'deleteRoute' => route('studio.catalog.bottle.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -34,6 +34,7 @@ public function columns(): array
$actions = ''; $actions = '';
$maxSortOrder = Brand::max('sort_order'); $maxSortOrder = Brand::max('sort_order');
if (auth()->user()->can('update brand')) {
if ($row->sort_order > 1) { if ($row->sort_order > 1) {
$actions .= view('components.datatables.sort-button', [ $actions .= view('components.datatables.sort-button', [
'id' => $row->id, 'id' => $row->id,
@ -73,17 +74,22 @@ public function columns(): array
'icon' => 'bars-arrow-down', 'icon' => 'bars-arrow-down',
])->render(); ])->render();
} }
}
if (auth()->user()->can('update brand')) {
$actions .= view('components.datatables.edit-modal', [ $actions .= view('components.datatables.edit-modal', [
'id' => $row->id, 'id' => $row->id,
'method' => 'update', 'method' => 'update',
'modalTitle' => 'Ubah Kategori', 'modalTitle' => 'Ubah Kategori',
])->render(); ])->render();
}
if (auth()->user()->can('delete brand')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.catalog.brand.delete', $row->id), 'deleteRoute' => route('studio.catalog.brand.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -25,6 +25,7 @@ public function columns(): array
$actions = ''; $actions = '';
$maxSortOrder = Category::max('sort_order'); $maxSortOrder = Category::max('sort_order');
if (auth()->user()->can('update category')) {
if ($row->sort_order > 1) { if ($row->sort_order > 1) {
$actions .= view('components.datatables.sort-button', [ $actions .= view('components.datatables.sort-button', [
'id' => $row->id, 'id' => $row->id,
@ -64,17 +65,22 @@ public function columns(): array
'icon' => 'bars-arrow-down', 'icon' => 'bars-arrow-down',
])->render(); ])->render();
} }
}
if (auth()->user()->can('update category')) {
$actions .= view('components.datatables.edit-modal', [ $actions .= view('components.datatables.edit-modal', [
'id' => $row->id, 'id' => $row->id,
'method' => 'update', 'method' => 'update',
'modalTitle' => 'Ubah Kategori', 'modalTitle' => 'Ubah Kategori',
])->render(); ])->render();
}
if (auth()->user()->can('delete category')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.catalog.category.delete', $row->id), 'deleteRoute' => route('studio.catalog.category.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -72,15 +72,19 @@ public function columns(): array
->label(function ($row) { ->label(function ($row) {
$actions = ''; $actions = '';
if (auth()->user()->can('update perfume')) {
$actions .= view('components.datatables.edit', [ $actions .= view('components.datatables.edit', [
'id' => $row->id, 'id' => $row->id,
'editRoute' => route('studio.catalog.perfume.edit', $row->id), 'editRoute' => route('studio.catalog.perfume.edit', $row->id),
])->render(); ])->render();
}
if (auth()->user()->can('delete perfume')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.catalog.perfume.delete', $row->id), 'deleteRoute' => route('studio.catalog.perfume.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -55,15 +55,19 @@ public function columns(): array
->label(function ($row) { ->label(function ($row) {
$actions = ''; $actions = '';
if (auth()->user()->can('update product')) {
$actions .= view('components.datatables.edit', [ $actions .= view('components.datatables.edit', [
'id' => $row->id, 'id' => $row->id,
'editRoute' => route('studio.catalog.product.edit', $row->id), 'editRoute' => route('studio.catalog.product.edit', $row->id),
])->render(); ])->render();
}
if (auth()->user()->can('delete product')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.catalog.product.delete', $row->id), 'deleteRoute' => route('studio.catalog.product.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -29,16 +29,20 @@ public function columns(): array
->label(function ($row) { ->label(function ($row) {
$actions = ''; $actions = '';
if (auth()->user()->can('update expense')) {
$actions .= view('components.datatables.edit-modal', [ $actions .= view('components.datatables.edit-modal', [
'id' => $row->id, 'id' => $row->id,
'method' => 'update', 'method' => 'update',
'modalTitle' => 'Ubah Kategori', 'modalTitle' => 'Ubah Kategori',
])->render(); ])->render();
}
if (auth()->user()->can('delete expense')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.finance.expense.delete', $row->id), 'deleteRoute' => route('studio.finance.expense.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -36,16 +36,20 @@ public function columns(): array
->label(function ($row) { ->label(function ($row) {
$actions = ''; $actions = '';
if (auth()->user()->can('update customer')) {
$actions .= view('components.datatables.edit-modal', [ $actions .= view('components.datatables.edit-modal', [
'id' => $row->id, 'id' => $row->id,
'method' => 'update', 'method' => 'update',
'modalTitle' => 'Ubah Customer', 'modalTitle' => 'Ubah Customer',
])->render(); ])->render();
}
if (auth()->user()->can('delete customer')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.loyalty.customer.delete', $row->id), 'deleteRoute' => route('studio.loyalty.customer.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -29,16 +29,20 @@ public function columns(): array
->label(function ($row) { ->label(function ($row) {
$actions = ''; $actions = '';
if (auth()->user()->can('update tier')) {
$actions .= view('components.datatables.edit-modal', [ $actions .= view('components.datatables.edit-modal', [
'id' => $row->id, 'id' => $row->id,
'method' => 'update', 'method' => 'update',
'modalTitle' => 'Ubah Tier', 'modalTitle' => 'Ubah Tier',
])->render(); ])->render();
}
if (auth()->user()->can('delete tier')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.loyalty.tier.delete', $row->id), 'deleteRoute' => route('studio.loyalty.tier.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -93,15 +93,19 @@ public function columns(): array
->label(function ($row) { ->label(function ($row) {
$actions = ''; $actions = '';
if (auth()->user()->can('update voucher')) {
$actions .= view('components.datatables.edit', [ $actions .= view('components.datatables.edit', [
'id' => $row->id, 'id' => $row->id,
'editRoute' => route('studio.loyalty.voucher.edit', $row->id), 'editRoute' => route('studio.loyalty.voucher.edit', $row->id),
])->render(); ])->render();
}
if (auth()->user()->can('delete voucher')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.loyalty.voucher.delete', $row->id), 'deleteRoute' => route('studio.loyalty.voucher.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -104,15 +104,19 @@ public function columns(): array
->label(function ($row) { ->label(function ($row) {
$actions = ''; $actions = '';
if (auth()->user()->can('update outlet')) {
$actions .= view('components.datatables.edit', [ $actions .= view('components.datatables.edit', [
'id' => $row->id, 'id' => $row->id,
'editRoute' => route('studio.master.outlet.edit', $row->id), 'editRoute' => route('studio.master.outlet.edit', $row->id),
])->render(); ])->render();
}
if (auth()->user()->can('delete outlet')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.master.outlet.delete', $row->id), 'deleteRoute' => route('studio.master.outlet.delete', $row->id),
])->render(); ])->render();
}
return $actions; return $actions;
}) })

View File

@ -124,16 +124,20 @@ public function columns(): array
$actions = ''; $actions = '';
if (auth()->id() !== $row?->user?->id) { if (auth()->id() !== $row?->user?->id) {
if (auth()->user()->can('update user')) {
$actions .= view('components.datatables.edit', [ $actions .= view('components.datatables.edit', [
'id' => $row->id, 'id' => $row->id,
'editRoute' => route('studio.master.user.edit', $row->id), 'editRoute' => route('studio.master.user.edit', $row->id),
])->render(); ])->render();
}
if (auth()->user()->can('delete user')) {
$actions .= view('components.datatables.delete', [ $actions .= view('components.datatables.delete', [
'id' => $row->id, 'id' => $row->id,
'deleteRoute' => route('studio.master.user.delete', $row->id), 'deleteRoute' => route('studio.master.user.delete', $row->id),
])->render(); ])->render();
} }
}
return $actions; return $actions;
}) })

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Catalog\BottleForm; use App\Livewire\Forms\Studio\Catalog\BottleForm;
use App\Models\Outlet; use App\Models\Outlet;
use App\Traits\WithAuthorization;
use App\Traits\WithOutletSelector; use App\Traits\WithOutletSelector;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
@ -13,7 +14,7 @@
#[Title('Tambah Botol')] #[Title('Tambah Botol')]
class Create extends Component class Create extends Component
{ {
use WithOutletSelector, WithToast, WithUpdatedData; use WithAuthorization, WithOutletSelector, WithToast, WithUpdatedData;
public BottleForm $form; public BottleForm $form;
@ -26,6 +27,8 @@ public function mount()
public function save() public function save()
{ {
$this->canOrAbort('create bottle');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -29,6 +29,8 @@ public function mount(Bottle $bottle)
public function save() public function save()
{ {
$this->canOrAbort('update bottle');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Catalog\BrandForm; use App\Livewire\Forms\Studio\Catalog\BrandForm;
use App\Models\Brand as BrandModel; use App\Models\Brand as BrandModel;
use App\Traits\WithAuthorization;
use App\Traits\WithCloseModal; use App\Traits\WithCloseModal;
use App\Traits\WithConfirmation; use App\Traits\WithConfirmation;
use App\Traits\WithToast; use App\Traits\WithToast;
@ -16,7 +17,7 @@
#[Title('Merek')] #[Title('Merek')]
class Brand extends Component class Brand extends Component
{ {
use WithCloseModal, WithConfirmation, WithToast, WithUpdatedData; use WithAuthorization, WithCloseModal, WithConfirmation, WithToast, WithUpdatedData;
public BrandForm $form; public BrandForm $form;
@ -40,6 +41,8 @@ public function openModal(string $method, string $modalTitle, ?string $id = null
public function create() public function create()
{ {
$this->canOrAbort('create brand');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');
@ -51,6 +54,8 @@ public function create()
public function update() public function update()
{ {
$this->canOrAbort('update brand');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');
@ -76,6 +81,8 @@ public function delete(BrandModel $brand)
#[On('fn:sortOrder')] #[On('fn:sortOrder')]
public function sortOrder(BrandModel $brand, string $direction) public function sortOrder(BrandModel $brand, string $direction)
{ {
$this->canOrAbort('update brand');
$this->form->brand = $brand; $this->form->brand = $brand;
$this->form->sortOrder($direction); $this->form->sortOrder($direction);

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Catalog\CategoryForm; use App\Livewire\Forms\Studio\Catalog\CategoryForm;
use App\Models\Category as CategoryModel; use App\Models\Category as CategoryModel;
use App\Traits\WithAuthorization;
use App\Traits\WithCloseModal; use App\Traits\WithCloseModal;
use App\Traits\WithConfirmation; use App\Traits\WithConfirmation;
use App\Traits\WithToast; use App\Traits\WithToast;
@ -16,7 +17,7 @@
#[Title('Kategori')] #[Title('Kategori')]
class Category extends Component class Category extends Component
{ {
use WithCloseModal, WithConfirmation, WithToast, WithUpdatedData; use WithAuthorization, WithCloseModal, WithConfirmation, WithToast, WithUpdatedData;
public CategoryForm $form; public CategoryForm $form;
@ -40,6 +41,8 @@ public function openModal(string $method, string $modalTitle, ?string $id = null
public function create() public function create()
{ {
$this->canOrAbort('create category');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');
@ -51,6 +54,8 @@ public function create()
public function update() public function update()
{ {
$this->canOrAbort('update category');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');
@ -76,6 +81,8 @@ public function delete(CategoryModel $category)
#[On('fn:sortOrder')] #[On('fn:sortOrder')]
public function sortOrder(CategoryModel $category, string $direction) public function sortOrder(CategoryModel $category, string $direction)
{ {
$this->canOrAbort('update category');
$this->form->category = $category; $this->form->category = $category;
$this->form->sortOrder($direction); $this->form->sortOrder($direction);

View File

@ -6,6 +6,7 @@
use App\Models\Brand; use App\Models\Brand;
use App\Models\Category; use App\Models\Category;
use App\Models\Outlet; use App\Models\Outlet;
use App\Traits\WithAuthorization;
use App\Traits\WithCategorySelector; use App\Traits\WithCategorySelector;
use App\Traits\WithOutletSelector; use App\Traits\WithOutletSelector;
use App\Traits\WithToast; use App\Traits\WithToast;
@ -16,7 +17,7 @@
#[Title('Tambah Parfum')] #[Title('Tambah Parfum')]
class Create extends Component class Create extends Component
{ {
use WithCategorySelector, WithOutletSelector, WithToast, WithUpdatedData; use WithAuthorization, WithCategorySelector, WithOutletSelector, WithToast, WithUpdatedData;
public PerfumeForm $form; public PerfumeForm $form;
@ -37,6 +38,8 @@ public function mount()
public function save() public function save()
{ {
$this->canOrAbort('create perfume');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -7,6 +7,7 @@
use App\Models\Category; use App\Models\Category;
use App\Models\Outlet; use App\Models\Outlet;
use App\Models\Perfume; use App\Models\Perfume;
use App\Traits\WithAuthorization;
use App\Traits\WithCategorySelector; use App\Traits\WithCategorySelector;
use App\Traits\WithOutletSelector; use App\Traits\WithOutletSelector;
use App\Traits\WithToast; use App\Traits\WithToast;
@ -17,7 +18,7 @@
#[Title('Ubah Parfum')] #[Title('Ubah Parfum')]
class Edit extends Component class Edit extends Component
{ {
use WithCategorySelector, WithOutletSelector, WithToast, WithUpdatedData; use WithAuthorization, WithCategorySelector, WithOutletSelector, WithToast, WithUpdatedData;
public PerfumeForm $form; public PerfumeForm $form;
@ -40,6 +41,8 @@ public function mount(Perfume $perfume)
public function save() public function save()
{ {
$this->canOrAbort('update perfume');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Catalog\ProductForm; use App\Livewire\Forms\Studio\Catalog\ProductForm;
use App\Models\Outlet; use App\Models\Outlet;
use App\Traits\WithAuthorization;
use App\Traits\WithOutletSelector; use App\Traits\WithOutletSelector;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
@ -13,7 +14,7 @@
#[Title('Tambah Produk')] #[Title('Tambah Produk')]
class Create extends Component class Create extends Component
{ {
use WithOutletSelector, WithToast, WithUpdatedData; use WithAuthorization, WithOutletSelector, WithToast, WithUpdatedData;
public ProductForm $form; public ProductForm $form;
@ -26,6 +27,8 @@ public function mount()
public function save() public function save()
{ {
$this->canOrAbort('create product');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -5,6 +5,7 @@
use App\Livewire\Forms\Studio\Catalog\ProductForm; use App\Livewire\Forms\Studio\Catalog\ProductForm;
use App\Models\Outlet; use App\Models\Outlet;
use App\Models\Product; use App\Models\Product;
use App\Traits\WithAuthorization;
use App\Traits\WithOutletSelector; use App\Traits\WithOutletSelector;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
@ -14,7 +15,7 @@
#[Title('Ubah Produk')] #[Title('Ubah Produk')]
class Edit extends Component class Edit extends Component
{ {
use WithOutletSelector, WithToast, WithUpdatedData; use WithAuthorization, WithOutletSelector, WithToast, WithUpdatedData;
public ProductForm $form; public ProductForm $form;
@ -29,6 +30,8 @@ public function mount(Product $product)
public function save() public function save()
{ {
$this->canOrAbort('update product');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Finance\ExpenseForm; use App\Livewire\Forms\Studio\Finance\ExpenseForm;
use App\Models\Expense as ExpenseModel; use App\Models\Expense as ExpenseModel;
use App\Traits\WithAuthorization;
use App\Traits\WithCloseModal; use App\Traits\WithCloseModal;
use App\Traits\WithConfirmation; use App\Traits\WithConfirmation;
use App\Traits\WithToast; use App\Traits\WithToast;
@ -16,7 +17,7 @@
#[Title('Pengeluaran')] #[Title('Pengeluaran')]
class Expense extends Component class Expense extends Component
{ {
use WithCloseModal, WithConfirmation, WithToast, WithUpdatedData; use WithAuthorization, WithCloseModal, WithConfirmation, WithToast, WithUpdatedData;
public ExpenseForm $form; public ExpenseForm $form;
@ -40,6 +41,8 @@ public function openModal(string $method, string $modalTitle, ?string $id = null
public function create() public function create()
{ {
$this->canOrAbort('create expense');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');
@ -51,6 +54,8 @@ public function create()
public function update() public function update()
{ {
$this->canOrAbort('update expense');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Loyalty\CustomerForm; use App\Livewire\Forms\Studio\Loyalty\CustomerForm;
use App\Models\Customer as CustomerModel; use App\Models\Customer as CustomerModel;
use App\Traits\WithAuthorization;
use App\Traits\WithCloseModal; use App\Traits\WithCloseModal;
use App\Traits\WithConfirmation; use App\Traits\WithConfirmation;
use App\Traits\WithToast; use App\Traits\WithToast;
@ -16,7 +17,7 @@
#[Title('Customer')] #[Title('Customer')]
class Customer extends Component class Customer extends Component
{ {
use WithCloseModal, WithConfirmation, WithToast, WithUpdatedData; use WithAuthorization, WithCloseModal, WithConfirmation, WithToast, WithUpdatedData;
public CustomerForm $form; public CustomerForm $form;
@ -40,6 +41,8 @@ public function openModal(string $method, string $modalTitle, ?string $id = null
public function create() public function create()
{ {
$this->canOrAbort('create customer');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');
@ -51,6 +54,8 @@ public function create()
public function update() public function update()
{ {
$this->canOrAbort('update customer');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Loyalty\TierForm; use App\Livewire\Forms\Studio\Loyalty\TierForm;
use App\Models\Tier as TierModel; use App\Models\Tier as TierModel;
use App\Traits\WithAuthorization;
use App\Traits\WithCloseModal; use App\Traits\WithCloseModal;
use App\Traits\WithConfirmation; use App\Traits\WithConfirmation;
use App\Traits\WithToast; use App\Traits\WithToast;
@ -16,7 +17,7 @@
#[Title('Tier')] #[Title('Tier')]
class Tier extends Component class Tier extends Component
{ {
use WithCloseModal, WithConfirmation, WithToast, WithUpdatedData; use WithAuthorization, WithCloseModal, WithConfirmation, WithToast, WithUpdatedData;
public TierForm $form; public TierForm $form;
@ -40,6 +41,8 @@ public function openModal(string $method, string $modalTitle, ?string $id = null
public function create() public function create()
{ {
$this->canOrAbort('create tier');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');
@ -51,6 +54,8 @@ public function create()
public function update() public function update()
{ {
$this->canOrAbort('update tier');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Loyalty\VoucherForm; use App\Livewire\Forms\Studio\Loyalty\VoucherForm;
use App\Models\Outlet; use App\Models\Outlet;
use App\Traits\WithAuthorization;
use App\Traits\WithOutletSelector; use App\Traits\WithOutletSelector;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
@ -13,7 +14,7 @@
#[Title('Tambah Voucher')] #[Title('Tambah Voucher')]
class Create extends Component class Create extends Component
{ {
use WithOutletSelector, WithToast, WithUpdatedData; use WithAuthorization, WithOutletSelector, WithToast, WithUpdatedData;
public VoucherForm $form; public VoucherForm $form;
@ -26,6 +27,8 @@ public function mount()
public function save() public function save()
{ {
$this->canOrAbort('create voucher');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -5,6 +5,7 @@
use App\Livewire\Forms\Studio\Loyalty\VoucherForm; use App\Livewire\Forms\Studio\Loyalty\VoucherForm;
use App\Models\Outlet; use App\Models\Outlet;
use App\Models\Voucher; use App\Models\Voucher;
use App\Traits\WithAuthorization;
use App\Traits\WithOutletSelector; use App\Traits\WithOutletSelector;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
@ -14,7 +15,7 @@
#[Title('Ubah Voucher')] #[Title('Ubah Voucher')]
class Edit extends Component class Edit extends Component
{ {
use WithOutletSelector, WithToast, WithUpdatedData; use WithAuthorization, WithOutletSelector, WithToast, WithUpdatedData;
public VoucherForm $form; public VoucherForm $form;
@ -29,6 +30,8 @@ public function mount(Voucher $voucher)
public function save() public function save()
{ {
$this->canOrAbort('update voucher');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -5,6 +5,7 @@
use App\Enums\Day; use App\Enums\Day;
use App\Livewire\Forms\Studio\Master\OutletForm; use App\Livewire\Forms\Studio\Master\OutletForm;
use App\Traits\Outlet\WithFacilityHandler; use App\Traits\Outlet\WithFacilityHandler;
use App\Traits\WithAuthorization;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
@ -13,7 +14,7 @@
#[Title('Tambah Outlet')] #[Title('Tambah Outlet')]
class Create extends Component class Create extends Component
{ {
use WithFacilityHandler, WithToast, WithUpdatedData; use WithAuthorization, WithFacilityHandler, WithToast, WithUpdatedData;
public OutletForm $form; public OutletForm $form;
@ -31,6 +32,8 @@ public function mount()
public function save() public function save()
{ {
$this->canOrAbort('create outlet');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -6,6 +6,7 @@
use App\Livewire\Forms\Studio\Master\OutletForm; use App\Livewire\Forms\Studio\Master\OutletForm;
use App\Models\Outlet; use App\Models\Outlet;
use App\Traits\Outlet\WithFacilityHandler; use App\Traits\Outlet\WithFacilityHandler;
use App\Traits\WithAuthorization;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
@ -14,7 +15,7 @@
#[Title('Ubah Outlet')] #[Title('Ubah Outlet')]
class Edit extends Component class Edit extends Component
{ {
use WithFacilityHandler, WithToast, WithUpdatedData; use WithAuthorization, WithFacilityHandler, WithToast, WithUpdatedData;
public OutletForm $form; public OutletForm $form;
@ -29,6 +30,8 @@ public function mount(Outlet $outlet)
public function save() public function save()
{ {
$this->canOrAbort('update outlet');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -3,6 +3,7 @@
namespace App\Livewire\Studio\Master\User; namespace App\Livewire\Studio\Master\User;
use App\Livewire\Forms\Studio\Master\UserForm; use App\Livewire\Forms\Studio\Master\UserForm;
use App\Traits\WithAuthorization;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
@ -11,12 +12,14 @@
#[Title('Tambah Pegawai')] #[Title('Tambah Pegawai')]
class Create extends Component class Create extends Component
{ {
use WithToast, WithUpdatedData; use WithAuthorization, WithToast, WithUpdatedData;
public UserForm $form; public UserForm $form;
public function save() public function save()
{ {
$this->canOrAbort('create user');
$this->form->store(); $this->form->store();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Master\UserForm; use App\Livewire\Forms\Studio\Master\UserForm;
use App\Models\User; use App\Models\User;
use App\Traits\WithAuthorization;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
@ -12,7 +13,7 @@
#[Title('Ubah Pegawai')] #[Title('Ubah Pegawai')]
class Edit extends Component class Edit extends Component
{ {
use WithToast, WithUpdatedData; use WithAuthorization, WithToast, WithUpdatedData;
public UserForm $form; public UserForm $form;
@ -23,6 +24,8 @@ public function mount(User $user)
public function save() public function save()
{ {
$this->canOrAbort('update user');
$this->form->update(); $this->form->update();
$this->dispatch('refreshDatatable'); $this->dispatch('refreshDatatable');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Setting\AccountForm; use App\Livewire\Forms\Studio\Setting\AccountForm;
use App\Models\User; use App\Models\User;
use App\Traits\WithAuthorization;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
@ -12,7 +13,7 @@
#[Title('Akun')] #[Title('Akun')]
class Account extends Component class Account extends Component
{ {
use WithToast, WithUpdatedData; use WithAuthorization, WithToast, WithUpdatedData;
public AccountForm $form; public AccountForm $form;
@ -23,6 +24,8 @@ public function mount(User $user)
public function update() public function update()
{ {
$this->canOrAbort('update account');
$this->form->update(); $this->form->update();
$this->toast('Akun berhasil diperbarui.'); $this->toast('Akun berhasil diperbarui.');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Setting\PasswordForm; use App\Livewire\Forms\Studio\Setting\PasswordForm;
use App\Models\User; use App\Models\User;
use App\Traits\WithAuthorization;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
@ -13,7 +14,7 @@
#[Title('Kata Sandi')] #[Title('Kata Sandi')]
class Password extends Component class Password extends Component
{ {
use WithToast, WithUpdatedData; use WithAuthorization, WithToast, WithUpdatedData;
public PasswordForm $form; public PasswordForm $form;
@ -24,6 +25,8 @@ public function mount(User $user)
public function update() public function update()
{ {
$this->canOrAbort('update password');
if (! Hash::check($this->form->current_password, $this->form->user->password)) { if (! Hash::check($this->form->current_password, $this->form->user->password)) {
$this->toast('Kata sandi yang Anda masukkan tidak sesuai dengan kata sandi saat ini.', 'Gagal', 'danger'); $this->toast('Kata sandi yang Anda masukkan tidak sesuai dengan kata sandi saat ini.', 'Gagal', 'danger');

View File

@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Setting\ProfileForm; use App\Livewire\Forms\Studio\Setting\ProfileForm;
use App\Models\Employee; use App\Models\Employee;
use App\Traits\WithAuthorization;
use App\Traits\WithToast; use App\Traits\WithToast;
use App\Traits\WithUpdatedData; use App\Traits\WithUpdatedData;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
@ -12,7 +13,7 @@
#[Title('Profil')] #[Title('Profil')]
class Profile extends Component class Profile extends Component
{ {
use WithToast, WithUpdatedData; use WithAuthorization, WithToast, WithUpdatedData;
public ProfileForm $form; public ProfileForm $form;
@ -23,6 +24,8 @@ public function mount(Employee $employee)
public function update() public function update()
{ {
$this->canOrAbort('update profile');
$this->form->update(); $this->form->update();
$this->toast('Profil berhasil diperbarui.'); $this->toast('Profil berhasil diperbarui.');

View File

@ -0,0 +1,128 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
public function boot(): void
{
View::composer('*', function ($view) {
$sidebar = [
[
'heading' => 'Dasbor',
'items' => [
[
'label' => 'Ringkasan',
'icon' => 'squares-plus',
'route' => 'studio.dashboard.overview',
'match' => 'studio.dashboard.overview',
'can' => 'view overview',
],
],
],
[
'heading' => 'Master',
'items' => [
[
'label' => 'Outlet',
'icon' => 'home-modern',
'route' => 'studio.master.outlet.index',
'match' => 'studio.master.outlet.*',
'can' => 'view outlet',
],
[
'label' => 'Pegawai',
'icon' => 'users',
'route' => 'studio.master.user.index',
'match' => 'studio.master.user.*',
'can' => 'view user',
],
],
],
[
'heading' => 'Loyalty',
'items' => [
[
'label' => 'Tier',
'icon' => 'star',
'route' => 'studio.loyalty.tier.index',
'match' => 'studio.loyalty.tier.*',
'can' => 'view tier',
],
[
'label' => 'Voucher',
'icon' => 'ticket',
'route' => 'studio.loyalty.voucher.index',
'match' => 'studio.loyalty.voucher.*',
'can' => 'view voucher',
],
[
'label' => 'Customer',
'icon' => 'user-plus',
'route' => 'studio.loyalty.customer.index',
'match' => 'studio.loyalty.customer.*',
'can' => 'view customer',
],
],
],
[
'heading' => 'Katalog',
'items' => [
[
'label' => 'Kategori',
'icon' => 'list-bullet',
'route' => 'studio.catalog.category.index',
'match' => 'studio.catalog.category.*',
'can' => 'view category',
],
[
'label' => 'Merek',
'icon' => 'tag',
'route' => 'studio.catalog.brand.index',
'match' => 'studio.catalog.brand.*',
'can' => 'view brand',
],
[
'label' => 'Parfum',
'icon' => 'flower-2',
'route' => 'studio.catalog.perfume.index',
'match' => 'studio.catalog.perfume.*',
'can' => 'view perfume',
],
[
'label' => 'Produk',
'icon' => 'boxes',
'route' => 'studio.catalog.product.index',
'match' => 'studio.catalog.product.*',
'can' => 'view product',
],
[
'label' => 'Botol',
'icon' => 'beaker',
'route' => 'studio.catalog.bottle.index',
'match' => 'studio.catalog.bottle.*',
'can' => 'view bottle',
],
],
],
[
'heading' => 'Keuangan',
'items' => [
[
'label' => 'Pengeluaran',
'icon' => 'banknote-arrow-down',
'route' => 'studio.finance.expense.index',
'match' => 'studio.finance.expense.*',
'can' => 'view expense',
],
],
],
];
$view->with('sidebar', $sidebar);
});
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Traits;
trait WithAuthorization
{
public function canOrAbort(string $ability, $model = null): void
{
if (auth()->user()->cannot($ability, $model)) {
abort(403);
}
}
}

View File

@ -2,4 +2,5 @@
return [ return [
App\Providers\AppServiceProvider::class, App\Providers\AppServiceProvider::class,
App\Providers\ViewServiceProvider::class,
]; ];

View File

@ -9,6 +9,7 @@ class DatabaseSeeder extends Seeder
public function run(): void public function run(): void
{ {
$this->call([ $this->call([
RolePermissionSeeder::class,
OutletSeeder::class, OutletSeeder::class,
UserSeeder::class, UserSeeder::class,
TierSeeder::class, TierSeeder::class,

View File

@ -0,0 +1,194 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class RolePermissionSeeder extends Seeder
{
public function run(): void
{
$developer = Role::firstOrCreate(['name' => 'Developer']);
$owner = Role::firstOrCreate(['name' => 'Owner']);
$leader = Role::firstOrCreate(['name' => 'Leader']);
$admin = Role::firstOrCreate(['name' => 'Admin']);
$partner = Role::firstOrCreate(['name' => 'Partner']);
$customer = Role::firstOrCreate(['name' => 'Customer']);
$permissions = [
'view account',
'update account',
'view profile',
'update profile',
'view password',
'update password',
'view overview',
'view outlet',
'create outlet',
'update outlet',
'delete outlet',
'view user',
'create user',
'update user',
'delete user',
'view tier',
'create tier',
'update tier',
'delete tier',
'view voucher',
'create voucher',
'update voucher',
'delete voucher',
'view customer',
'create customer',
'update customer',
'delete customer',
'view category',
'create category',
'update category',
'delete category',
'view brand',
'create brand',
'update brand',
'delete brand',
'view perfume',
'create perfume',
'update perfume',
'delete perfume',
'view product',
'create product',
'update product',
'delete product',
'view bottle',
'create bottle',
'update bottle',
'delete bottle',
'view expense',
'create expense',
'update expense',
'delete expense',
];
foreach ($permissions as $permission) {
Permission::firstOrCreate(['name' => $permission]);
}
$developer->syncPermissions($permissions);
$owner->syncPermissions($permissions);
$leader->syncPermissions(array_diff($permissions, [
'view outlet',
'create outlet',
'update outlet',
'delete outlet',
'view user',
'create user',
'update user',
'delete user',
'create tier',
'update tier',
'delete tier',
'create voucher',
'update voucher',
'delete voucher',
'create perfume',
'update perfume',
'delete perfume',
'create product',
'update product',
'delete product',
'create bottle',
'update bottle',
'delete bottle',
]));
$admin->syncPermissions(array_diff($permissions, [
'view outlet',
'create outlet',
'update outlet',
'delete outlet',
'view user',
'create user',
'update user',
'delete user',
'create tier',
'update tier',
'delete tier',
'create voucher',
'update voucher',
'delete voucher',
'create category',
'update category',
'delete category',
'create brand',
'update brand',
'delete brand',
'create perfume',
'update perfume',
'delete perfume',
'create product',
'update product',
'delete product',
'create bottle',
'update bottle',
'delete bottle',
]));
$partner->syncPermissions([
'view overview',
'view account',
'update account',
'view profile',
'update profile',
'view password',
'update password',
]);
$customer->syncPermissions([
'view overview',
'view account',
'update account',
'view profile',
'update profile',
'view password',
'update password',
]);
}
}

View File

@ -4,6 +4,7 @@
use App\Enums\EmploymentStatus; use App\Enums\EmploymentStatus;
use App\Enums\Gender; use App\Enums\Gender;
use App\Enums\UserRole;
use App\Models\Employee; use App\Models\Employee;
use App\Models\ReferralCode; use App\Models\ReferralCode;
use App\Models\User; use App\Models\User;
@ -36,5 +37,7 @@ public function run(): void
'user_id' => $user->id, 'user_id' => $user->id,
'code' => generateReferralCode('pangestu', 1), 'code' => generateReferralCode('pangestu', 1),
]); ]);
$user->assignRole(UserRole::Developer);
} }
} }

View File

@ -1,75 +1,41 @@
<flux:sidebar sticky stashable <flux:sidebar sticky stashable
class="bg-zinc-50 dark:bg-zinc-900 border-r rtl:border-r-0 rtl:border-l border-zinc-200 dark:border-zinc-700"> class="bg-zinc-50 dark:bg-zinc-900 border-r rtl:border-r-0 rtl:border-l border-zinc-200 dark:border-zinc-700">
<flux:sidebar.toggle class="lg:hidden" icon="x-mark" /> <flux:sidebar.toggle class="lg:hidden" icon="x-mark" />
<flux:brand logo="{{ asset('assets/images/logo.png') }}" name="Yadi Parfum" class="px-2 dark:hidden" /> <flux:brand logo="{{ asset('assets/images/logo.png') }}" name="Yadi Parfum" class="px-2 dark:hidden" />
<flux:brand logo="{{ asset('assets/images/dark-logo.png') }}" name="Yadi Parfum" class="px-2 hidden dark:flex" /> <flux:brand logo="{{ asset('assets/images/dark-logo.png') }}" name="Yadi Parfum" class="px-2 hidden dark:flex" />
<flux:navlist variant="outline"> <flux:navlist variant="outline">
<flux:navlist.item icon="squares-plus" href="{{ route('studio.dashboard.overview') }}" @foreach ($sidebar as $section)
:current="request()->routeIs('studio.dashboard.overview')" wire:navigate.hover> @php
Ringkasan</flux:navlist.item> $items = collect($section['items'])->filter(function ($item) {
return !isset($item['can']) || auth()->user()?->can($item['can']);
});
@endphp
@if ($items->isNotEmpty())
@if ($section['heading'])
<div class="mt-3 mb-1"> <div class="mt-3 mb-1">
<div class="text-zinc-500 dark:text-gray-300 text-sm/6">Master</div> <div class="text-zinc-500 dark:text-gray-300 text-sm/6">{{ $section['heading'] }}</div>
<div class="grid gap-2"> <div class="grid gap-2">
<flux:navlist.item icon="home-modern" href="{{ route('studio.master.outlet.index') }}" @foreach ($items as $item)
:current="request()->routeIs('studio.master.outlet.*')" wire:navigate.hover>Outlet <flux:navlist.item icon="{{ $item['icon'] }}" href="{{ route($item['route']) }}"
</flux:navlist.item> :current="request()->routeIs($item['match'])" wire:navigate.hover>
{{ $item['label'] }}
<flux:navlist.item icon="users" href="{{ route('studio.master.user.index') }}"
:current="request()->routeIs('studio.master.user.*')" wire:navigate.hover>Pegawai
</flux:navlist.item> </flux:navlist.item>
@endforeach
</div> </div>
</div> </div>
@else
<div class="mt-3 mb-1"> @foreach ($items as $item)
<div class="text-zinc-500 dark:text-gray-300 text-sm/6">Loyalty</div> <flux:navlist.item icon="{{ $item['icon'] }}" href="{{ route($item['route']) }}"
<div class="grid gap-2"> :current="request()->routeIs($item['match'])" wire:navigate.hover>
<flux:navlist.item icon="star" href="{{ route('studio.loyalty.tier.index') }}" {{ $item['label'] }}
:current="request()->routeIs('studio.loyalty.tier.*')" wire:navigate.hover>Tier
</flux:navlist.item> </flux:navlist.item>
@endforeach
<flux:navlist.item icon="ticket" href="{{ route('studio.loyalty.voucher.index') }}" @endif
:current="request()->routeIs('studio.loyalty.voucher.*')" wire:navigate.hover>Voucher @endif
</flux:navlist.item> @endforeach
<flux:navlist.item icon="user-plus" href="{{ route('studio.loyalty.customer.index') }}"
:current="request()->routeIs('studio.loyalty.customer.*')" wire:navigate.hover>Customer
</flux:navlist.item>
</div>
</div>
<div class="mt-3 mb-1">
<div class="text-zinc-500 dark:text-gray-300 text-sm/6">Katalog</div>
<div class="grid gap-2">
<flux:navlist.item icon="list-bullet" href="{{ route('studio.catalog.category.index') }}"
:current="request()->routeIs('studio.catalog.category.*')" wire:navigate.hover>Kategori
</flux:navlist.item>
<flux:navlist.item icon="tag" href="{{ route('studio.catalog.brand.index') }}"
:current="request()->routeIs('studio.catalog.brand.*')" wire:navigate.hover>Merek
</flux:navlist.item>
<flux:navlist.item icon="flower-2" href="{{ route('studio.catalog.perfume.index') }}"
:current="request()->routeIs('studio.catalog.perfume.*')" wire:navigate.hover>Parfum
</flux:navlist.item>
<flux:navlist.item icon="boxes" href="{{ route('studio.catalog.product.index') }}"
:current="request()->routeIs('studio.catalog.product.*')" wire:navigate.hover>Produk
</flux:navlist.item>
<flux:navlist.item icon="beaker" href="{{ route('studio.catalog.bottle.index') }}"
:current="request()->routeIs('studio.catalog.bottle.*')" wire:navigate.hover>Botol
</flux:navlist.item>
</div>
</div>
<div class="mt-3 mb-1">
<div class="text-zinc-500 dark:text-gray-300 text-sm/6">Keuangan</div>
<div class="grid gap-2">
<flux:navlist.item icon="banknote-arrow-down" href="{{ route('studio.finance.expense.index') }}"
:current="request()->routeIs('studio.finance.expense.*')" wire:navigate.hover>Pengeluaran
</flux:navlist.item>
</div>
</div>
</flux:navlist> </flux:navlist>
</flux:sidebar> </flux:sidebar>

View File

@ -3,12 +3,14 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create bottle'))
<div> <div>
<flux:button href="{{ route('studio.catalog.bottle.create') }}" variant="primary" wire:navigate.hover <flux:button href="{{ route('studio.catalog.bottle.create') }}" variant="primary" wire:navigate.hover
class="text-sm"> class="text-sm">
Tambah Tambah
</flux:button> </flux:button>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,6 +3,7 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create brand'))
<div> <div>
<flux:modal.trigger name="form-modal"> <flux:modal.trigger name="form-modal">
<flux:button variant="primary" class="text-sm" <flux:button variant="primary" class="text-sm"
@ -10,6 +11,7 @@
</flux:button> </flux:button>
</flux:modal.trigger> </flux:modal.trigger>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,6 +3,7 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create category'))
<div> <div>
<flux:modal.trigger name="form-modal"> <flux:modal.trigger name="form-modal">
<flux:button variant="primary" class="text-sm" <flux:button variant="primary" class="text-sm"
@ -10,6 +11,7 @@
</flux:button> </flux:button>
</flux:modal.trigger> </flux:modal.trigger>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,12 +3,14 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create perfume'))
<div> <div>
<flux:button href="{{ route('studio.catalog.perfume.create') }}" variant="primary" wire:navigate.hover <flux:button href="{{ route('studio.catalog.perfume.create') }}" variant="primary" wire:navigate.hover
class="text-sm"> class="text-sm">
Tambah Tambah
</flux:button> </flux:button>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,12 +3,14 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create product'))
<div> <div>
<flux:button href="{{ route('studio.catalog.product.create') }}" variant="primary" wire:navigate.hover <flux:button href="{{ route('studio.catalog.product.create') }}" variant="primary" wire:navigate.hover
class="text-sm"> class="text-sm">
Tambah Tambah
</flux:button> </flux:button>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,13 +3,16 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create expense'))
<div> <div>
<flux:modal.trigger name="form-modal"> <flux:modal.trigger name="form-modal">
<flux:button variant="primary" class="text-sm" <flux:button variant="primary" class="text-sm"
wire:click="$dispatch('modal:open', {method: 'create', 'modalTitle': 'Tambah Pengeluaran'})">Tambah wire:click="$dispatch('modal:open', {method: 'create', 'modalTitle': 'Tambah Pengeluaran'})">
Tambah
</flux:button> </flux:button>
</flux:modal.trigger> </flux:modal.trigger>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,6 +3,7 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create customer'))
<div> <div>
<flux:modal.trigger name="form-modal"> <flux:modal.trigger name="form-modal">
<flux:button variant="primary" class="text-sm" <flux:button variant="primary" class="text-sm"
@ -10,6 +11,7 @@
</flux:button> </flux:button>
</flux:modal.trigger> </flux:modal.trigger>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,6 +3,7 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create tier'))
<div> <div>
<flux:modal.trigger name="form-modal"> <flux:modal.trigger name="form-modal">
<flux:button variant="primary" class="text-sm" <flux:button variant="primary" class="text-sm"
@ -10,6 +11,7 @@
</flux:button> </flux:button>
</flux:modal.trigger> </flux:modal.trigger>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,12 +3,14 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create voucher'))
<div> <div>
<flux:button href="{{ route('studio.loyalty.voucher.create') }}" variant="primary" wire:navigate.hover <flux:button href="{{ route('studio.loyalty.voucher.create') }}" variant="primary" wire:navigate.hover
class="text-sm"> class="text-sm">
Tambah Tambah
</flux:button> </flux:button>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,12 +3,14 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create outlet'))
<div> <div>
<flux:button href="{{ route('studio.master.outlet.create') }}" variant="primary" wire:navigate.hover <flux:button href="{{ route('studio.master.outlet.create') }}" variant="primary" wire:navigate.hover
class="text-sm"> class="text-sm">
Tambah Tambah
</flux:button> </flux:button>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -3,12 +3,14 @@
<div> <div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading> <flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div> </div>
@if (auth()->user()->can('create user'))
<div> <div>
<flux:button href="{{ route('studio.master.user.create') }}" variant="primary" wire:navigate.hover <flux:button href="{{ route('studio.master.user.create') }}" variant="primary" wire:navigate.hover
class="text-sm"> class="text-sm">
Tambah Tambah
</flux:button> </flux:button>
</div> </div>
@endif
</div> </div>
<div class="mt-6"> <div class="mt-6">

View File

@ -30,9 +30,11 @@
</div> </div>
</div> </div>
@if (auth()->user()->can('update account'))
<div class="flex justify-start mt-4"> <div class="flex justify-start mt-4">
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update"> <flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update">
Simpan Simpan
</flux:button> </flux:button>
</div> </div>
@endif
</flux:main> </flux:main>

View File

@ -32,9 +32,11 @@
</div> </div>
</div> </div>
@if (auth()->user()->can('update password'))
<div class="flex justify-start mt-4"> <div class="flex justify-start mt-4">
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update"> <flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update">
Simpan Simpan
</flux:button> </flux:button>
</div> </div>
@endif
</flux:main> </flux:main>

View File

@ -81,9 +81,11 @@
</div> </div>
</div> </div>
@if (auth()->user()->can('update profile'))
<div class="flex justify-start mt-4"> <div class="flex justify-start mt-4">
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update"> <flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="update">
Simpan Simpan
</flux:button> </flux:button>
</div> </div>
@endif
</flux:main> </flux:main>

View File

@ -29,104 +29,89 @@
use App\Livewire\Studio\Setting\Profile as ProfileComponent; use App\Livewire\Studio\Setting\Profile as ProfileComponent;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
Route::middleware('auth') Route::middleware('auth')->prefix('studio')->group(function () {
->prefix('studio') Route::prefix('setting')->name('studio.setting.')->group(function () {
->group(function () { Route::get('/account/{user}', AccountComponent::class)->name('account')->middleware('can:view account');
Route::prefix('setting') Route::get('/profile/{employee}', ProfileComponent::class)->name('profile')->middleware('can:view profile');
->name('studio.setting.') Route::get('/password/{user}', PasswordComponent::class)->name('password')->middleware('can:view password');
->group(function () {
Route::get('/account/{user}', AccountComponent::class)->name('account');
Route::get('/profile/{employee}', ProfileComponent::class)->name('profile');
Route::get('/password/{user}', PasswordComponent::class)->name('password');
}); });
Route::prefix('dashboard') Route::prefix('dashboard')->name('studio.dashboard.')->group(function () {
->name('studio.dashboard.') Route::get('/overview', OverviewComponent::class)->name('overview')->middleware('can:view overview');
->group(function () {
Route::get('/overview', OverviewComponent::class)->name('overview');
}); });
Route::prefix('master') Route::prefix('master')->name('studio.master.')->group(function () {
->name('studio.master.')
->group(function () {
Route::prefix('outlets')->name('outlet.')->group(function () { Route::prefix('outlets')->name('outlet.')->group(function () {
Route::get('/', OutletIndex::class)->name('index'); Route::get('/', OutletIndex::class)->name('index')->middleware('can:view outlet');
Route::get('/create', OutletCreate::class)->name('create'); Route::get('/create', OutletCreate::class)->name('create')->middleware('can:create outlet');
Route::get('/{outlet}/edit', OutletEdit::class)->name('edit'); Route::get('/{outlet}/edit', OutletEdit::class)->name('edit')->middleware('can:edit outlet');
Route::delete('/{outlet}/delete', OutletCreate::class)->name('delete'); Route::delete('/{outlet}/delete', OutletCreate::class)->name('delete')->middleware('can:delete outlet');
}); });
Route::prefix('users')->name('user.')->group(function () { Route::prefix('users')->name('user.')->group(function () {
Route::get('/', UserIndex::class)->name('index'); Route::get('/', UserIndex::class)->name('index')->middleware('can:view user');
Route::get('/create', UserCreate::class)->name('create'); Route::get('/create', UserCreate::class)->name('create')->middleware('can:create user');
Route::get('/{user}/edit', UserEdit::class)->name('edit'); Route::get('/{user}/edit', UserEdit::class)->name('edit')->middleware('can:edit user');
Route::delete('/{user}/delete', UserCreate::class)->name('delete'); Route::delete('/{user}/delete', UserCreate::class)->name('delete')->middleware('can:delete user');
}); });
}); });
Route::prefix('loyalty') Route::prefix('loyalty')->name('studio.loyalty.')->group(function () {
->name('studio.loyalty.')
->group(function () {
Route::prefix('tiers')->name('tier.')->group(function () { Route::prefix('tiers')->name('tier.')->group(function () {
Route::get('/', TierComponent::class)->name('index'); Route::get('/', TierComponent::class)->name('index')->middleware('can:view tier');
Route::delete('/{tier}/delete', TierComponent::class)->name('delete'); Route::delete('/{tier}/delete', TierComponent::class)->name('delete')->middleware('can:delete tier');
}); });
Route::prefix('customers')->name('customer.')->group(function () { Route::prefix('customers')->name('customer.')->group(function () {
Route::get('/', CustomerComponent::class)->name('index'); Route::get('/', CustomerComponent::class)->name('index')->middleware('can:view customer');
Route::delete('/{customer}/delete', CustomerComponent::class)->name('delete'); Route::delete('/{customer}/delete', CustomerComponent::class)->name('delete')->middleware('can:delete customer');
}); });
Route::prefix('vouchers')->name('voucher.')->group(function () { Route::prefix('vouchers')->name('voucher.')->group(function () {
Route::get('/', VoucherIndex::class)->name('index'); Route::get('/', VoucherIndex::class)->name('index')->middleware('can:view voucher');
Route::get('/create', VoucherCreate::class)->name('create'); Route::get('/create', VoucherCreate::class)->name('create')->middleware('can:create voucher');
Route::get('/{voucher}/edit', VoucherEdit::class)->name('edit'); Route::get('/{voucher}/edit', VoucherEdit::class)->name('edit')->middleware('can:edit voucher');
Route::delete('/{voucher}/delete', VoucherCreate::class)->name('delete'); Route::delete('/{voucher}/delete', VoucherCreate::class)->name('delete')->middleware('can:delete voucher');
}); });
}); });
Route::prefix('catalog') Route::prefix('catalog')->name('studio.catalog.')->group(function () {
->name('studio.catalog.')
->group(function () {
Route::prefix('categories')->name('category.')->group(function () { Route::prefix('categories')->name('category.')->group(function () {
Route::get('/', CategoryComponent::class)->name('index'); Route::get('/', CategoryComponent::class)->name('index')->middleware('can:view category');
Route::delete('/{category}/delete', CategoryComponent::class)->name('delete'); Route::delete('/{category}/delete', CategoryComponent::class)->name('delete')->middleware('can:delete category');
}); });
Route::prefix('brands')->name('brand.')->group(function () { Route::prefix('brands')->name('brand.')->group(function () {
Route::get('/', BrandComponent::class)->name('index'); Route::get('/', BrandComponent::class)->name('index')->middleware('can:view brand');
Route::delete('/{brand}/delete', BrandComponent::class)->name('delete'); Route::delete('/{brand}/delete', BrandComponent::class)->name('delete')->middleware('can:delete brand');
}); });
Route::prefix('perfumes')->name('perfume.')->group(function () { Route::prefix('perfumes')->name('perfume.')->group(function () {
Route::get('/', PerfumeIndex::class)->name('index'); Route::get('/', PerfumeIndex::class)->name('index')->middleware('can:view perfume');
Route::get('/create', PerfumeCreate::class)->name('create'); Route::get('/create', PerfumeCreate::class)->name('create')->middleware('can:create perfume');
Route::get('/{perfume}/edit', PerfumeEdit::class)->name('edit'); Route::get('/{perfume}/edit', PerfumeEdit::class)->name('edit')->middleware('can:edit perfume');
Route::delete('/{perfume}/delete', PerfumeCreate::class)->name('delete'); Route::delete('/{perfume}/delete', PerfumeCreate::class)->name('delete')->middleware('can:delete perfume');
}); });
Route::prefix('products')->name('product.')->group(function () { Route::prefix('products')->name('product.')->group(function () {
Route::get('/', ProductIndex::class)->name('index'); Route::get('/', ProductIndex::class)->name('index')->middleware('can:view product');
Route::get('/create', ProductCreate::class)->name('create'); Route::get('/create', ProductCreate::class)->name('create')->middleware('can:create product');
Route::get('/{product}/edit', ProductEdit::class)->name('edit'); Route::get('/{product}/edit', ProductEdit::class)->name('edit')->middleware('can:edit product');
Route::delete('/{product}/delete', ProductCreate::class)->name('delete'); Route::delete('/{product}/delete', ProductCreate::class)->name('delete')->middleware('can:delete product');
}); });
Route::prefix('bottles')->name('bottle.')->group(function () { Route::prefix('bottles')->name('bottle.')->group(function () {
Route::get('/', BottleIndex::class)->name('index'); Route::get('/', BottleIndex::class)->name('index')->middleware('can:view bottle');
Route::get('/create', BottleCreate::class)->name('create'); Route::get('/create', BottleCreate::class)->name('create')->middleware('can:create bottle');
Route::get('/{bottle}/edit', BottleEdit::class)->name('edit'); Route::get('/{bottle}/edit', BottleEdit::class)->name('edit')->middleware('can:edit bottle');
Route::get('/{bottle}/delete', BottleCreate::class)->name('delete'); Route::delete('/{bottle}/delete', BottleCreate::class)->name('delete')->middleware('can:delete bottle');
Route::get('/{bottle}/delete', BottleCreate::class)->name('delete');
}); });
}); });
Route::prefix('finance') Route::prefix('finance')->name('studio.finance.')->group(function () {
->name('studio.finance.')
->group(function () {
Route::prefix('expenses')->name('expense.')->group(function () { Route::prefix('expenses')->name('expense.')->group(function () {
Route::get('/', ExpenseComponent::class)->name('index'); Route::get('/', ExpenseComponent::class)->name('index')->middleware('can:view expense');
Route::delete('/{expense}/delete', ExpenseComponent::class)->name('delete'); Route::delete('/{expense}/delete', ExpenseComponent::class)->name('delete')->middleware();
});
}); });
}); });
});