mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-24 15:42:09 +08:00
wip
This commit is contained in:
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace Filament\Database\Factories;
|
||||
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class FilamentUserFactory extends Factory
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
protected $model = FilamentUser::class;
|
||||
protected $model = User::class;
|
||||
|
||||
public function definition()
|
||||
{
|
||||
@@ -18,6 +18,7 @@ class FilamentUserFactory extends Factory
|
||||
'name' => $this->faker->name,
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||
'remember_token' => Str::random(10),
|
||||
'roles' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ class CreateFilamentUsersTable extends Migration
|
||||
$table->boolean('is_admin')->default(0);
|
||||
$table->string('name');
|
||||
$table->string('password');
|
||||
$table->json('roles')->nullable();
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateFilamentRoleUserTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('filament_role_user', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('role');
|
||||
$table->foreignId('user_id')->constrained('filament_users')->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('filament_role_user');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
@pushonce('filament-scripts:multi-select-component')
|
||||
<script>
|
||||
function multiSelect(config) {
|
||||
return {
|
||||
autofocus: config.autofocus,
|
||||
|
||||
emptyOptionsMessage: config.emptyOptionsMessage,
|
||||
|
||||
focusedOptionIndex: null,
|
||||
|
||||
initialOptions: config.initialOptions,
|
||||
|
||||
name: config.name,
|
||||
|
||||
noSearchResultsMessage: config.noSearchResultsMessage,
|
||||
|
||||
open: false,
|
||||
|
||||
options: {},
|
||||
|
||||
required: config.required,
|
||||
|
||||
search: '',
|
||||
|
||||
value: config.value,
|
||||
|
||||
closeListbox: function () {
|
||||
this.open = false
|
||||
|
||||
this.focusedOptionIndex = null
|
||||
|
||||
this.search = ''
|
||||
},
|
||||
|
||||
evaluatePosition: function () {
|
||||
let availableHeight = window.innerHeight - this.$refs.button.offsetHeight
|
||||
|
||||
let element = this.$refs.button
|
||||
|
||||
while (element) {
|
||||
availableHeight -= element.offsetTop
|
||||
|
||||
element = element.offsetParent
|
||||
}
|
||||
|
||||
if (this.$refs.listbox.offsetHeight <= availableHeight) {
|
||||
this.$refs.listbox.style.bottom = 'auto'
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.$refs.listbox.style.bottom = `${this.$refs.button.offsetHeight}px`
|
||||
},
|
||||
|
||||
focusNextOption: function () {
|
||||
if (this.focusedOptionIndex === null) {
|
||||
this.focusedOptionIndex = Object.keys(this.options).length - 1
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (this.focusedOptionIndex + 1 >= Object.keys(this.options).length) return
|
||||
|
||||
this.focusedOptionIndex++
|
||||
|
||||
this.$refs.listboxOptionsList.children[this.focusedOptionIndex].scrollIntoView({
|
||||
block: 'center',
|
||||
})
|
||||
},
|
||||
|
||||
focusPreviousOption: function () {
|
||||
if (this.focusedOptionIndex === null) {
|
||||
this.focusedOptionIndex = 0
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (this.focusedOptionIndex <= 0) return
|
||||
|
||||
this.focusedOptionIndex--
|
||||
|
||||
this.$refs.listboxOptionsList.children[this.focusedOptionIndex].scrollIntoView({
|
||||
block: 'center',
|
||||
})
|
||||
},
|
||||
|
||||
init: function () {
|
||||
if (! this.value) {
|
||||
this.value = []
|
||||
}
|
||||
|
||||
this.setOptions()
|
||||
|
||||
if (this.autofocus) this.openListbox()
|
||||
|
||||
this.$watch('search', (() => {
|
||||
if (! this.open || this.search === '' || this.search === null) {
|
||||
this.setOptions()
|
||||
this.focusedOptionIndex = 0
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.options = {}
|
||||
|
||||
for (const [key, label] of Object.entries(this.initialOptions)) {
|
||||
if (
|
||||
! this.value.includes(key) &&
|
||||
label.toLowerCase().includes(this.search.toLowerCase())
|
||||
) {
|
||||
this.options[key] = label
|
||||
}
|
||||
}
|
||||
|
||||
this.focusedOptionIndex = 0
|
||||
}))
|
||||
|
||||
this.$watch('value', (() => {
|
||||
if (this.value) return
|
||||
|
||||
this.value = []
|
||||
}))
|
||||
},
|
||||
|
||||
openListbox: function () {
|
||||
this.setOptions()
|
||||
|
||||
this.focusedOptionIndex = 0
|
||||
|
||||
this.open = true
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.search.focus()
|
||||
|
||||
this.evaluatePosition()
|
||||
|
||||
this.$refs.listboxOptionsList.children[this.focusedOptionIndex].scrollIntoView({
|
||||
block: 'center'
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
selectOption: function (index = null) {
|
||||
if (! this.open) {
|
||||
this.closeListbox()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.value.push(Object.keys(this.options)[index ?? this.focusedOptionIndex])
|
||||
|
||||
this.closeListbox()
|
||||
},
|
||||
|
||||
setOptions: function () {
|
||||
this.options = {}
|
||||
|
||||
for (const [key, label] of Object.entries(this.initialOptions)) {
|
||||
if (! this.value.includes(key)) {
|
||||
this.options[key] = label
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toggleListboxVisibility: function () {
|
||||
if (this.open) {
|
||||
this.closeListbox()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.openListbox()
|
||||
},
|
||||
|
||||
unselectOption: function (keyToUnselect) {
|
||||
this.value = this.value.filter((key) => {
|
||||
return key !== keyToUnselect
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpushonce
|
||||
|
||||
<x-forms::field-group
|
||||
:column-span="$formComponent->columnSpan"
|
||||
:error-key="$formComponent->name"
|
||||
:for="$formComponent->id"
|
||||
:help-message="__($formComponent->helpMessage)"
|
||||
:hint="__($formComponent->hint)"
|
||||
:label="__($formComponent->label)"
|
||||
:required="$formComponent->required"
|
||||
>
|
||||
<div
|
||||
x-data="multiSelect({
|
||||
autofocus: {{ $formComponent->autofocus ? 'true' : 'false' }},
|
||||
emptyOptionsMessage: '{{ __($formComponent->emptyOptionsMessage) }}',
|
||||
initialOptions: {{ json_encode($formComponent->options) }},
|
||||
name: '{{ $formComponent->name }}',
|
||||
noSearchResultsMessage: '{{ __($formComponent->noSearchResultsMessage) }}',
|
||||
required: {{ $formComponent->required ? 'true' : 'false' }},
|
||||
@if (Str::of($formComponent->nameAttribute)->startsWith('wire:model'))
|
||||
value: @entangle($formComponent->name){{ Str::of($formComponent->nameAttribute)->after('wire:model') }},
|
||||
@endif
|
||||
})"
|
||||
x-init="init()"
|
||||
x-on:click.away="closeListbox()"
|
||||
x-on:keydown.escape.stop="closeListbox()"
|
||||
{!! $formComponent->id ? "id=\"{$formComponent->id}\"" : null !!}
|
||||
class="relative"
|
||||
{!! Filament\format_attributes($formComponent->extraAttributes) !!}
|
||||
>
|
||||
@unless (Str::of($formComponent->nameAttribute)->startsWith(['wire:model', 'x-model']))
|
||||
<input
|
||||
x-model="value"
|
||||
{!! $formComponent->name ? "{$formComponent->nameAttribute}=\"{$formComponent->name}\"" : null !!}
|
||||
type="hidden"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@unless($formComponent->disabled)
|
||||
<div
|
||||
x-ref="button"
|
||||
x-on:click="toggleListboxVisibility()"
|
||||
x-on:keydown.enter.stop.prevent="open ? selectOption() : openListbox()"
|
||||
x-on:keydown.space="if (! open) openListbox()"
|
||||
x-bind:aria-expanded="open"
|
||||
aria-haspopup="listbox"
|
||||
tabindex="1"
|
||||
class="bg-white relative w-full border rounded shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:border-secondary-300 focus:ring focus:ring-secondary-200 focus:ring-opacity-50 {{ $formComponent->disabled ? 'text-gray-500' : '' }} {{ $errors->has($formComponent->name) ? 'border-danger-600 motion-safe:animate-shake' : 'border-gray-300' }}"
|
||||
>
|
||||
<span
|
||||
x-show="! open"
|
||||
class="block truncate text-gray-400"
|
||||
>
|
||||
{{ __($formComponent->placeholder) }}
|
||||
</span>
|
||||
|
||||
<input
|
||||
x-ref="search"
|
||||
x-show="open"
|
||||
x-model.debounce.500="search"
|
||||
x-on:keydown.enter.stop.prevent="selectOption()"
|
||||
x-on:keydown.arrow-up.stop.prevent="focusPreviousOption()"
|
||||
x-on:keydown.arrow-down.stop.prevent="focusNextOption()"
|
||||
type="search"
|
||||
class="w-full h-full border-0 p-0 focus:ring-0 focus:outline-none"
|
||||
/>
|
||||
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<x-heroicon-s-selector x-cloak class="w-5 h-5 text-gray-400" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
x-ref="listbox"
|
||||
x-show="open"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
role="listbox"
|
||||
x-bind:aria-activedescendant="focusedOptionIndex ? '{{ $formComponent->name }}' + 'Option' + focusedOptionIndex : null"
|
||||
tabindex="-1"
|
||||
x-cloak
|
||||
class="absolute z-10 w-full my-1 bg-white rounded shadow-sm border border-gray-300"
|
||||
>
|
||||
<ul
|
||||
x-ref="listboxOptionsList"
|
||||
class="py-1 overflow-auto text-base leading-6 rounded shadow-sm max-h-60 focus:outline-none"
|
||||
>
|
||||
<template x-for="(key, index) in Object.keys(options)" :key="index">
|
||||
<li
|
||||
x-bind:id="'{{ $formComponent->name }}' + 'Option' + focusedOptionIndex"
|
||||
x-on:click="selectOption(index)"
|
||||
x-on:mouseenter="focusedOptionIndex = index"
|
||||
x-on:mouseleave="focusedOptionIndex = null"
|
||||
role="option"
|
||||
x-bind:aria-selected="focusedOptionIndex === index"
|
||||
x-bind:class="{
|
||||
'text-white bg-secondary-600': index === focusedOptionIndex,
|
||||
'text-gray-900': index !== focusedOptionIndex,
|
||||
}"
|
||||
class="relative py-2 pl-3 text-gray-900 cursor-default select-none pr-9"
|
||||
>
|
||||
<span
|
||||
x-text="Object.values(options)[index]"
|
||||
x-bind:class="{
|
||||
'font-medium': index === focusedOptionIndex,
|
||||
'font-normal': index !== focusedOptionIndex,
|
||||
}"
|
||||
class="block font-normal truncate"
|
||||
></span>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<div
|
||||
x-show="! Object.keys(options).length"
|
||||
x-text="! search ? emptyOptionsMessage : noSearchResultsMessage"
|
||||
class="px-3 py-2 text-gray-900 cursor-default select-none text-sm"
|
||||
></div>
|
||||
</ul>
|
||||
</div>
|
||||
@endunless
|
||||
|
||||
<div>
|
||||
<template x-for="key in value">
|
||||
<button
|
||||
@unless($formComponent->disabled)
|
||||
x-on:click="unselectOption(key)"
|
||||
@endunless
|
||||
type="button"
|
||||
class="my-1 w-full flex justify-between space-x-2 items-center font-mono text-xs py-2 px-3 border border-gray-300 bg-gray-100 text-gray-800 rounded shadow-sm inline-block relative @unless($formComponent->disabled) cursor-pointer transition duration-200 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 hover:bg-gray-200 transition-colors duration-200 @else cursor-default @endunless"
|
||||
>
|
||||
<span x-text="initialOptions[key] ?? key"></span>
|
||||
|
||||
@unless($formComponent->disabled)
|
||||
<x-heroicon-s-x class="h-3 w-3 text-gray-500" />
|
||||
@endunless
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</x-forms::field-group>
|
||||
@@ -117,7 +117,7 @@
|
||||
if (this.autofocus) this.openListbox()
|
||||
|
||||
this.$watch('search', (() => {
|
||||
if (!this.open || this.search === '' || this.search === null) {
|
||||
if (! this.open || this.search === '' || this.search === null) {
|
||||
this.options = this.initialOptions
|
||||
this.focusedOptionIndex = 0
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@pushonce('filament-scripts:tags-component')
|
||||
@pushonce('filament-scripts:tags-input-component')
|
||||
<script>
|
||||
function tags(config) {
|
||||
function tagsInput(config) {
|
||||
return {
|
||||
newTag: '',
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
:required="$formComponent->required"
|
||||
>
|
||||
<div
|
||||
x-data="tags({
|
||||
x-data="tagsInput({
|
||||
separator: '{{ $formComponent->separator }}',
|
||||
@if (Str::of($formComponent->nameAttribute)->startsWith('wire:model'))
|
||||
value: @entangle($formComponent->name){{ Str::of($formComponent->nameAttribute)->after('wire:model') }},
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Forms\Components;
|
||||
|
||||
class MultiSelect extends Field
|
||||
{
|
||||
use Concerns\CanBeAutofocused;
|
||||
use Concerns\HasPlaceholder;
|
||||
|
||||
public $emptyOptionsMessage = 'forms::fields.select.emptyOptionsMessage';
|
||||
|
||||
public $noSearchResultsMessage = 'forms::fields.select.noSearchResultsMessage';
|
||||
|
||||
public $options = [];
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
parent::__construct($name);
|
||||
|
||||
$this->placeholder('forms::fields.select.placeholder');
|
||||
}
|
||||
|
||||
public function emptyOptionsMessage($message)
|
||||
{
|
||||
$this->emptyOptionsMessage = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noSearchResultsMessage($message)
|
||||
{
|
||||
$this->noSearchResultsMessage = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function options($options)
|
||||
{
|
||||
$this->options = $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ class Select extends Field
|
||||
use Concerns\CanBeAutofocused;
|
||||
use Concerns\CanBeCompared;
|
||||
use Concerns\CanBeUnique;
|
||||
use Concerns\CanBeLengthConstrained;
|
||||
use Concerns\HasPlaceholder;
|
||||
|
||||
public $emptyOptionsMessage = 'forms::fields.select.emptyOptionsMessage';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Filament\Commands;
|
||||
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
@@ -24,7 +24,7 @@ class MakeUserCommand extends Command
|
||||
|
||||
$isAdmin = $this->confirm('Would you like this user to be an administrator?', true);
|
||||
|
||||
$user = FilamentUser::create([
|
||||
$user = User::create([
|
||||
'is_admin' => $isAdmin,
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
@@ -34,7 +34,7 @@ class MakeUserCommand extends Command
|
||||
$loginUrl = route('filament.auth.login');
|
||||
$this->info("Success! {$user->email} may now log in at {$loginUrl}.");
|
||||
|
||||
if (FilamentUser::count() === 1 && $this->confirm('Would you like to show some love by starring the repo?', true)) {
|
||||
if (User::count() === 1 && $this->confirm('Would you like to show some love by starring the repo?', true)) {
|
||||
if (PHP_OS_FAMILY === 'Darwin') exec('open https://github.com/laravel-filament/filament');
|
||||
if (PHP_OS_FAMILY === 'Linux') exec('xdg-open https://github.com/laravel-filament/filament');
|
||||
if (PHP_OS_FAMILY === 'Windows') exec('start https://github.com/laravel-filament/filament');
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Filament;
|
||||
use BladeUI\Icons\Factory as BladeUIFactory;
|
||||
use Filament\Commands;
|
||||
use Filament\Http\Middleware;
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Providers\RouteServiceProvider;
|
||||
use Filament\Resources\Resource;
|
||||
@@ -151,7 +151,7 @@ class FilamentServiceProvider extends ServiceProvider
|
||||
|
||||
$this->app['config']->set('auth.providers.filament_users', [
|
||||
'driver' => 'eloquent',
|
||||
'model' => FilamentUser::class,
|
||||
'model' => User::class,
|
||||
]);
|
||||
|
||||
$this->app['config']->set('forms', [
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Filament\Http\Livewire;
|
||||
use Filament\Forms\Components;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\HasForm;
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Filament\Pages\Page;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
@@ -36,7 +36,7 @@ class EditAccount extends Page
|
||||
->email()
|
||||
->disableAutocomplete()
|
||||
->required()
|
||||
->unique(FilamentUser::class, 'email', true),
|
||||
->unique(User::class, 'email', true),
|
||||
]),
|
||||
Components\Fieldset::make('Set a new password', [
|
||||
Components\TextInput::make('newPassword')
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class FilamentRoleUser extends Model
|
||||
{
|
||||
public $table = 'filament_role_user';
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(FilamentUser::class, 'user_id');
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,22 @@
|
||||
|
||||
namespace Filament\Models;
|
||||
|
||||
use Filament\Database\Factories\FilamentUserFactory;
|
||||
use Filament\Database\Factories\UserFactory;
|
||||
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class FilamentUser extends Authenticatable
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasFactory;
|
||||
use Notifiable;
|
||||
|
||||
protected $casts = [
|
||||
'roles' => 'array',
|
||||
];
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $hidden = [
|
||||
@@ -21,9 +25,11 @@ class FilamentUser extends Authenticatable
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
protected $table = 'filament_users';
|
||||
|
||||
protected static function newFactory()
|
||||
{
|
||||
return FilamentUserFactory::new();
|
||||
return UserFactory::new();
|
||||
}
|
||||
|
||||
public function can($resource, $route = 'index')
|
||||
@@ -33,10 +39,7 @@ class FilamentUser extends Authenticatable
|
||||
|
||||
public function hasRole($role)
|
||||
{
|
||||
return FilamentRoleUser::where([
|
||||
['role', $role],
|
||||
['user_id', $this->attributes['id']],
|
||||
])->exists();
|
||||
return in_array($role, $this->roles);
|
||||
}
|
||||
|
||||
public function sendPasswordResetNotification($token)
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Resources\Forms\Components;
|
||||
|
||||
class MultiSelect extends \Filament\Forms\Components\MultiSelect
|
||||
{
|
||||
use Concerns\InteractsWithResource;
|
||||
}
|
||||
@@ -2,26 +2,24 @@
|
||||
|
||||
namespace Filament\Resources;
|
||||
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Filament;
|
||||
use Filament\Models\User;
|
||||
use Filament\Resources\Forms\Components;
|
||||
use Filament\Resources\Forms\Form;
|
||||
use Filament\Resources\Tables\Columns;
|
||||
use Filament\Resources\Tables\Filter;
|
||||
use Filament\Resources\Tables\Table;
|
||||
use Filament\Resources\UserResource\Pages;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class UserResource extends Resource
|
||||
{
|
||||
public static $icon = 'heroicon-o-user-group';
|
||||
|
||||
public static $label = 'user';
|
||||
|
||||
public static $model = FilamentUser::class;
|
||||
public static $model = User::class;
|
||||
|
||||
public static $routeNamePrefix = 'filament';
|
||||
|
||||
public static $slug = 'users';
|
||||
|
||||
public static function form(Form $form)
|
||||
{
|
||||
return $form
|
||||
@@ -34,7 +32,7 @@ class UserResource extends Resource
|
||||
->email()
|
||||
->disableAutocomplete()
|
||||
->required()
|
||||
->unique(FilamentUser::class, 'email', true),
|
||||
->unique(User::class, 'email', true),
|
||||
]),
|
||||
Components\Fieldset::make('Password', [
|
||||
Components\TextInput::make('password')
|
||||
@@ -69,6 +67,13 @@ class UserResource extends Resource
|
||||
->directory('filament-avatars')
|
||||
->disk(config('filament.default_filesystem_disk')),
|
||||
Components\Checkbox::make('is_admin')->label('Administrator?'),
|
||||
Components\MultiSelect::make('roles')
|
||||
->placeholder('Select a role')
|
||||
->options(
|
||||
collect(Filament::getRoles())
|
||||
->mapWithKeys(fn ($role) => [$role => Str::ucfirst($role::getLabel())])
|
||||
->toArray(),
|
||||
),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ class Role
|
||||
if (static::$label) return static::$label;
|
||||
|
||||
return (string) Str::of(class_basename(static::class))
|
||||
->beforeLast('Role')
|
||||
->kebab()
|
||||
->replace('-', ' ');
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Filament\Tests\Feature\Auth;
|
||||
|
||||
use Filament\Http\Livewire\Auth\Login;
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Filament\Tests\TestCase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
@@ -20,7 +20,7 @@ class LoginTest extends TestCase
|
||||
/** @test */
|
||||
public function can_login()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
Livewire::test(Login::class)
|
||||
->set('email', $user->email)
|
||||
@@ -34,7 +34,7 @@ class LoginTest extends TestCase
|
||||
/** @test */
|
||||
public function shows_an_error_when_bad_login_attempt()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
Livewire::test(Login::class)
|
||||
->set('email', $user->email)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Filament\Tests\Feature\Auth;
|
||||
|
||||
use Filament\Http\Livewire\Auth\Logout;
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Filament\Tests\TestCase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
@@ -12,7 +12,7 @@ class LogoutTest extends TestCase
|
||||
/** @test */
|
||||
public function can_log_out()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->be($user);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Filament\Tests\Feature\Auth;
|
||||
|
||||
use Filament\Http\Livewire\Auth\RequestPassword;
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Filament\Tests\TestCase;
|
||||
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
@@ -24,7 +24,7 @@ class RequestPasswordTest extends TestCase
|
||||
{
|
||||
Notification::fake();
|
||||
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
Livewire::test(RequestPassword::class)
|
||||
->set('email', $user->email)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Filament\Tests\Feature\Auth;
|
||||
|
||||
use Filament\Http\Livewire\Auth\ResetPassword;
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Filament\Tests\TestCase;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
@@ -26,7 +26,7 @@ class ResetPasswordTest extends TestCase
|
||||
protected function generateToken($user = null)
|
||||
{
|
||||
if (! $user) {
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
}
|
||||
|
||||
return Password::broker('filament_users')->createToken($user);
|
||||
@@ -35,7 +35,7 @@ class ResetPasswordTest extends TestCase
|
||||
/** @test */
|
||||
public function can_reset_password()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
$newPassword = Str::random();
|
||||
|
||||
Livewire::test(ResetPassword::class, [
|
||||
@@ -68,7 +68,7 @@ class ResetPasswordTest extends TestCase
|
||||
/** @test */
|
||||
public function shows_an_error_when_invalid_token_supplied()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
$newPassword = Str::random();
|
||||
|
||||
Livewire::test(ResetPassword::class, [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Filament\Tests\Feature;
|
||||
|
||||
use Filament\Http\Livewire\EditAccount;
|
||||
use Filament\Models\FilamentUser;
|
||||
use Filament\Models\User;
|
||||
use Filament\Tests\TestCase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@@ -16,7 +16,7 @@ class EditAccountTest extends TestCase
|
||||
/** @test */
|
||||
public function can_view_account_page()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->be($user);
|
||||
|
||||
@@ -30,9 +30,9 @@ class EditAccountTest extends TestCase
|
||||
{
|
||||
Storage::fake(config('filament.storage_disk'));
|
||||
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
// $newAvatar = UploadedFile::fake()->image('avatar.jpg');
|
||||
$newUserDetails = FilamentUser::factory()->make();
|
||||
$newUserDetails = User::factory()->make();
|
||||
$newPassword = Str::random();
|
||||
|
||||
$this->be($user);
|
||||
@@ -65,7 +65,7 @@ class EditAccountTest extends TestCase
|
||||
// /** @test */
|
||||
// public function can_delete_avatar()
|
||||
// {
|
||||
// $user = FilamentUser::factory()->create();
|
||||
// $user = User::factory()->create();
|
||||
// $newAvatar = UploadedFile::fake()->image('avatar.jpg');
|
||||
//
|
||||
// $this->be($user);
|
||||
@@ -92,7 +92,7 @@ class EditAccountTest extends TestCase
|
||||
// /** @test */
|
||||
// public function new_avatar_is_image()
|
||||
// {
|
||||
// $user = FilamentUser::factory()->create();
|
||||
// $user = User::factory()->create();
|
||||
// $newAvatar = UploadedFile::fake()->create('document.txt');
|
||||
//
|
||||
// $this->be($user);
|
||||
@@ -105,7 +105,7 @@ class EditAccountTest extends TestCase
|
||||
/** @test */
|
||||
public function new_password_contains_minimum_8_characters()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->be($user);
|
||||
|
||||
@@ -118,7 +118,7 @@ class EditAccountTest extends TestCase
|
||||
/** @test */
|
||||
public function new_password_is_confirmed()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->be($user);
|
||||
|
||||
@@ -132,7 +132,7 @@ class EditAccountTest extends TestCase
|
||||
/** @test */
|
||||
public function record_email_is_required()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->be($user);
|
||||
|
||||
@@ -145,7 +145,7 @@ class EditAccountTest extends TestCase
|
||||
/** @test */
|
||||
public function record_email_is_valid_email()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->be($user);
|
||||
|
||||
@@ -158,7 +158,7 @@ class EditAccountTest extends TestCase
|
||||
/** @test */
|
||||
public function record_name_is_required()
|
||||
{
|
||||
$user = FilamentUser::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->be($user);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user