Merge branch 'feature/theming' of github.com:laravel-filament/filament into feature/theming

This commit is contained in:
Ryan Chandler
2021-03-10 10:38:39 +00:00
66 changed files with 1421 additions and 449 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,4 +1,4 @@
{
"/js/filament.js": "/js/filament.js?id=a3029b69d87820f78653",
"/css/filament.css": "/css/filament.css?id=aeaa524e8752927a70a9"
"/css/filament.css": "/css/filament.css?id=258be1ef621afe17391c"
}
@@ -0,0 +1,88 @@
<?php
return [
'dateTimePicker' => [
'abbreviatedDays' => [
'friday' => 'Jum',
'monday' => 'Sen',
'saturday' => 'Sab',
'sunday' => 'Min',
'thursday' => 'Kam',
'tuesday' => 'Sel',
'wednesday' => 'Rab',
],
'months' => [
'april' => 'April',
'august' => 'Agustus',
'december' => 'Desember',
'february' => 'Februari',
'january' => 'Januari',
'july' => 'Juli',
'june' => 'Juni',
'march' => 'Maret',
'may' => 'Mei',
'november' => 'November',
'october' => 'Oktober',
'september' => 'September',
],
],
'multiSelect' => [
'emptyOptionsMessage' => 'Tidak ada opsi yang bisa dipilih.',
'noSearchResultsMessage' => 'Tidak ada opsi yang sesuai pencarian anda.',
'placeholder' => 'Pilih Opsi',
],
'richEditor' => [
'dialogs' => [
'link' => [
'buttons' => [
'link' => 'Tautan',
'unlink' => 'Batalkan tautan',
],
'label' => 'Tautan',
'placeholder' => 'Masukkan tautan',
],
],
'toolbarButtons' => [
'attachFiles' => 'Lampirkan File',
'bold' => 'Bold',
'bullet' => 'Bullet',
'code' => 'Kode',
'heading' => 'Judul',
'italic' => 'Italic',
'link' => 'Tautan',
'number' => 'Nomor',
'quote' => 'Quote',
'redo' => 'Redo',
'strike' => 'Coret',
'subheading' => 'Anak judul',
'title' => 'Judul',
'undo' => 'Undo',
],
],
'select' => [
'emptyOptionsMessage' => 'Mulai mengetik untuk mencari...',
'noSearchResultsMessage' => 'Tidak ada opsi yang sesuai pencarian anda.',
'placeholder' => 'Pilih salah satu opsi',
],
'tags' => [
'placeholder' => 'Tag baru',
],
];
+3 -3
View File
@@ -262,7 +262,7 @@ class Component
{
$this->record = $record;
if ($this->record) {
if ($this->record instanceof Model) {
$this->model(get_class($this->record));
}
@@ -276,7 +276,7 @@ class Component
}
if ($shouldExecuteCallback) {
$callback($this);
$callback($this, $this->record);
}
}
@@ -320,7 +320,7 @@ class Component
}
if ($shouldExecuteCallback) {
$callback($this);
$callback($this, $this->record);
}
return $this;
@@ -40,12 +40,6 @@ return [
],
],
'record' => [
'button' => [
'label' => 'Bearbeiten',
],
],
'search' => [
'placeholder' => 'Suche',
],
@@ -50,14 +50,6 @@ return [
],
'record' => [
'button' => [
'label' => 'Edit',
],
],
'search' => [
'placeholder' => 'Search',
],
@@ -50,14 +50,6 @@ return [
],
'record' => [
'button' => [
'label' => 'Editar',
],
],
'search' => [
'placeholder' => 'Buscar',
],
@@ -50,14 +50,6 @@ return [
],
'record' => [
'button' => [
'label' => 'Modifier',
],
],
'search' => [
'placeholder' => 'Rechercher',
],
@@ -0,0 +1,20 @@
<?php
return [
'buttons' => [
'goToPage' => 'Ke halaman :page',
'next' => 'Selanjutnya',
'previous' => 'Sebelumnya',
],
'label' => 'Navigasi halaman',
'overview' => [
'Menampilkan',
'sampai',
'dari',
'hasil',
],
];
@@ -0,0 +1,57 @@
<?php
return [
'delete' => [
'button' => [
'label' => 'Hapus yang dipilih',
],
'modal' => [
'buttons' => [
'cancel' => [
'label' => 'Batalkan',
],
'delete' => [
'label' => 'Hapus yang terpilih',
],
],
'description' => 'Apakah anda yakin ingin menghapus data yang dipilih? Aksi ini tidak bisa dibatalkan..',
'heading' => 'Hapus data yang dipilih?',
],
],
'filter' => [
'placeholder' => 'Filter',
],
'messages' => [
'noRecords' => 'Data tidak ditemukan',
],
'pagination' => [
'fields' => [
'recordsPerPage' => [
'label' => 'per halaman',
],
],
],
'search' => [
'placeholder' => 'Cari',
],
];
@@ -50,14 +50,6 @@ return [
],
'record' => [
'button' => [
'label' => 'Modifica',
],
],
'search' => [
'placeholder' => 'Cerca',
],
@@ -41,12 +41,6 @@ return [
],
],
'record' => [
'button' => [
'label' => 'Bewerk',
],
],
'search' => [
'placeholder' => 'Zoeken',
],
@@ -50,14 +50,6 @@ return [
],
'record' => [
'button' => [
'label' => 'Editar',
],
],
'search' => [
'placeholder' => 'Procurar',
],
@@ -8,7 +8,7 @@
<div {{ $attributes->merge(['class' => 'space-y-4']) }}>
<div class="items-center justify-between space-y-4 sm:flex sm:space-y-0">
<x-tables::delete-selected :selected="$selected" />
<x-tables::delete-selected :disabled="count($selected) === 0" />
<x-tables::filter :table="$table" />
</div>
@@ -1,5 +1,5 @@
@props([
'selected' => [],
'disabled' => true,
])
<span
@@ -18,10 +18,10 @@
x-cloak
>
<button
type="button"
x-on:click="open = true"
{!! count($selected) === 0 ? 'disabled' : null !!}
class="{{ count($selected) === 0 ? 'opacity-25 cursor-not-allowed' : 'cursor-pointer' }} font-medium border rounded transition duration-200 shadow-sm focus:ring focus:ring-opacity-50 text-sm py-2 px-4 border-transparent bg-danger-600 text-white hover:bg-danger-700 focus:ring-danger-200"
type="button"
x-on:click="open = true"
{!! $disabled ? 'disabled' : null !!}
class="{{ $disabled ? 'opacity-25 cursor-not-allowed' : 'cursor-pointer' }} font-medium border rounded transition duration-200 shadow-sm focus:ring focus:ring-opacity-50 text-sm py-2 px-4 border-transparent bg-danger-600 text-white hover:bg-danger-700 focus:ring-danger-200"
>
{{ __('tables::table.delete.button.label') }}
</button>
@@ -49,11 +49,7 @@
</th>
@endforeach
@if ($table->recordButtonLabel)
<th scope="col">
<span class="sr-only">{{ __($table->recordButtonLabel) }}</span>
</th>
@endif
<th scope="col"></th>
</tr>
</thead>
@@ -78,32 +74,17 @@
</td>
@endforeach
@if ($table->recordButtonLabel)
<td class="px-6 py-4 text-right whitespace-nowrap">
@if ($table->recordAction)
<button
wire:click="{{ $table->recordAction }}('{{ $record->getKey() }}')"
type="button"
class="font-medium transition-colors duration-200 text-primary-600 hover:underline hover:text-primary-700"
>
{{ __($table->recordButtonLabel) }}
</button>
@elseif ($table->recordUrl)
<a
href="{{ $table->getRecordUrl($record) }}"
class="font-medium transition-colors duration-200 text-primary-600 hover:underline hover:text-primary-700"
>
{{ __($table->recordButtonLabel) }}
</a>
@endif
</td>
@endif
<td class="px-6 py-4 text-right whitespace-nowrap">
@foreach ($table->recordActions as $recordAction)
{{ $recordAction->render($record) }}
@endforeach
</td>
</tr>
@empty
<tr>
<td
class="px-6 py-4 whitespace-nowrap"
colspan="{{ count($table->getVisibleColumns()) + 1 + ($table->recordButtonLabel ? 1 : 0) }}"
colspan="{{ count($table->getVisibleColumns()) + 2 }}"
>
<div class="flex items-center justify-center h-16">
<p class="font-mono text-xs text-gray-500">
@@ -0,0 +1,22 @@
@if ($recordAction->action)
<button
wire:click="{{ $recordAction->action }}('{{ $record->getKey() }}')"
type="button"
class="font-medium transition-colors duration-200 text-primary-600 hover:underline hover:text-primary-700"
>
{{ __($recordAction->label) }}
</button>
@elseif ($recordAction->url)
<a
href="{{ $recordAction->getUrl($record) }}"
class="font-medium transition-colors duration-200 text-primary-600 hover:underline hover:text-primary-700"
@if ($recordAction->shouldOpenUrlInNewTab)
target="_blank"
rel="noopener noreferrer"
@endif
>
{{ __($recordAction->label) }}
</a>
@else
<span>{{ __($recordAction->label) }}</span>
@endif
+3 -1
View File
@@ -174,6 +174,8 @@ class Column
->replace(['-', '_', '.'], ' ')
->ucfirst(),
);
return $this;
}
public function only($contexts, $callback = null)
@@ -253,7 +255,7 @@ class Column
{
if ($this->hidden) return;
$view = $this->view ?? 'tables::cells.'.Str::of(class_basename(static::class))->kebab();
$view = $this->view ?? 'tables::cells.' . Str::of(class_basename(static::class))->kebab();
return view($view, array_merge($this->viewData, [
'column' => $this,
+10
View File
@@ -3,6 +3,7 @@
namespace Filament\Tables\Columns;
use Carbon\Carbon;
use Illuminate\Support\Str;
class Text extends Column
{
@@ -85,4 +86,13 @@ class Text extends Column
return $this;
}
public function limit($length = -1)
{
$this->formatUsing = function ($value) use ($length) {
return Str::limit($value, $length);
};
return $this;
}
}
@@ -0,0 +1,82 @@
<?php
namespace Filament\Tables\RecordActions;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Tappable;
class Action
{
use Tappable;
public $name;
public $view;
public $viewData = [];
public $when;
public function __construct($name)
{
$this->name($name);
$this->when(fn ($record) => true);
$this->setUp();
}
public static function make($name)
{
return new static($name);
}
protected function setUp()
{
//
}
public function name($name)
{
$this->name = $name;
return $this;
}
public function view($view, $data = [])
{
$this->view = $view;
$this->viewData($data);
return $this;
}
public function viewData($data = [])
{
$this->viewData = array_merge($this->viewData, $data);
return $this;
}
public function when($callback)
{
$this->when = $callback;
return $this;
}
public function render($record)
{
$when = $this->when;
if (! $when($record)) return;
$view = $this->view ?? 'tables::record-actions.' . Str::of(class_basename(static::class))->kebab();
return view($view, array_merge($this->viewData, [
'record' => $record,
'recordAction' => $this,
]));
}
}
@@ -0,0 +1,15 @@
<?php
namespace Filament\Tables\RecordActions\Concerns;
trait CanCallAction
{
public $action;
public function action($action)
{
$this->action = $action;
return $this;
}
}
@@ -0,0 +1,36 @@
<?php
namespace Filament\Tables\RecordActions\Concerns;
trait CanOpenUrl
{
public $shouldOpenUrlInNewTab = false;
public $url;
public function getUrl($record)
{
if (is_callable($this->url)) {
$callback = $this->url;
return $callback($record);
}
return $this->url;
}
public function openUrlInNewTab()
{
$this->shouldOpenUrlInNewTab = true;
return $this;
}
public function url($url, $shouldOpenInNewTab = false)
{
$this->url = $url;
if ($shouldOpenInNewTab) $this->openUrlInNewTab();
return $this;
}
}
@@ -0,0 +1,34 @@
<?php
namespace Filament\Tables\RecordActions;
use Illuminate\Support\Str;
class Link extends Action
{
use Concerns\CanCallAction;
use Concerns\CanOpenUrl;
public $label;
public function label($label)
{
$this->label = $label;
return $this;
}
public function name($name)
{
$this->name = $name;
$this->label(
(string) Str::of($this->name)
->kebab()
->replace(['-', '_', '.'], ' ')
->ucfirst(),
);
return $this;
}
}
+3 -52
View File
@@ -12,11 +12,7 @@ class Table
public $pagination = true;
public $recordAction;
public $recordButtonLabel = 'tables::table.record.button.label';
public $recordUrl;
public $recordActions = [];
public $searchable = true;
@@ -93,17 +89,6 @@ class Table
return $this;
}
public function getRecordUrl($record = null)
{
if (is_callable($this->recordUrl)) {
$callback = $this->recordUrl;
return $callback($record);
}
return $this->recordUrl;
}
public function getVisibleColumns()
{
$columns = collect($this->columns)
@@ -153,43 +138,9 @@ class Table
return $this;
}
public function recordAction($action)
public function recordActions($actions)
{
$this->recordAction = $action;
$this->columns = collect($this->columns)
->map(function ($column) {
if ($column->primary && ! $column->action) {
$column->action($this->recordAction);
}
return $column;
})
->toArray();
return $this;
}
public function recordButtonLabel($label)
{
$this->recordButtonLabel = $label;
return $this;
}
public function recordUrl($url)
{
$this->recordUrl = $url;
$this->columns = collect($this->columns)
->map(function ($column) {
if ($column->primary && ! $column->url) {
$column->url($this->recordUrl);
}
return $column;
})
->toArray();
$this->recordActions = $actions;
return $this;
}
+36 -36
View File
@@ -1,41 +1,41 @@
@layer base {
:root {
--f-primary-100: theme('colors.lightBlue.100');
--f-primary-200: theme('colors.lightBlue.200');
--f-primary-300: theme('colors.lightBlue.300');
--f-primary-400: theme('colors.lightBlue.400');
--f-primary-500: theme('colors.lightBlue.500');
--f-primary-600: theme('colors.lightBlue.600');
--f-primary-700: theme('colors.lightBlue.700');
--f-primary-800: theme('colors.lightBlue.800');
--f-primary-900: theme('colors.lightBlue.900');
--f-success-100: theme('colors.emerald.100');
--f-success-200: theme('colors.emerald.200');
--f-success-300: theme('colors.emerald.300');
--f-success-400: theme('colors.emerald.400');
--f-success-500: theme('colors.emerald.500');
--f-success-600: theme('colors.emerald.600');
--f-success-700: theme('colors.emerald.700');
--f-success-800: theme('colors.emerald.800');
--f-success-900: theme('colors.emerald.900');
--f-danger-100: theme('colors.rose.100');
--f-danger-200: theme('colors.rose.200');
--f-danger-300: theme('colors.rose.300');
--f-danger-400: theme('colors.rose.400');
--f-danger-500: theme('colors.rose.500');
--f-danger-600: theme('colors.rose.600');
--f-danger-700: theme('colors.rose.700');
--f-danger-800: theme('colors.rose.800');
--f-danger-900: theme('colors.rose.900');
--f-gray-100: theme('colors.coolGray.100');
--f-gray-200: theme('colors.coolGray.200');
--f-gray-300: theme('colors.coolGray.300');
--f-gray-400: theme('colors.coolGray.400');
--f-gray-500: theme('colors.coolGray.500');
--f-gray-600: theme('colors.coolGray.600');
--f-gray-700: theme('colors.coolGray.700');
--f-gray-800: theme('colors.coolGray.800');
--f-gray-900: theme('colors.coolGray.900');
--f-primary-100: theme('colors.defaultPrimary.100');
--f-primary-200: theme('colors.defaultPrimary.200');
--f-primary-300: theme('colors.defaultPrimary.300');
--f-primary-400: theme('colors.defaultPrimary.400');
--f-primary-500: theme('colors.defaultPrimary.500');
--f-primary-600: theme('colors.defaultPrimary.600');
--f-primary-700: theme('colors.defaultPrimary.700');
--f-primary-800: theme('colors.defaultPrimary.800');
--f-primary-900: theme('colors.defaultPrimary.900');
--f-success-100: theme('colors.defaultSuccess.100');
--f-success-200: theme('colors.defaultSuccess.200');
--f-success-300: theme('colors.defaultSuccess.300');
--f-success-400: theme('colors.defaultSuccess.400');
--f-success-500: theme('colors.defaultSuccess.500');
--f-success-600: theme('colors.defaultSuccess.600');
--f-success-700: theme('colors.defaultSuccess.700');
--f-success-800: theme('colors.defaultSuccess.800');
--f-success-900: theme('colors.defaultSuccess.900');
--f-danger-100: theme('colors.defaultDanger.100');
--f-danger-200: theme('colors.defaultDanger.200');
--f-danger-300: theme('colors.defaultDanger.300');
--f-danger-400: theme('colors.defaultDanger.400');
--f-danger-500: theme('colors.defaultDanger.500');
--f-danger-600: theme('colors.defaultDanger.600');
--f-danger-700: theme('colors.defaultDanger.700');
--f-danger-800: theme('colors.defaultDanger.800');
--f-danger-900: theme('colors.defaultDanger.900');
--f-gray-100: theme('colors.defaultGray.100');
--f-gray-200: theme('colors.defaultGray.200');
--f-gray-300: theme('colors.defaultGray.300');
--f-gray-400: theme('colors.defaultGray.400');
--f-gray-500: theme('colors.defaultGray.500');
--f-gray-600: theme('colors.defaultGray.600');
--f-gray-700: theme('colors.defaultGray.700');
--f-gray-800: theme('colors.defaultGray.800');
--f-gray-900: theme('colors.defaultGray.900');
--f-blue-100: theme('colors.defaultBlue.100');
--f-blue-200: theme('colors.defaultBlue.200');
--f-blue-300: theme('colors.defaultBlue.300');
@@ -10,4 +10,16 @@ return [
],
'table' => [
'recordActions' => [
'edit' => [
'label' => 'Bearbeiten',
],
],
],
];
@@ -10,4 +10,16 @@ return [
],
'table' => [
'recordActions' => [
'edit' => [
'label' => 'Edit',
],
],
],
];
@@ -10,4 +10,16 @@ return [
],
'table' => [
'recordActions' => [
'edit' => [
'label' => 'Editar',
],
],
],
];
@@ -10,4 +10,16 @@ return [
],
'table' => [
'recordActions' => [
'edit' => [
'label' => 'Modifier',
],
],
],
];
+37
View File
@@ -0,0 +1,37 @@
<?php
return [
'buttons' => [
'submit' => [
'label' => 'Masuk',
],
],
'form' => [
'email' => [
'label' => 'Alamat email',
],
'password' => [
'hint' => 'Lupa kata sandi anda?',
'label' => 'Kata sandi',
],
'remember' => [
'label' => 'Ingat saya',
],
],
'messages' => [
'failed' => 'Identitas tersebut tidak cocok dengan data kami.',
'throttled' => 'Terlalu banyak upaya masuk. Silahkan coba lagi dalam :seconds detik.',
],
'title' => 'Masuk ke akun anda',
];
+9
View File
@@ -0,0 +1,9 @@
<?php
return [
'button' => [
'label' => 'Keluar',
],
];
@@ -0,0 +1,36 @@
<?php
return [
'buttons' => [
'submit' => [
'label' => 'Kirim link reset sandi',
],
],
'form' => [
'email' => [
'hint' => 'Kembali ke login',
'label' => 'Alamat email',
],
],
'messages' => [
'throttled' => 'Terlalu banyak upaya masuk. Silahkan coba lagi dalam :seconds detik.',
'passwords' => [
'sent' => 'Kami sudah mengirimkan email yang berisi tautan untuk mereset sandi anda!',
'throttled' => 'Harap tunggu sebelum mencoba lagi.',
'user' => 'Kami tidak menemukan pengguna dengan alamat email tersebut.',
],
],
'title' => 'Reset kata sandi',
];
+41
View File
@@ -0,0 +1,41 @@
<?php
return [
'buttons' => [
'submit' => [
'label' => 'Reset kata sandi',
],
],
'form' => [
'email' => [
'label' => 'Alamat email',
],
'password' => [
'label' => 'Kata sandi',
],
'passwordConfirmation' => [
'label' => 'Konfirmasi kata sandi',
],
],
'messages' => [
'passwords' => [
'throttled' => 'Harap tunggu sebelum mencoba lagi.',
'token' => 'Token reset kata sandi ini tidak valid.',
'user' => 'Kami tidak menemukan pengguna dengan alamat email tersebut.',
],
],
'title' => 'Reset kata sandi',
];
+41
View File
@@ -0,0 +1,41 @@
<?php
return [
'title' => 'Beranda',
'widgets' => [
'account' => [
'heading' => 'Selamat datang, :name',
'links' => [
'account' => [
'label' => 'Atur akun anda',
],
],
],
'filament' => [
'links' => [
'documentation' => [
'label' => 'Baca dokumentasi',
],
'repository' => [
'label' => 'Buka repository',
],
],
],
],
];
+19
View File
@@ -0,0 +1,19 @@
<?php
return [
'buttons' => [
'save' => [
'label' => 'Simpan',
],
],
'messages' => [
'saved' => 'Tersimpan!',
],
'title' => 'Akun',
];
+17
View File
@@ -0,0 +1,17 @@
<?php
return [
'dropdown' => [
'account' => [
'label' => 'Edit akun',
],
'users' => [
'label' => 'Manajemen pengguna',
],
],
];
@@ -0,0 +1,13 @@
<?php
return [
'buttons' => [
'create' => [
'label' => 'Buat',
],
],
];
@@ -0,0 +1,45 @@
<?php
return [
'buttons' => [
'delete' => [
'label' => 'Hapus',
],
'save' => [
'label' => 'Simpan',
],
],
'modals' => [
'delete' => [
'buttons' => [
'cancel' => [
'label' => 'Batalkan',
],
'confirm' => [
'label' => 'Konfirmasi',
],
],
'description' => 'Apakah anda yakin ingin menghapus data ini? Aksi ini tidak bisa dibatalkan.',
'heading' => 'Hapus data ini?',
],
],
'messages' => [
'saved' => 'Tersimpan!',
],
];
@@ -0,0 +1,25 @@
<?php
return [
'buttons' => [
'create' => [
'label' => 'Buat',
],
],
'table' => [
'recordActions' => [
'edit' => [
'label' => 'Edit',
],
],
],
];
@@ -0,0 +1,131 @@
<?php
return [
'buttons' => [
'attach' => [
'label' => 'Sambungkan yang sudah ada',
],
'create' => [
'label' => 'Baru',
],
'detach' => [
'label' => 'Putuskan yang terpilih'
],
],
'modals' => [
'attach' => [
'buttons' => [
'cancel' => [
'label' => 'Batalkan',
],
'attach' => [
'label' => 'Sambungkan',
],
'attachAnother' => [
'label' => 'Sambungkan ini lalu sambungkan yang lain',
],
],
'form' => [
'related' => [
'placeholder' => 'Mulai mengetik untuk mencari...',
],
],
'heading' => 'Sambungkan yang sudah ada',
'messages' => [
'attached' => 'Tersambung!',
],
],
'create' => [
'buttons' => [
'cancel' => [
'label' => 'Batal',
],
'create' => [
'label' => 'Buat',
],
'createAnother' => [
'label' => 'Buat ini lalu buat yang baru',
],
],
'heading' => 'Buat',
'messages' => [
'created' => 'Dibuat!',
],
],
'detach' => [
'buttons' => [
'cancel' => [
'label' => 'Batalkan',
],
'detach' => [
'label' => 'Putus yang dipilih',
],
],
'description' => 'Apakah anda yakin ingin memutuskan data yang dipilih? Aksi ini tidak bisa dibatalkan.',
'heading' => 'Putuskan data yang dipilih? ',
'messages' => [
'detached' => 'Terputuskan!',
],
],
'edit' => [
'buttons' => [
'cancel' => [
'label' => 'Batalkan',
],
'save' => [
'label' => 'Simpan',
],
],
'heading' => 'Edit',
'messages' => [
'saved' => 'Tersimpan!',
],
],
],
];
@@ -0,0 +1,84 @@
<?php
return [
'form' => [
'avatar' => [
'label' => 'Avatar',
],
'email' => [
'label' => 'Alamat email',
],
'isAdmin' => [
'label' => 'Admin Filament?',
'helpMessage' => 'Admin Filament dapat mengakses semua area yang dimiliki filament dan mengatur pengguna lain',
],
'isUser' => [
'label' => 'Pengguna Filament?',
],
'name' => [
'label' => 'Nama',
],
'password' => [
'fieldset' => [
'label' => [
'create' => 'Password',
'edit' => 'Buat password baru',
],
],
'fields' => [
'password' => [
'label' => 'Password',
],
'passwordConfirmation' => [
'label' => 'Konfirmasi password',
],
],
],
'roles' => [
'label' => 'Role',
'placeholder' => 'Pilih role',
],
],
'table' => [
'columns' => [
'email' => [
'label' => 'Alamat email',
],
'name' => [
'label' => 'Nama',
],
],
'filters' => [
'administrators' => [
'label' => 'Administrator',
],
],
],
];
@@ -10,4 +10,16 @@ return [
],
'table' => [
'recordActions' => [
'edit' => [
'label' => 'Modifica',
],
],
],
];
@@ -10,4 +10,16 @@ return [
],
'table' => [
'recordActions' => [
'edit' => [
'label' => 'Bewerk',
],
],
],
];
@@ -10,4 +10,16 @@ return [
],
'table' => [
'recordActions' => [
'edit' => [
'label' => 'Editar',
],
],
],
];
@@ -15,8 +15,8 @@
'path' => 'css/filament.css',
]) }}" />
@foreach (\Filament\Filament::getStyles() as $style)
<link rel="stylesheet" href="{{ $style }}">
@foreach (\Filament\Filament::getStyles() as $path)
<link rel="stylesheet" href="{{ $path }}" />
@endforeach
@stack('filament-styles')
@@ -37,8 +37,8 @@
'path' => 'js/filament.js',
]) }}"></script>
@foreach (\Filament\Filament::getScripts() as $script)
<script src="{{ $script }}"></script>
@foreach (\Filament\Filament::getScripts() as $path)
<script src="{{ $path }}"></script>
@endforeach
@stack('filament-scripts')
@@ -5,8 +5,6 @@
<div class="space-y-6">
@foreach ($relations as $manager)
<x-filament::card>
@livewire(\Livewire\Livewire::getAlias($manager, $manager::getName()), ['owner' => $owner], key($loop->index))
</x-filament::card>
@livewire(\Livewire\Livewire::getAlias($manager, $manager::getName()), ['owner' => $owner], key($loop->index))
@endforeach
</div>
+112 -104
View File
@@ -1,124 +1,132 @@
<div class="space-y-4">
<h3 class="text-lg font-medium leading-tight">
{{ __(static::getTitle()) }}
</h3>
@if (\Filament\Filament::can('viewAny', $this->getModel()))
<x-filament::card class="space-y-4">
<h3 class="text-lg font-medium leading-tight">
{{ __(static::getTitle()) }}
</h3>
<div class="items-center justify-between space-y-4 lg:flex lg:space-y-0 lg:space-x-4">
<div class="space-x-3 flex-shink-0">
<x-filament::button wire:click="openCreate">
{{ __(static::$createButtonLabel) }}
</x-filament::button>
<div class="items-center justify-between space-y-4 lg:flex lg:space-y-0 lg:space-x-4">
<div class="space-x-3 flex-shink-0">
@if ($this->canCreate())
<x-filament::button wire:click="openCreate">
{{ __(static::$createButtonLabel) }}
</x-filament::button>
@endif
@if ($this->canAttach())
<x-filament::button wire:click="openAttach">
{{ __(static::$attachButtonLabel) }}
</x-filament::button>
@endif
@if ($this->canAttach())
<x-filament::button wire:click="openAttach">
{{ __(static::$attachButtonLabel) }}
</x-filament::button>
@endif
@if ($this->canDetach())
<x-filament::button
wire:click="openDetach"
color="danger"
:disabled="count($selected) === 0"
>
{{ __(static::$detachButtonLabel) }}
</x-filament::button>
@endif
@if ($this->canDetachSelected())
<x-filament::button
wire:click="openDetach"
color="danger"
:disabled="count($selected) === 0"
>
{{ __(static::$detachButtonLabel) }}
</x-filament::button>
@endif
<x-tables::delete-selected :selected="$selected" />
@if ($this->canDelete())
<x-tables::delete-selected :disabled="! $this->canDeleteSelected()" />
@endif
</div>
<x-tables::filter :table="$this->getTable()" />
</div>
<x-tables::filter :table="$this->getTable()" />
</div>
<x-tables::table
:records="$records"
:selected="$selected"
:sort-column="$sortColumn"
:sort-direction="$sortDirection"
:table="$this->getTable()"
/>
<x-tables::table
:records="$records"
:selected="$selected"
:sort-column="$sortColumn"
:sort-direction="$sortDirection"
:table="$this->getTable()"
/>
@if ($this->canCreate())
<x-filament::modal
class="w-full max-w-4xl"
:name="static::class . 'RelationManagerCreateModal'"
>
<x-filament::card class="space-y-5">
<x-filament::card-header :title="__(static::$createModalHeading)" />
<x-filament::modal
class="w-full max-w-4xl"
:name="static::class . 'RelationManagerCreateModal'"
>
<x-filament::card class="space-y-5">
<x-filament::card-header :title="__(static::$createModalHeading)" />
@livewire(\Livewire\Livewire::getAlias(Filament\Resources\RelationManager\CreateRecord::class), [
'cancelButtonLabel' => __(static::$createModalCancelButtonLabel),
'createAnotherButtonLabel' => __(static::$createModalCreateAnotherButtonLabel),
'createButtonLabel' => __(static::$createModalCreateButtonLabel),
'createdMessage' => __(static::$createModalCreatedMessage),
'manager' => static::class,
'owner' => $this->owner,
])
</x-filament::card>
</x-filament::modal>
@endif
@livewire(\Livewire\Livewire::getAlias(Filament\Resources\RelationManager\CreateRecord::class), [
'cancelButtonLabel' => __(static::$createModalCancelButtonLabel),
'createAnotherButtonLabel' => __(static::$createModalCreateAnotherButtonLabel),
'createButtonLabel' => __(static::$createModalCreateButtonLabel),
'createdMessage' => __(static::$createModalCreatedMessage),
'manager' => static::class,
'owner' => $this->owner,
])
</x-filament::card>
</x-filament::modal>
<x-filament::modal
class="w-full max-w-4xl"
:name="static::class . 'RelationManagerEditModal'"
>
<x-filament::card class="space-y-5">
<x-filament::card-header :title="__(static::$editModalHeading)" />
@livewire(\Livewire\Livewire::getAlias(Filament\Resources\RelationManager\EditRecord::class), [
'cancelButtonLabel' => __(static::$editModalCancelButtonLabel),
'manager' => static::class,
'owner' => $this->owner,
'saveButtonLabel' => __(static::$editModalSaveButtonLabel),
'savedMessage' => __(static::$editModalSavedMessage),
])
</x-filament::card>
</x-filament::modal>
@if ($this->canAttach())
<x-filament::modal
class="w-full max-w-lg"
:name="static::class . 'RelationManagerAttachModal'"
class="w-full max-w-4xl"
:name="static::class . 'RelationManagerEditModal'"
>
<x-filament::card class="w-full space-y-5">
<x-filament::card-header :title="__(static::$attachModalHeading)" />
<x-filament::card class="space-y-5">
<x-filament::card-header :title="__(static::$editModalHeading)" />
@livewire(\Livewire\Livewire::getAlias(Filament\Resources\RelationManager\AttachRecord::class), [
'cancelButtonLabel' => __(static::$attachModalCancelButtonLabel),
'attachAnotherButtonLabel' => __(static::$attachModalAttachAnotherButtonLabel),
'attachButtonLabel' => __(static::$attachModalAttachButtonLabel),
'attachedMessage' => __(static::$attachModalAttachedMessage),
@livewire(\Livewire\Livewire::getAlias(Filament\Resources\RelationManager\EditRecord::class), [
'cancelButtonLabel' => __(static::$editModalCancelButtonLabel),
'manager' => static::class,
'owner' => $this->owner,
'saveButtonLabel' => __(static::$editModalSaveButtonLabel),
'savedMessage' => __(static::$editModalSavedMessage),
])
</x-filament::card>
</x-filament::modal>
@endif
@if ($this->canDetach())
<x-filament::modal
:name="static::class . 'RelationManagerDetachModal'"
>
<x-filament::card class="space-y-5">
<x-filament::card-header :title="__(static::$detachModalHeading)">
<p class="text-sm text-gray-500">
{{ __(static::$detachModalDescription) }}
</p>
</x-filament::card-header>
@if ($this->canAttach())
<x-filament::modal
class="w-full max-w-lg"
:name="static::class . 'RelationManagerAttachModal'"
>
<x-filament::card class="w-full space-y-5">
<x-filament::card-header :title="__(static::$attachModalHeading)" />
<div class="space-y-3 sm:space-y-0 sm:space-x-3 sm:flex sm:justify-end">
<x-filament::button x-on:click="$dispatch('close', '{{ (string) Str::of(get_class($this))->replace('\\', '\\\\') }}RelationManagerDetachModal')">
{{ __(static::$detachModalCancelButtonLabel) }}
</x-filament::button>
@livewire(\Livewire\Livewire::getAlias(Filament\Resources\RelationManager\AttachRecord::class), [
'cancelButtonLabel' => __(static::$attachModalCancelButtonLabel),
'attachAnotherButtonLabel' => __(static::$attachModalAttachAnotherButtonLabel),
'attachButtonLabel' => __(static::$attachModalAttachButtonLabel),
'attachedMessage' => __(static::$attachModalAttachedMessage),
'manager' => static::class,
'owner' => $this->owner,
])
</x-filament::card>
</x-filament::modal>
@endif
<x-filament::button
type="button"
color="danger"
wire:click="detachSelected"
>
{{ __(static::$detachModalDetachButtonLabel) }}
</x-filament::button>
</div>
</x-filament::card>
</x-filament::modal>
@endif
</div>
@if ($this->canDetachSelected())
<x-filament::modal
:name="static::class . 'RelationManagerDetachModal'"
>
<x-filament::card class="space-y-5">
<x-filament::card-header :title="__(static::$detachModalHeading)">
<p class="text-sm text-gray-500">
{{ __(static::$detachModalDescription) }}
</p>
</x-filament::card-header>
<div class="space-y-3 sm:space-y-0 sm:space-x-3 sm:flex sm:justify-end">
<x-filament::button x-on:click="$dispatch('close', '{{ (string) Str::of(get_class($this))->replace('\\', '\\\\') }}RelationManagerDetachModal')">
{{ __(static::$detachModalCancelButtonLabel) }}
</x-filament::button>
<x-filament::button
type="button"
color="danger"
wire:click="detachSelected"
>
{{ __(static::$detachModalDetachButtonLabel) }}
</x-filament::button>
</div>
</x-filament::card>
</x-filament::modal>
@endif
</x-filament::card>
@endif
@@ -4,39 +4,41 @@
:title="$title"
>
<x-slot name="actions">
<x-filament::modal>
<x-slot name="trigger">
<x-filament::button
x-on:click="open = true"
color="danger"
>
{{ __(static::$deleteButtonLabel) }}
</x-filament::button>
</x-slot>
<x-filament::card class="space-y-5 max-w-2xl">
<x-filament::card-header :title="__(static::$deleteModalHeading)">
<p class="text-sm text-gray-500">
{{ __(static::$deleteModalDescription) }}
</p>
</x-filament::card-header>
<div class="space-y-3 sm:space-y-0 sm:flex sm:space-x-3 sm:justify-end">
@if ($this->canDelete())
<x-filament::modal>
<x-slot name="trigger">
<x-filament::button
x-on:click="open = false"
>
{{ __(static::$deleteModalCancelButtonLabel) }}
</x-filament::button>
<x-filament::button
wire:click="delete"
x-on:click="open = true"
color="danger"
>
{{ __(static::$deleteModalConfirmButtonLabel) }}
{{ __(static::$deleteButtonLabel) }}
</x-filament::button>
</div>
</x-filament::card>
</x-filament::modal>
</x-slot>
<x-filament::card class="space-y-5 max-w-2xl">
<x-filament::card-header :title="__(static::$deleteModalHeading)">
<p class="text-sm text-gray-500">
{{ __(static::$deleteModalDescription) }}
</p>
</x-filament::card-header>
<div class="space-y-3 sm:space-y-0 sm:flex sm:space-x-3 sm:justify-end">
<x-filament::button
x-on:click="open = false"
>
{{ __(static::$deleteModalCancelButtonLabel) }}
</x-filament::button>
<x-filament::button
wire:click="delete"
color="danger"
>
{{ __(static::$deleteModalConfirmButtonLabel) }}
</x-filament::button>
</div>
</x-filament::card>
</x-filament::modal>
@endif
</x-slot>
</x-filament::app-header>
@@ -1,22 +1,38 @@
<div>
<x-filament::app-header :title="$title">
<x-slot name="actions">
<x-filament::button
color="primary"
:href="static::getResource()::generateUrl($createRoute)"
>
{{ __(static::$createButtonLabel) }}
</x-filament::button>
@if ($this->canCreate())
<x-filament::button
color="primary"
:href="static::getResource()::generateUrl($createRoute)"
>
{{ __(static::$createButtonLabel) }}
</x-filament::button>
@endif
</x-slot>
</x-filament::app-header>
<x-filament::app-content>
<x-tables::container
<x-filament::app-content class="space-y-4">
<div class="items-center justify-between space-y-4 sm:flex sm:space-y-0">
<div>
@if ($this->canDelete())
<x-tables::delete-selected :disabled="! $this->canDeleteSelected()" />
@endif
</div>
<x-tables::filter :table="$this->getTable()" />
</div>
<x-tables::table
:records="$records"
:selected="$selected"
:sort-column="$sortColumn"
:sort-direction="$sortDirection"
:table="$this->getTable()"
/>
@if ($this->getTable()->hasPagination())
<x-tables::pagination.paginator :paginator="$records" />
@endif
</x-filament::app-content>
</div>
-75
View File
@@ -1,75 +0,0 @@
<?php
namespace Filament;
class AuthorizationManager
{
public $authorizations = [];
public $mode = 'deny';
public $target;
public function __construct($target)
{
$this->target = $target;
$this->authorize($this->target::authorization());
}
public function authorize($authorizations)
{
if (! $authorizations) return;
if (! is_array($authorizations)) {
$authorizations = [$authorizations];
}
if (! count($this->authorizations)) {
$this->mode = $authorizations[0]->mode;
}
$authorizations = array_filter(
$authorizations,
fn ($authorization) => $authorization->mode === $this->mode,
);
$this->authorizations = array_merge($this->authorizations, $authorizations);
return $this;
}
public function can($action = null)
{
$user = Filament::auth()->user();
if (
! count($this->authorizations) ||
$user->isFilamentAdmin()
) {
return true;
}
if ($this->mode === 'allow') {
foreach ($this->authorizations as $authorization) {
if ($user->hasFilamentRole($authorization->role)) {
return true;
}
}
return false;
}
if ($this->mode === 'deny') {
foreach ($this->authorizations as $authorization) {
if ($user->hasFilamentRole($authorization->role)) {
return false;
}
}
return true;
}
return false;
}
}
+6 -2
View File
@@ -3,6 +3,7 @@
namespace Filament\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class MakeThemeCommand extends Command
{
@@ -14,7 +15,10 @@ class MakeThemeCommand extends Command
public function handle()
{
$path = resource_path("css/filament/{$this->argument('name')}.css");
$theme = (string) Str::of($this->argument('name'))
->trim(' ');
$path = resource_path("css/filament/{$theme}.css");
if ($this->checkForCollision([
$path,
@@ -22,6 +26,6 @@ class MakeThemeCommand extends Command
$this->copyStubToApp('Theme', $path);
$this->info("Successfully created {$this->argument('name')} theme!");
$this->info("Successfully created {$theme} theme!");
}
}
+3
View File
@@ -8,6 +8,8 @@ use Illuminate\Support\Facades\Facade;
/**
* @method static StatefulGuard auth()
* @method static bool can(string $action, object|string $target)
* @method static array getAuthorizations()
* @method static array getPages()
* @method static array getResources()
* @method static array getRoles()
@@ -16,6 +18,7 @@ use Illuminate\Support\Facades\Facade;
* @method static array getStyles()
* @method static array getWidgets()
* @method static void provideToScript(array $variables)
* @method static void registerAuthorizations(string $target, array $authorizations = [])
* @method static void registerPage(string $page)
* @method static void registerResource(string $resource)
* @method static void registerRole(string $role)
+79 -6
View File
@@ -7,9 +7,12 @@ use Filament\Events\ServingFilament;
use Filament\Resources\UserResource;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Gate;
class FilamentManager
{
public $authorizations = [];
public $pages = [];
public $resources = [];
@@ -29,6 +32,64 @@ class FilamentManager
return Auth::guard(config('filament.auth.guard', 'filament'));
}
public function can($action, $target)
{
$user = $this->auth()->user();
if ($user->isFilamentAdmin()) return true;
$targetClass = is_object($target) ? get_class($target) : $target;
if (count($this->authorizations[$targetClass] ?? [])) {
$mode = $this->authorizations[$targetClass][0]->mode;
$fallback = [
'allow' => false,
'deny' => true,
][$mode];
foreach ($this->authorizations[$targetClass] as $authorization) {
if ($mode !== $authorization->mode) continue;
if (! $user->hasFilamentRole($authorization->role)) continue;
if (in_array($action, $authorization->exceptActions)) continue;
if (in_array($action, $authorization->onlyActions)) {
if ($mode === 'allow') return true;
if ($mode === 'deny') {
$fallback = false;
continue;
}
}
if (count($authorization->onlyActions)) continue;
return [
'allow' => true,
'deny' => false,
][$mode];
}
return $fallback;
}
$policy = Gate::getPolicyFor($targetClass);
if ($policy === null || ! method_exists($policy, $action)) {
return true;
}
return Gate::check($action, $target);
}
public function getAuthorizations()
{
return $this->authorizations;
}
public function getPages()
{
return $this->pages;
@@ -72,34 +133,46 @@ class FilamentManager
$this->scriptData = array_merge($this->scriptData, $variables);
}
public function registerAuthorizations($target, $authorizations = [])
{
$this->authorizations[$target] = array_merge(
$this->authorizations[$target] ?? [],
$authorizations,
);
}
public function registerPage($page)
{
$this->pages = array_merge($this->pages, [$page]);
$this->pages[] = $page;
$this->registerAuthorizations($page, $page::authorization());
}
public function registerResource($resource)
{
$this->resources = array_merge($this->resources, [$resource]);
$this->resources[] = $resource;
$this->registerAuthorizations($resource::getModel(), $resource::authorization());
}
public function registerRole($role)
{
$this->roles = array_merge($this->roles, [$role]);
$this->roles[] = $role;
}
public function registerScript($name, $script)
public function registerScript($name, $path)
{
$this->scripts[$name] = $script;
}
public function registerStyle($name, $style)
public function registerStyle($name, $path)
{
$this->styles[$name] = $style;
}
public function registerWidget($widget)
{
$this->widgets = array_merge($this->widgets, [$widget]);
$this->widgets[] = $widget;
}
public function serving($callback)
+10 -15
View File
@@ -2,7 +2,7 @@
namespace Filament\Pages;
use Filament\AuthorizationManager;
use Filament\Filament;
use Filament\NavigationItem;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Str;
@@ -22,21 +22,18 @@ class Page extends Component
public static $view;
public function __invoke(Container $container, \Illuminate\Routing\Route $route)
{
abort_unless($this->isAuthorized(), 403);
return parent::__invoke($container, $route);
}
public static function authorization()
{
return [];
}
public static function authorizationManager()
{
return new AuthorizationManager(static::class);
}
public static function can($action = null)
{
return static::authorizationManager()->can($action);
}
public static function generateUrl($parameters = [], $absolute = true)
{
return route('filament.pages.' . static::route()->name, $parameters, $absolute);
@@ -104,11 +101,9 @@ class Page extends Component
return Route::make(static::getSlug(), static::getSlug());
}
public function __invoke(Container $container, \Illuminate\Routing\Route $route)
public function isAuthorized()
{
abort_unless(static::can(), 403);
return parent::__invoke($container, $route);
return Filament::can('view', static::class);
}
public function notify($message)
+7
View File
@@ -2,9 +2,11 @@
namespace Filament\Resources\Pages;
use Filament\Filament;
use Filament\Forms\HasForm;
use Filament\Resources\Forms\Form;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Gate;
class CreateRecord extends Page
{
@@ -57,6 +59,11 @@ class CreateRecord extends Page
->submitMethod('create');
}
public function isAuthorized()
{
return Filament::can('create', static::getModel());
}
public function mount()
{
$this->record = [];
+13
View File
@@ -2,6 +2,7 @@
namespace Filament\Resources\Pages;
use Filament\Filament;
use Filament\Forms\HasForm;
use Filament\Resources\Forms\Form;
use Illuminate\Support\Str;
@@ -37,8 +38,15 @@ class EditRecord extends Page
];
}
public function canDelete()
{
return Filament::can('delete', $this->record);
}
public function delete()
{
$this->authorize('delete');
$this->callHook('beforeDelete');
$this->record->delete();
@@ -57,6 +65,11 @@ class EditRecord extends Page
->submitMethod('save');
}
public function isAuthorized()
{
return Filament::can('update', $this->record);
}
public function mount($record)
{
$this->record = static::getModel()::findOrFail($record);
+48 -1
View File
@@ -2,7 +2,9 @@
namespace Filament\Resources\Pages;
use Filament\Filament;
use Filament\Resources\Tables\Table;
use Filament\Resources\Tables\RecordActions;
use Filament\Tables\HasTable;
use Illuminate\Support\Str;
@@ -12,6 +14,8 @@ class ListRecords extends Page
public static $createButtonLabel = 'filament::resources/pages/list-records.buttons.create.label';
public static $editRecordActionLabel = 'filament::resources/pages/list-records.table.recordActions.edit.label';
public static $view = 'filament::resources.pages.list-records';
public $filterable = true;
@@ -37,17 +41,60 @@ class ListRecords extends Page
->title();
}
public function canCreate()
{
return Filament::can('create', static::getModel());
}
public function canDelete()
{
return true;
}
public function canDeleteSelected()
{
return static::getModel()::find($this->selected)
->contains(function ($record) {
return Filament::can('delete', $record);
});
}
public function deleteSelected()
{
$this->authorize('delete');
static::getModel()::destroy(
static::getModel()::find($this->selected)
->filter(function ($record) {
return Filament::can('delete', $record);
})
->toArray(),
);
$this->selected = [];
}
public function getTable()
{
return static::getResource()::table(Table::make())
->context(static::class)
->filterable($this->filterable)
->pagination($this->pagination)
->recordUrl(fn ($record) => $this->getResource()::generateUrl($this->recordRoute, ['record' => $record]))
->recordActions([
RecordActions\Link::make('edit')
->label(static::$editRecordActionLabel)
->url(fn ($record) => $this->getResource()::generateUrl($this->recordRoute, ['record' => $record]))
->when(fn ($record) => Filament::can('update', $record)),
])
->searchable($this->searchable)
->sortable($this->sortable);
}
public function isAuthorized()
{
return Filament::can('viewAny', static::getModel());
}
protected function viewData()
{
return [
+10 -5
View File
@@ -3,16 +3,12 @@
namespace Filament\Resources\Pages;
use Filament\Resources\Route;
use Illuminate\Support\Str;
class Page extends \Filament\Pages\Page
{
public static $resource;
public static function authorizationManager()
{
return static::getResource()::authorizationManager();
}
public static function getResource()
{
return static::$resource;
@@ -33,6 +29,15 @@ class Page extends \Filament\Pages\Page
return static::getModel()::query();
}
protected function authorize($action)
{
$method = (string) Str::of($action)->ucfirst()->prepend('can');
if (! method_exists($this, $method)) return true;
abort_unless($this->{$method}() ?? true, 403);
}
protected function callHook($hook)
{
if (! method_exists($this, $hook)) return;
+44 -2
View File
@@ -2,7 +2,9 @@
namespace Filament\Resources;
use Filament\Filament;
use Filament\Resources\Tables\Table;
use Filament\Resources\Tables\RecordActions;
use Filament\Tables\HasTable;
use Illuminate\Database\Eloquent\Relations;
use Illuminate\Support\Str;
@@ -56,6 +58,8 @@ class RelationManager extends Component
public static $editModalSavedMessage = 'filament::resources/relation-manager.modals.edit.messages.saved';
public static $editRecordActionLabel = 'filament::resources/pages/list-records.table.recordActions.edit.label';
public static $relationship;
public $filterable = true;
@@ -102,7 +106,25 @@ class RelationManager extends Component
return true;
}
public function canDetach()
public function canCreate()
{
return Filament::can('create', $this->getModel());
}
public function canDelete()
{
return true;
}
public function canDeleteSelected()
{
return $this->getModel()::find($this->selected)
->contains(function ($record) {
return Filament::can('delete', $record);
});
}
public function canDetachSelected()
{
if (
$this->isType(Relations\HasMany::class) ||
@@ -112,6 +134,21 @@ class RelationManager extends Component
return true;
}
public function deleteSelected()
{
abort_unless($this->canDelete(), 403);
$this->getModel()::destroy(
$this->getModel()::find($this->selected)
->filter(function ($record) {
return Filament::can('delete', $record);
})
->toArray(),
);
$this->selected = [];
}
public function detachSelected()
{
$relationship = $this->owner->{$this->getRelationship()}();
@@ -129,7 +166,12 @@ class RelationManager extends Component
return static::table(Table::make())
->filterable($this->filterable)
->pagination(false)
->recordAction('openEdit')
->recordActions([
RecordActions\Link::make('edit')
->label(static::$editRecordActionLabel)
->action('openEdit')
->when(fn ($record) => Filament::can('update', $record)),
])
->searchable($this->searchable)
->sortable($this->sortable);
}
@@ -2,6 +2,7 @@
namespace Filament\Resources\RelationManager;
use Filament\Filament;
use Filament\Forms\HasForm;
use Filament\Resources\Forms\Form;
use Illuminate\Database\Eloquent\Model;
@@ -60,6 +61,8 @@ class EditRecord extends Component
public function save()
{
abort_unless(Filament::can('update', $this->record), 403);
$this->validateTemporaryUploadedFiles();
$this->storeTemporaryUploadedFiles();
-6
View File
@@ -2,7 +2,6 @@
namespace Filament\Resources;
use Filament\AuthorizationManager;
use Filament\NavigationItem;
use Filament\Resources\Forms\Form;
use Filament\Resources\Tables\Table;
@@ -29,11 +28,6 @@ class Resource
return [];
}
public static function authorizationManager()
{
return new AuthorizationManager(static::class);
}
public static function form(Form $form)
{
return $form;
@@ -0,0 +1,8 @@
<?php
namespace Filament\Resources\Tables\RecordActions;
class Link extends \Filament\Tables\RecordActions\Link
{
//
}
+2 -2
View File
@@ -12,9 +12,9 @@ class Authorization
public $role;
public function __construct($role, $mode = 'deny')
public function __construct($role, $mode)
{
$this->mode = in_array($mode, ['allow', 'deny']) ? $mode : 'deny';
$this->mode = $mode;
$this->role = $role;
}
+3 -2
View File
@@ -4,6 +4,7 @@ namespace Filament\View\Components;
use Filament\Filament;
use Filament\NavigationItem;
use Illuminate\Support\Facades\Gate;
use Illuminate\View\Component;
class Nav extends Component
@@ -22,13 +23,13 @@ class Nav extends Component
);
foreach (Filament::getResources() as $resource) {
if ($resource::authorizationManager()->can()) {
if (Filament::can('viewAny', $resource::getModel())) {
$this->items->push(...$resource::navigationItems());
}
}
foreach (Filament::getPages() as $page) {
if ($page::authorizationManager()->can()) {
if (Filament::can('view', $page)) {
$this->items->push(...$page::navigationItems());
}
}
+6 -6
View File
@@ -77,12 +77,12 @@ module.exports = {
900: 'var(--f-blue-900)',
},
white: 'var(--f-white)',
lightBlue: colors.lightBlue,
emerald: colors.emerald,
rose: colors.rose,
coolGray: colors.coolGray,
defaultWhite: colors.white,
defaultBlue: colors.blue
defaultPrimary: colors.lightBlue,
defaultSuccess: colors.emerald,
defaultDanger: colors.rose,
defaultGray: colors.coolGray,
defaultBlue: colors.blue,
defaultWhite: colors.white
},
keyframes: {
shake: {