refactor: remove unused UI components and factories for cleanup
- Deleted obsolete components (`Choices`, `Input`, `ListItem`, `Table`) and trait (`HasAttributeHelpers`) from the `App\View\Components` namespace. - Removed `ConnectedAppFactory` as it is no longer utilized in the current workflow.
This commit is contained in:
parent
0de768e924
commit
9b6af8135d
@ -12,7 +12,7 @@ trait PasswordValidationRules
|
|||||||
/**
|
/**
|
||||||
* Get the validation rules used to validate passwords.
|
* Get the validation rules used to validate passwords.
|
||||||
*
|
*
|
||||||
* @return array<int, ValidationRule|array<mixed>|string>
|
* @return array<int, ValidationRule|Password|string>
|
||||||
*/
|
*/
|
||||||
protected function passwordRules(): array
|
protected function passwordRules(): array
|
||||||
{
|
{
|
||||||
|
|||||||
@ -7,13 +7,14 @@
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Contracts\Validation\ValidationRule;
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Validation\Rules\Unique;
|
||||||
|
|
||||||
trait ProfileValidationRules
|
trait ProfileValidationRules
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get the validation rules used to validate user profiles.
|
* Get the validation rules used to validate user profiles.
|
||||||
*
|
*
|
||||||
* @return array<string, array<int, ValidationRule|array<mixed>|string>>
|
* @return array<string, array<int, ValidationRule|Unique|string>>
|
||||||
*/
|
*/
|
||||||
protected function profileRules(?int $userId = null): array
|
protected function profileRules(?int $userId = null): array
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ protected function nameRules(): array
|
|||||||
/**
|
/**
|
||||||
* Get the validation rules used to validate user emails.
|
* Get the validation rules used to validate user emails.
|
||||||
*
|
*
|
||||||
* @return array<int, ValidationRule|array<mixed>|string>
|
* @return array<int, ValidationRule|Unique|string>
|
||||||
*/
|
*/
|
||||||
protected function emailRules(?int $userId = null): array
|
protected function emailRules(?int $userId = null): array
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,75 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Concerns\UI;
|
|
||||||
|
|
||||||
use Illuminate\View\ComponentAttributeBag;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This trait is used to add some helpers methods to components.
|
|
||||||
*
|
|
||||||
* @property ComponentAttributeBag $attributes
|
|
||||||
*/
|
|
||||||
trait HasAttributeHelpers
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Check if the element has the given attribute.
|
|
||||||
*/
|
|
||||||
public function hasAttribute(string $attribute): bool
|
|
||||||
{
|
|
||||||
return $this->attributes->has($attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the error field name.
|
|
||||||
* If the parent class has an error field name (passed via props or defined), it will be returned.
|
|
||||||
* Otherwise, the model name will be returned.
|
|
||||||
*/
|
|
||||||
public function errorFieldName(): ?string
|
|
||||||
{
|
|
||||||
return $this->errorField ?? $this->modelName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function will return the model name from the wire:model attribute.
|
|
||||||
* This is useful as we use this to bind wire:model props passed from high level
|
|
||||||
* components (ex: input) to the internal html components (ex: input tag).
|
|
||||||
*/
|
|
||||||
public function modelName(): ?string
|
|
||||||
{
|
|
||||||
return $this->attributes->whereStartsWith('wire:model')->first();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the input is required.
|
|
||||||
*/
|
|
||||||
public function isRequired(): bool
|
|
||||||
{
|
|
||||||
return $this->attributes->has('required') && true === $this->attributes->get('required');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the input is disabled.
|
|
||||||
*/
|
|
||||||
public function isDisabled(): bool
|
|
||||||
{
|
|
||||||
return $this->attributes->has('disabled') && true === $this->attributes->get('disabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the input is readonly.
|
|
||||||
*/
|
|
||||||
public function isReadonly(): bool
|
|
||||||
{
|
|
||||||
return $this->attributes->has('readonly') && true === $this->attributes->get('readonly');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the input is autofocus.
|
|
||||||
*/
|
|
||||||
public function isAutofocus(): bool
|
|
||||||
{
|
|
||||||
return $this->attributes->has('autofocus') && true === $this->attributes->get('autofocus');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Factories;
|
|
||||||
|
|
||||||
use App\Data\ConnectedApp\ConnectAppRequest;
|
|
||||||
use App\Enums\ConnectionStatusEnum;
|
|
||||||
use App\Models\ConnectionStatus;
|
|
||||||
|
|
||||||
final class ConnectedAppFactory
|
|
||||||
{
|
|
||||||
public function createDataFromDto(ConnectAppRequest $data): array
|
|
||||||
{
|
|
||||||
$result = [
|
|
||||||
'name' => $data->name,
|
|
||||||
'connection_provider_id' => $data->connectionProviderId,
|
|
||||||
'connection_protocol_id' => $data->connectionProtocolId,
|
|
||||||
'slug' => $data->slug,
|
|
||||||
'connection_status_id' => $data->statusId,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Check if the staus is filled, if not select disconnected by default
|
|
||||||
if (null === $data->statusId) {
|
|
||||||
$status = ConnectionStatus::query()
|
|
||||||
->where('name', '=', ConnectionStatusEnum::Disconnected->value)
|
|
||||||
->pluck('id');
|
|
||||||
$result['service_status_id'] = $status[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -70,13 +70,18 @@ public function resendVerificationNotification(): void
|
|||||||
#[Computed]
|
#[Computed]
|
||||||
public function hasUnverifiedEmail(): bool
|
public function hasUnverifiedEmail(): bool
|
||||||
{
|
{
|
||||||
return Auth::user() instanceof MustVerifyEmail && ! Auth::user()->hasVerifiedEmail();
|
/** @var \App\Models\User|MustVerifyEmail|null $user */
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
return $user instanceof MustVerifyEmail && ! $user->hasVerifiedEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Computed]
|
#[Computed]
|
||||||
public function showDeleteUser(): bool
|
public function showDeleteUser(): bool
|
||||||
{
|
{
|
||||||
return ! Auth::user() instanceof MustVerifyEmail
|
/** @var \App\Models\User|MustVerifyEmail|null $user */
|
||||||
|| (Auth::user() instanceof MustVerifyEmail && Auth::user()->hasVerifiedEmail());
|
$user = Auth::user();
|
||||||
|
|
||||||
|
return ! $user instanceof MustVerifyEmail || $user->hasVerifiedEmail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,10 @@
|
|||||||
use Spatie\Activitylog\Models\Concerns\LogsActivity;
|
use Spatie\Activitylog\Models\Concerns\LogsActivity;
|
||||||
use Spatie\Activitylog\Support\LogOptions;
|
use Spatie\Activitylog\Support\LogOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $name
|
||||||
|
*/
|
||||||
#[Fillable([
|
#[Fillable([
|
||||||
'name',
|
'name',
|
||||||
'slug',
|
'slug',
|
||||||
@ -21,16 +25,25 @@ class ConnectedApp extends Model
|
|||||||
{
|
{
|
||||||
use LogsActivity, SoftDeletes;
|
use LogsActivity, SoftDeletes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return HasOne<ConnectionProtocol, $this>
|
||||||
|
*/
|
||||||
public function protocol(): HasOne
|
public function protocol(): HasOne
|
||||||
{
|
{
|
||||||
return $this->hasOne(ConnectionProtocol::class, 'id', 'connection_protocol_id');
|
return $this->hasOne(ConnectionProtocol::class, 'id', 'connection_protocol_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return BelongsTo<ConnectionProvider, $this>
|
||||||
|
*/
|
||||||
public function provider(): BelongsTo
|
public function provider(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(ConnectionProvider::class, 'connection_provider_id');
|
return $this->belongsTo(ConnectionProvider::class, 'connection_provider_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return HasOne<ConnectionStatus, $this>
|
||||||
|
*/
|
||||||
public function status(): HasOne
|
public function status(): HasOne
|
||||||
{
|
{
|
||||||
return $this->hasOne(ConnectionStatus::class, 'id', 'connection_status_id');
|
return $this->hasOne(ConnectionStatus::class, 'id', 'connection_status_id');
|
||||||
|
|||||||
@ -6,6 +6,11 @@
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\{Model, SoftDeletes};
|
use Illuminate\Database\Eloquent\{Model, SoftDeletes};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $name
|
||||||
|
* @property string $slug
|
||||||
|
*/
|
||||||
final class ConnectionProtocol extends Model
|
final class ConnectionProtocol extends Model
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|||||||
@ -1,430 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\View\Components;
|
|
||||||
|
|
||||||
use App\Concerns\UI\HasAttributeHelpers;
|
|
||||||
use Closure;
|
|
||||||
use Exception;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\View\Component;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This allows dropdowns with search, multiple selections, fully inline with server.
|
|
||||||
* This is code from MaryUI Choices component, modified to work with our usecase.
|
|
||||||
* https://github.com/robsontenorio/mary/blob/main/src/View/Components/Choices.php
|
|
||||||
*/
|
|
||||||
final class Choices extends Component
|
|
||||||
{
|
|
||||||
use HasAttributeHelpers;
|
|
||||||
|
|
||||||
public string $uuid;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public ?string $id = null,
|
|
||||||
public ?string $label = null,
|
|
||||||
public ?string $hint = null,
|
|
||||||
public ?string $hintClass = 'fieldset-label',
|
|
||||||
public ?string $icon = null,
|
|
||||||
public ?string $iconRight = null,
|
|
||||||
public ?bool $inline = false,
|
|
||||||
public ?bool $clearable = false,
|
|
||||||
public ?string $prefix = null,
|
|
||||||
public ?string $suffix = null,
|
|
||||||
public ?bool $searchable = false,
|
|
||||||
public ?bool $noProgress = false,
|
|
||||||
public ?bool $single = false,
|
|
||||||
public ?bool $compact = false,
|
|
||||||
public ?string $compactText = 'selected',
|
|
||||||
public ?bool $allowAll = false,
|
|
||||||
public ?string $debounce = '250ms',
|
|
||||||
public ?int $minChars = 0,
|
|
||||||
public ?string $allowAllText = 'Select all',
|
|
||||||
public ?string $removeAllText = 'Remove all',
|
|
||||||
public ?string $searchFunction = 'search',
|
|
||||||
public ?string $optionValue = 'id',
|
|
||||||
public ?string $optionLabel = 'name',
|
|
||||||
public ?string $optionSubLabel = '',
|
|
||||||
public ?bool $valuesAsString = false,
|
|
||||||
public ?bool $escapeValues = false,
|
|
||||||
public ?string $height = 'max-h-64',
|
|
||||||
public Collection|array $options = new Collection(),
|
|
||||||
public ?string $noResultText = 'No results found.',
|
|
||||||
|
|
||||||
// Validations
|
|
||||||
public ?string $errorField = null,
|
|
||||||
public ?string $errorClass = 'text-error',
|
|
||||||
public ?bool $omitError = false,
|
|
||||||
public ?bool $firstErrorOnly = false,
|
|
||||||
|
|
||||||
// Slots
|
|
||||||
public mixed $item = null,
|
|
||||||
public mixed $selection = null,
|
|
||||||
public mixed $prepend = null,
|
|
||||||
public mixed $append = null
|
|
||||||
) {
|
|
||||||
$this->uuid = 'component'.md5(serialize($this)).$id;
|
|
||||||
|
|
||||||
if (($this->allowAll || $this->compact) && ($this->single || $this->searchable)) {
|
|
||||||
throw new Exception('`allow-all` and `compact` does not work combined with `single` or `searchable`.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOptionValue($option): mixed
|
|
||||||
{
|
|
||||||
$value = data_get($option, $this->optionValue);
|
|
||||||
|
|
||||||
if ($this->valuesAsString) {
|
|
||||||
return "'$value'";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->escapeValues) {
|
|
||||||
$value = addslashes($value);
|
|
||||||
|
|
||||||
return "'$value'";
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_numeric($value) && ! str($value)->startsWith('0') ? $value : "'$value'";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(): View|Closure|string
|
|
||||||
{
|
|
||||||
return <<<'HTML'
|
|
||||||
<div x-data="{ focused: false, selection: @entangle($attributes->wire('model')) }">
|
|
||||||
<div
|
|
||||||
@click.outside = "clear()"
|
|
||||||
@keyup.esc = "clear()"
|
|
||||||
|
|
||||||
x-data="{
|
|
||||||
options: {{ json_encode($options) }},
|
|
||||||
isSingle: {{ json_encode($single) }},
|
|
||||||
isSearchable: {{ json_encode($searchable) }},
|
|
||||||
isReadonly: {{ json_encode($isReadonly()) }},
|
|
||||||
isDisabled: {{ json_encode($isDisabled()) }},
|
|
||||||
isRequired: {{ json_encode($isRequired()) }},
|
|
||||||
minChars: {{ $minChars }},
|
|
||||||
|
|
||||||
init() {
|
|
||||||
// Fix weird issue when navigating back
|
|
||||||
document.addEventListener('livewire:navigating', () => {
|
|
||||||
let elements = document.querySelectorAll('.choices-element');
|
|
||||||
elements.forEach(el => el.remove());
|
|
||||||
});
|
|
||||||
},
|
|
||||||
get selectedOptions() {
|
|
||||||
return this.isSingle
|
|
||||||
? this.options.filter(i => i.{{ $optionValue }} == this.selection)
|
|
||||||
: this.selection.map(i => this.options.filter(o => o.{{ $optionValue }} == i)[0])
|
|
||||||
},
|
|
||||||
get noResults() {
|
|
||||||
if (!this.isSearchable || this.$refs.searchInput.value == '') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.isSingle
|
|
||||||
? (this.selection && this.options.length == 1) || (!this.selection && this.options.length == 0)
|
|
||||||
: this.options.length <= this.selection.length
|
|
||||||
},
|
|
||||||
get isAllSelected() {
|
|
||||||
return this.options.length == this.selection.length
|
|
||||||
},
|
|
||||||
get isSelectionEmpty() {
|
|
||||||
return this.isSingle
|
|
||||||
? this.selection == null || this.selection == ''
|
|
||||||
: this.selection.length == 0
|
|
||||||
},
|
|
||||||
selectAll() {
|
|
||||||
this.selection = this.options.map(i => i.{{ $optionValue }})
|
|
||||||
this.dispatchChangeEvent({ value: this.selection })
|
|
||||||
},
|
|
||||||
clear() {
|
|
||||||
this.focused = false;
|
|
||||||
this.$refs.searchInput.value = ''
|
|
||||||
},
|
|
||||||
reset() {
|
|
||||||
this.clear();
|
|
||||||
this.isSingle
|
|
||||||
? this.selection = null
|
|
||||||
: this.selection = []
|
|
||||||
|
|
||||||
this.dispatchChangeEvent({ value: this.selection})
|
|
||||||
},
|
|
||||||
focus() {
|
|
||||||
if (this.isReadonly || this.isDisabled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.focused = true
|
|
||||||
this.$refs.searchInput.focus()
|
|
||||||
},
|
|
||||||
resize() {
|
|
||||||
$nextTick(() => $refs.searchInput.style.width = ($refs.searchInput.value.length + 1) * 0.55 + 'rem')
|
|
||||||
},
|
|
||||||
isActive(id) {
|
|
||||||
return this.isSingle
|
|
||||||
? this.selection == id
|
|
||||||
: this.selection.includes(id)
|
|
||||||
},
|
|
||||||
toggle(id, keepOpen = false) {
|
|
||||||
if (this.isReadonly || this.isDisabled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isSingle) {
|
|
||||||
this.selection = id
|
|
||||||
this.focused = false
|
|
||||||
} else {
|
|
||||||
this.selection.includes(id)
|
|
||||||
? this.selection = this.selection.filter(i => i != id)
|
|
||||||
: this.selection.push(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dispatchChangeEvent({ value: this.selection })
|
|
||||||
|
|
||||||
this.$refs.searchInput.value = ''
|
|
||||||
|
|
||||||
if (!keepOpen) {
|
|
||||||
this.$refs.searchInput.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
search(value, event) {
|
|
||||||
if (value.length < this.minChars) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent search for this keys
|
|
||||||
if (event && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Shift', 'CapsLock', 'Tab',
|
|
||||||
'Control', 'Alt', 'Home', 'End', 'PageUp', 'PageDown'].includes(event.key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call search function from parent component
|
|
||||||
// `search(value)` or `search(value, extra1, extra2 ...)`
|
|
||||||
@this.{{ str_contains($searchFunction, '(')
|
|
||||||
? preg_replace('/\((.*?)\)/', '(value, $1)', $searchFunction)
|
|
||||||
: $searchFunction . '(value)'
|
|
||||||
}}.then(()=> this.resize())
|
|
||||||
},
|
|
||||||
dispatchChangeEvent(detail) {
|
|
||||||
this.$refs.searchInput.dispatchEvent(new CustomEvent('change-selection', { bubbles: true, detail }))
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
|
|
||||||
@keydown.up="$focus.previous()"
|
|
||||||
@keydown.down="$focus.next()"
|
|
||||||
>
|
|
||||||
<fieldset class="flex flex-col py-0">
|
|
||||||
{{-- STANDARD LABEL --}}
|
|
||||||
@if($label && !$inline)
|
|
||||||
<legend class="text-sm font-medium text-gray-900 mb-2 ml-1">
|
|
||||||
{{ $label }}
|
|
||||||
|
|
||||||
@if($attributes->get('required'))
|
|
||||||
<span class="text-red-400">*</span>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
</legend>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<label @class(["floating-label" => $label && $inline])>
|
|
||||||
{{-- FLOATING LABEL--}}
|
|
||||||
@if ($label && $inline)
|
|
||||||
<span class="font-semibold">{{ $label }}</span>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div @class(["w-full", "join" => $prepend || $append])>
|
|
||||||
{{-- PREPEND --}}
|
|
||||||
@if($prepend)
|
|
||||||
{{ $prepend }}
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{-- THE LABEL THAT HOLDS THE INPUT --}}
|
|
||||||
<label
|
|
||||||
x-ref="container"
|
|
||||||
|
|
||||||
@if($isDisabled())
|
|
||||||
disabled
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if(!$isDisabled() && !$isReadonly())
|
|
||||||
@click="focus()"
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{
|
|
||||||
$attributes->whereStartsWith('class')->class([
|
|
||||||
"flex items-center border border-gray-300 px-4 py-2 rounded-xl w-full min-h-[var(--size)] h-auto ps-2.5",
|
|
||||||
"join-item" => $prepend || $append,
|
|
||||||
"border-dashed" => $attributes->has("readonly") && $attributes->get("readonly") == true,
|
|
||||||
"!select-error" => $errorFieldName() && $errors->has($errorFieldName()) && !$omitError
|
|
||||||
])
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{{-- PREFIX --}}
|
|
||||||
@if($prefix)
|
|
||||||
<span class="label">{{ $prefix }}</span>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div class="w-full py-0.5 min-h-3 content-center text-wrap">
|
|
||||||
|
|
||||||
{{-- SELECTED OPTIONS --}}
|
|
||||||
<span wire:key="selected-options-{{ $uuid }}">
|
|
||||||
@if($compact)
|
|
||||||
<div class="badge badge-soft">
|
|
||||||
<span class="font-black" x-text="selectedOptions.length"></span> {{ $compactText }}
|
|
||||||
</div>
|
|
||||||
@else
|
|
||||||
<template x-for="(option, index) in selectedOptions" :key="index">
|
|
||||||
<span class="choices-element cursor-pointer bg-gray-100 rounded-full px-4 py-0.5">
|
|
||||||
{{-- SELECTION SLOT --}}
|
|
||||||
@if($selection)
|
|
||||||
<span x-html="document.getElementById('selection-{{ $uuid . '-\' + option.'. $optionValue }}).innerHTML"></span>
|
|
||||||
@else
|
|
||||||
<span x-text="option?.{{ $optionLabel }}"></span>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if(!$isDisabled() && !$isReadonly())
|
|
||||||
<lucide-x @click="toggle(option.{{ $optionValue }})" x-show="!isReadonly && !isDisabled && !isSingle" class="w-4 h-4 hover:text-error" />
|
|
||||||
@endif
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
@endif
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{{-- PLACEHOLDER --}}
|
|
||||||
<span :class="(focused || !isSelectionEmpty) && 'hidden'" class="text-gray-400">
|
|
||||||
{{ $attributes->get('placeholder') }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{{-- INPUT SEARCH --}}
|
|
||||||
<input
|
|
||||||
x-ref="searchInput"
|
|
||||||
@input="focus(); resize();"
|
|
||||||
@keydown.arrow-down.prevent="focus()"
|
|
||||||
:required="isRequired && isSelectionEmpty"
|
|
||||||
class="w-1 !inline-block outline-hidden"
|
|
||||||
|
|
||||||
{{ $attributes->whereStartsWith('@') }}
|
|
||||||
|
|
||||||
@if($isReadonly() || $isDisabled() || ! $searchable)
|
|
||||||
readonly
|
|
||||||
@else
|
|
||||||
@focus="focus()"
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if($isDisabled())
|
|
||||||
disabled
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if($searchable)
|
|
||||||
@keydown.debounce.{{ $debounce }}="search($el.value, $event)"
|
|
||||||
@endif
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- CLEAR ICON --}}
|
|
||||||
@if($clearable && !$isReadonly() && !$isDisabled())
|
|
||||||
<x-lucide-x @click="reset()" x-show="!isSelectionEmpty" class="cursor-pointer w-4 h-4 opacity-40"/>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{-- ICON RIGHT --}}
|
|
||||||
@if($iconRight)
|
|
||||||
<span class="pointer-events-none w-4 h-4 opacity-40">
|
|
||||||
@svg("lucide-{$iconRight}", 'w-4 h-4')
|
|
||||||
</span>
|
|
||||||
@else
|
|
||||||
<x-lucide-chevron-down class="w-4 h-4 opacity-40" />
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{-- SUFFIX --}}
|
|
||||||
@if($suffix)
|
|
||||||
<span class="label">{{ $suffix }}</span>
|
|
||||||
@endif
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{{-- APPEND --}}
|
|
||||||
@if($append)
|
|
||||||
{{ $append }}
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{{-- ERROR --}}
|
|
||||||
@if(!$omitError)
|
|
||||||
<x-shared.error :errorFieldName="$errorFieldName()" />
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{-- HINT --}}
|
|
||||||
@if($hint)
|
|
||||||
<div class="{{ $hintClass }}" x-classes="fieldset-label">{{ $hint }}</div>
|
|
||||||
@endif
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
{{-- OPTIONS LIST --}}
|
|
||||||
<div x-cloak x-show="focused" class="relative" wire:key="options-list-main-{{ $uuid }}">
|
|
||||||
<div
|
|
||||||
wire:key="options-list-{{ $uuid }}"
|
|
||||||
class="{{ $height }} w-full absolute z-10 shadow-xl bg-gray-50 border border-gray-300 rounded-lg cursor-pointer overflow-y-auto"
|
|
||||||
x-anchor.bottom-start="$refs.container"
|
|
||||||
>
|
|
||||||
|
|
||||||
{{-- PROGRESS --}}
|
|
||||||
@if(!$noProgress)
|
|
||||||
<progress wire:loading wire:target="{{ preg_replace('/\((.*?)\)/', '', $searchFunction) }}" class="progress absolute top-0 h-0.5"></progress>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{-- SELECT ALL --}}
|
|
||||||
@if($allowAll)
|
|
||||||
<div
|
|
||||||
wire:key="allow-all-{{ rand() }}"
|
|
||||||
class="font-bold border border-base-200 hover:bg-gray-200"
|
|
||||||
>
|
|
||||||
<div x-show="!isAllSelected" @click="selectAll()" class="p-3 underline decoration-wavy decoration-info">{{ $allowAllText }}</div>
|
|
||||||
<div x-show="isAllSelected" @click="reset()" class="p-3 underline decoration-wavy decoration-error">{{ $removeAllText }}</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{-- NO RESULTS --}}
|
|
||||||
<div
|
|
||||||
x-show="noResults"
|
|
||||||
wire:key="no-results-{{ rand() }}"
|
|
||||||
class="p-3 decoration-wavy decoration-warning underline font-bold border border-s-4 border-s-warning border-b-base-200"
|
|
||||||
>
|
|
||||||
{{ $noResultText }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@forelse($options as $option)
|
|
||||||
<div
|
|
||||||
wire:key="option-{{ data_get($option, $optionValue) }}"
|
|
||||||
@click="toggle({{ $getOptionValue($option) }}, true)"
|
|
||||||
@keydown.enter="toggle({{ $getOptionValue($option) }}, true)"
|
|
||||||
:class="isActive({{ $getOptionValue($option) }}) && ''"
|
|
||||||
class="border border-gray-200 focus:bg-base-200 focus:outline-none"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
{{-- ITEM SLOT --}}
|
|
||||||
@if($item)
|
|
||||||
{{ $item($option) }}
|
|
||||||
@else
|
|
||||||
<x-list-item :item="$option" :value="$optionLabel" :sub-value="$optionSubLabel" />
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{-- SELECTION SLOT --}}
|
|
||||||
@if($selection)
|
|
||||||
<span id="selection-{{ $uuid }}-{{ data_get($option, $optionValue) }}" class="hidden">
|
|
||||||
{{ $selection($option) }}
|
|
||||||
</span>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
@empty
|
|
||||||
<div class="p-3 text-center text-sm">No results found.</div>
|
|
||||||
@endforelse
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
HTML;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\View\Components;
|
|
||||||
|
|
||||||
use App\Concerns\UI\HasAttributeHelpers;
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\View\Component;
|
|
||||||
|
|
||||||
final class Input extends Component
|
|
||||||
{
|
|
||||||
use HasAttributeHelpers;
|
|
||||||
|
|
||||||
public string $uuid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new component instance.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
public ?string $name = null,
|
|
||||||
public ?string $label = null,
|
|
||||||
public ?string $id = null,
|
|
||||||
) {
|
|
||||||
$this->uuid = 'component'.md5(serialize($this)).$id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the view / contents that represent the component.
|
|
||||||
*/
|
|
||||||
public function render(): View|Closure|string
|
|
||||||
{
|
|
||||||
return <<<'BLADE'
|
|
||||||
<div>
|
|
||||||
<fieldset class="flex flex-col">
|
|
||||||
@if($label)
|
|
||||||
<legend class="text-sm font-medium text-gray-900 mb-2 ml-1">
|
|
||||||
{{$label}}
|
|
||||||
@if($hasAttribute('required'))
|
|
||||||
<span class="text-red-400">*</span>
|
|
||||||
@endif
|
|
||||||
</legend>
|
|
||||||
@endif
|
|
||||||
<input
|
|
||||||
id="{{$uuid}}"
|
|
||||||
"
|
|
||||||
placeholder="{{$attributes->get('placeholder') ?? ''}}"
|
|
||||||
@if($isAutofocus()) autofocus @endif
|
|
||||||
{{
|
|
||||||
$attributes->merge([
|
|
||||||
'type' => 'text',
|
|
||||||
'class' => 'rounded-xl border border-gray-300 px-4 py-2'
|
|
||||||
])
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<x-shared.error :errorFieldName="$errorFieldName()" />
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
BLADE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\View\Components;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\View\Component;
|
|
||||||
|
|
||||||
class ListItem extends Component
|
|
||||||
{
|
|
||||||
public string $uuid;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public object|array $item,
|
|
||||||
public ?string $id = null,
|
|
||||||
public string $value = 'name',
|
|
||||||
public ?string $subValue = '',
|
|
||||||
public ?bool $noSeparator = false,
|
|
||||||
public ?bool $noHover = false,
|
|
||||||
public ?string $link = null,
|
|
||||||
public ?string $fallbackAvatar = null,
|
|
||||||
|
|
||||||
// Slots
|
|
||||||
public mixed $actions = null,
|
|
||||||
) {
|
|
||||||
$this->uuid = 'component'.md5(serialize($this)).$id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(): View|Closure|string
|
|
||||||
{
|
|
||||||
return <<<'HTML'
|
|
||||||
<div wire:key="{{ $uuid }}">
|
|
||||||
<div
|
|
||||||
{{ $attributes->class([
|
|
||||||
"flex justify-start items-center gap-4 px-3 py-3",
|
|
||||||
"hover:bg-gray-200" => !$noHover,
|
|
||||||
"cursor-pointer" => $link
|
|
||||||
])
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
|
|
||||||
<!-- CONTENT -->
|
|
||||||
<div class="flex-1 overflow-hidden whitespace-nowrap text-ellipsis truncate mary-hideable">
|
|
||||||
@if($link)
|
|
||||||
<a href="{{ $link }}" wire:navigate>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div @if(!is_string($value)) {{ $value->attributes->class(["font-semibold truncate"]) }} @else class="font-semibold truncate" @endif>
|
|
||||||
{{ is_string($value) ? data_get($item, $value) : $value }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div @if(!is_string($subValue)) {{ $subValue->attributes->class(["text-base-content/50 text-sm truncate"]) }} @else class="text-base-content/50 text-sm truncate" @endif>
|
|
||||||
{{ is_string($subValue) ? data_get($item, $subValue) : $subValue }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if($link)
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ACTION -->
|
|
||||||
@if($actions)
|
|
||||||
@if($link && !Str::of($actions)->contains([':click', '@click' , 'href']))
|
|
||||||
<a href="{{ $link }}" wire:navigate>
|
|
||||||
@endif
|
|
||||||
<div {{ $actions->attributes->class(["flex items-center gap-3 mary-hideable"]) }}>
|
|
||||||
{{ $actions }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if($link && !Str::of($actions)->contains([':click', '@click' , 'href']))
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
HTML;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,267 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\View\Components;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Support\{Arr, Carbon, Str};
|
|
||||||
use Illuminate\View\Component;
|
|
||||||
|
|
||||||
final class Table extends Component
|
|
||||||
{
|
|
||||||
public string $uuid;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public array $headers,
|
|
||||||
public mixed $rows, // Supports Arrays, standard Collections, and Spatie PaginatedDataCollections
|
|
||||||
public ?string $id = null,
|
|
||||||
public bool $striped = false,
|
|
||||||
public bool $noHeaders = false,
|
|
||||||
public ?string $link = null,
|
|
||||||
public bool $withPagination = false,
|
|
||||||
public array $rowDecoration = [],
|
|
||||||
public array $cellDecoration = [],
|
|
||||||
public bool $showEmptyText = true,
|
|
||||||
public string $emptyText = 'No records found.',
|
|
||||||
public string $containerClass = 'relative overflow-x-auto rounded-2xl -pb-2',
|
|
||||||
public bool $noHover = false,
|
|
||||||
|
|
||||||
// Slots
|
|
||||||
public mixed $actions = null,
|
|
||||||
public mixed $cell = null,
|
|
||||||
public mixed $empty = null,
|
|
||||||
public mixed $footer = null,
|
|
||||||
) {
|
|
||||||
// Temp save closures to prevent Livewire/PHP 8.5 serialization crashes
|
|
||||||
$rowDecoration = $this->rowDecoration;
|
|
||||||
$cellDecoration = $this->cellDecoration;
|
|
||||||
$headers = $this->headers;
|
|
||||||
|
|
||||||
// Remove closures from serialization
|
|
||||||
unset($this->rowDecoration, $this->cellDecoration, $this->headers);
|
|
||||||
|
|
||||||
// Serialize a unique ID for component tracking
|
|
||||||
$this->uuid = 'table-'.md5(serialize($this)).$id;
|
|
||||||
|
|
||||||
// Restore closures
|
|
||||||
$this->rowDecoration = $rowDecoration;
|
|
||||||
$this->cellDecoration = $cellDecoration;
|
|
||||||
$this->headers = $headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if header is hidden
|
|
||||||
public function isHidden(mixed $header): bool
|
|
||||||
{
|
|
||||||
return $header['hidden'] ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format header contents
|
|
||||||
public function format(mixed $row, mixed $field, mixed $header): mixed
|
|
||||||
{
|
|
||||||
$format = $header['format'] ?? null;
|
|
||||||
|
|
||||||
if (! $format) {
|
|
||||||
return $field;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_callable($format)) {
|
|
||||||
return $format($row, $field);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('currency' === $format[0]) {
|
|
||||||
return ($format[2] ?? '').number_format((float) $field, ...mb_str_split($format[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('date' === $format[0] && $field) {
|
|
||||||
return Carbon::parse($field)->translatedFormat($format[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $field;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if link should be shown in cell
|
|
||||||
public function hasLink(mixed $header): bool
|
|
||||||
{
|
|
||||||
return $this->link && empty($header['disableLink']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build row link
|
|
||||||
public function redirectLink(mixed $row): string
|
|
||||||
{
|
|
||||||
$link = $this->link;
|
|
||||||
|
|
||||||
// Transform from `route()` pattern
|
|
||||||
$link = Str::of($link)->replace('%5B', '{')->replace('%5D', '}');
|
|
||||||
|
|
||||||
// Extract tokens like {id}, {city.name} ...
|
|
||||||
$tokens = Str::of($link)->matchAll('/\{(.*?)\}/');
|
|
||||||
|
|
||||||
// Replace tokens with actual row values
|
|
||||||
$tokens->each(function (string $token) use ($row, &$link): void {
|
|
||||||
$link = Str::of($link)->replace('{'.$token.'}', data_get($row, $token))->toString();
|
|
||||||
});
|
|
||||||
|
|
||||||
return $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rowClasses(mixed $row): ?string
|
|
||||||
{
|
|
||||||
$classes = [];
|
|
||||||
|
|
||||||
foreach ($this->rowDecoration as $class => $condition) {
|
|
||||||
if ($condition($row)) {
|
|
||||||
$classes[] = $class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Arr::join($classes, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function cellClasses(mixed $row, array $header): ?string
|
|
||||||
{
|
|
||||||
$classes = Str::of($header['class'] ?? '')->explode(' ')->all();
|
|
||||||
|
|
||||||
foreach ($this->cellDecoration[$header['key']] ?? [] as $class => $condition) {
|
|
||||||
if ($condition($row)) {
|
|
||||||
$classes[] = $class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Arr::join($classes, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(): View|Closure|string
|
|
||||||
{
|
|
||||||
return <<<'HTML'
|
|
||||||
<div class="w-full">
|
|
||||||
<div class="{{ $containerClass }}">
|
|
||||||
<table
|
|
||||||
{{
|
|
||||||
$attributes
|
|
||||||
->whereDoesntStartWith('wire:model')
|
|
||||||
->class([
|
|
||||||
'w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400',
|
|
||||||
])
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<thead @class([
|
|
||||||
"text-xs text-gray-700 uppercase bg-gray-50 border-b border-gray-200",
|
|
||||||
"hidden" => $noHeaders
|
|
||||||
])>
|
|
||||||
<tr>
|
|
||||||
@foreach($headers as $header)
|
|
||||||
@php
|
|
||||||
if($isHidden($header)) continue;
|
|
||||||
|
|
||||||
# Scoped slot's name like `user.city` are compiled to `user___city` through `@scope / @endscope`.
|
|
||||||
# So we use current `$header` key to find that slot on context.
|
|
||||||
$temp_key = str_replace('.', '___', $header['key'])
|
|
||||||
@endphp
|
|
||||||
|
|
||||||
<th scope="col" class="px-6 py-4 {{ $header['class'] ?? '' }}">
|
|
||||||
{{ isset(${"header_".$temp_key}) ? ${"header_".$temp_key}($header) : $header['label'] }}
|
|
||||||
</th>
|
|
||||||
@endforeach
|
|
||||||
|
|
||||||
@if($actions)
|
|
||||||
<th scope="col" class="px-6 py-3 w-1 whitespace-nowrap">
|
|
||||||
<span>Actions</span>
|
|
||||||
</th>
|
|
||||||
@endif
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
@isset($rows)
|
|
||||||
<tbody>
|
|
||||||
@foreach($rows as $k => $row)
|
|
||||||
<tr
|
|
||||||
wire:key="{{ $uuid }}-{{ $k }}"
|
|
||||||
@class([
|
|
||||||
'bg-white',
|
|
||||||
'border-b' => $k < $rows->count() - 1,
|
|
||||||
$rowClasses($row),
|
|
||||||
'even:bg-gray-50 ' => $striped,
|
|
||||||
'hover:bg-gray-50' => !$noHover,
|
|
||||||
'cursor-pointer' => $attributes->has('@row-click') || $link
|
|
||||||
])
|
|
||||||
@if($attributes->has('@row-click'))
|
|
||||||
@click="$dispatch('row-click', {{ json_encode($row) }});"
|
|
||||||
@endif
|
|
||||||
>
|
|
||||||
@foreach($headers as $header)
|
|
||||||
@php
|
|
||||||
if($isHidden($header)) continue;
|
|
||||||
$temp_key = str_replace('.', '___', $header['key'])
|
|
||||||
@endphp
|
|
||||||
|
|
||||||
@if(isset(${"cell_".$temp_key}))
|
|
||||||
<td @class(["px-6 py-4", $cellClasses($row, $header), "p-0" => $hasLink($header)])>
|
|
||||||
@if($hasLink($header))
|
|
||||||
<a href="{{ $redirectLink($row) }}" wire:navigate class="block px-6 py-4">
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{ ${"cell_".$temp_key}($row) }}
|
|
||||||
|
|
||||||
@if($hasLink($header))
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
@else
|
|
||||||
<td @class(["px-6 py-4", $cellClasses($row, $header), "p-0" => $hasLink($header)])>
|
|
||||||
@if($hasLink($header))
|
|
||||||
<a href="{{ $redirectLink($row) }}" wire:navigate class="block px-6 py-4 text-inherit">
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{ $format($row, data_get($row, $header['key']), $header) }}
|
|
||||||
|
|
||||||
@if($hasLink($header))
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
@endif
|
|
||||||
@endforeach
|
|
||||||
|
|
||||||
@if($actions)
|
|
||||||
<td class="px-6 py-4 text-right whitespace-nowrap">
|
|
||||||
{{ $actions($row) }}
|
|
||||||
</td>
|
|
||||||
@endif
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</tbody>
|
|
||||||
@endisset
|
|
||||||
@isset ($footer)
|
|
||||||
<tfoot @class([
|
|
||||||
"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400",
|
|
||||||
$footer->attributes->get('class') ?? ''
|
|
||||||
])>
|
|
||||||
{{ $footer }}
|
|
||||||
</tfoot>
|
|
||||||
@endisset
|
|
||||||
</table>
|
|
||||||
|
|
||||||
@if(!$rows || count($rows) === 0)
|
|
||||||
@if($showEmptyText)
|
|
||||||
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
|
|
||||||
{{ $emptyText }}
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
@if($empty)
|
|
||||||
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
|
|
||||||
{{ $empty }}
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if($withPagination && method_exists($rows, 'links'))
|
|
||||||
<div class="mt-4 px-2">
|
|
||||||
{{ $rows->links() }}
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
HTML;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -19,5 +19,5 @@ parameters:
|
|||||||
# ignoreErrors:
|
# ignoreErrors:
|
||||||
# - '#PHPDoc tag @var#'
|
# - '#PHPDoc tag @var#'
|
||||||
#
|
#
|
||||||
# excludePaths:
|
excludePaths:
|
||||||
# - ./*/*/FileToBeExcluded.php
|
- resources/views/flux/*
|
||||||
|
|||||||
@ -43,9 +43,6 @@
|
|||||||
"ordered_interfaces": true,
|
"ordered_interfaces": true,
|
||||||
"ordered_traits": true,
|
"ordered_traits": true,
|
||||||
"php_unit_strict": true,
|
"php_unit_strict": true,
|
||||||
"phpdoc_align": {
|
|
||||||
"align": "vertical"
|
|
||||||
},
|
|
||||||
"phpdoc_separation": true,
|
"phpdoc_separation": true,
|
||||||
"return_type_declaration": {
|
"return_type_declaration": {
|
||||||
"space_before": "none"
|
"space_before": "none"
|
||||||
|
|||||||
@ -16,8 +16,11 @@ public function mount(): void
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**\
|
||||||
|
* @return PaginatedDataCollection<int, ConnectedAppData>
|
||||||
|
*/
|
||||||
#[Computed]
|
#[Computed]
|
||||||
private function getConnectedApps()
|
public function getConnectedApps(): PaginatedDataCollection
|
||||||
{
|
{
|
||||||
$service = app(ConnectedAppService::class);
|
$service = app(ConnectedAppService::class);
|
||||||
return $service->getAll();
|
return $service->getAll();
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public function mount(): void
|
|||||||
</x-shared.button>
|
</x-shared.button>
|
||||||
</x-slot:actions>
|
</x-slot:actions>
|
||||||
|
|
||||||
<x-table :headers="$header->toArray()" :rows="$rolePermissions">
|
<x-mary-table :headers="$header->toArray()" :rows="$rolePermissions->items()">
|
||||||
@scope('cell_view', $row)
|
@scope('cell_view', $row)
|
||||||
<x-shared.toggle :checked="$row->view"/>
|
<x-shared.toggle :checked="$row->view"/>
|
||||||
@endscope
|
@endscope
|
||||||
@ -55,6 +55,6 @@ public function mount(): void
|
|||||||
@scope('cell_delete', $row)
|
@scope('cell_delete', $row)
|
||||||
<x-shared.toggle :checked="$row->update"/>
|
<x-shared.toggle :checked="$row->update"/>
|
||||||
@endscope
|
@endscope
|
||||||
</x-table>
|
</x-mary-table>
|
||||||
</x-shared.card>
|
</x-shared.card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -80,7 +80,7 @@ public function mount(): void
|
|||||||
</x-shared.button>
|
</x-shared.button>
|
||||||
</x-slot:actions>
|
</x-slot:actions>
|
||||||
|
|
||||||
<x-table :headers="$header->toArray()" :rows="$roles">
|
<x-mary-table :headers="$header->toArray()" :rows="$roles->items()">
|
||||||
@scope('cell_users', $role)
|
@scope('cell_users', $role)
|
||||||
@php /** @var RoleData $role */ @endphp
|
@php /** @var RoleData $role */ @endphp
|
||||||
<div class="flex -space-x-3">
|
<div class="flex -space-x-3">
|
||||||
@ -107,6 +107,6 @@ public function mount(): void
|
|||||||
@scope('actions', $role)
|
@scope('actions', $role)
|
||||||
<x-shared.button icon="lucide-square-pen"/>
|
<x-shared.button icon="lucide-square-pen"/>
|
||||||
@endscope
|
@endscope
|
||||||
</x-table>
|
</x-mary-table>
|
||||||
</x-shared.card>
|
</x-shared.card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -98,7 +98,7 @@ public function mount(): void
|
|||||||
</x-shared.button>
|
</x-shared.button>
|
||||||
</x-slot:actions>
|
</x-slot:actions>
|
||||||
|
|
||||||
<x-table :headers="$header->toArray()" :rows="$users">
|
<x-mary-table :headers="$header->toArray()" :rows="$users->items()">
|
||||||
@scope('cell_name', $row)
|
@scope('cell_name', $row)
|
||||||
<x-shared.avatar :placeholder="$row->name" :title="$row->name" :subtitle="$row->email"/>
|
<x-shared.avatar :placeholder="$row->name" :title="$row->name" :subtitle="$row->email"/>
|
||||||
@endscope
|
@endscope
|
||||||
@ -133,6 +133,6 @@ public function mount(): void
|
|||||||
@scope('actions', $row)
|
@scope('actions', $row)
|
||||||
<x-shared.button icon="lucide-square-pen"/>
|
<x-shared.button icon="lucide-square-pen"/>
|
||||||
@endscope
|
@endscope
|
||||||
</x-table>
|
</x-mary-table>
|
||||||
</x-shared.card>
|
</x-shared.card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
use App\Livewire\Forms\StoreConnectedAppFrom;
|
use App\Livewire\Forms\StoreConnectedAppFrom;
|
||||||
use App\Models\{ConnectionProtocol, ConnectionProvider, ConnectionStatus};
|
use App\Models\{ConnectionProtocol, ConnectionProvider, ConnectionStatus};
|
||||||
use App\Services\{ConnectedAppService, ConnectionProtocolService, ConnectionProviderService, ConnectionStatusService};
|
use App\Services\{ConnectedAppService, ConnectionProtocolService, ConnectionProviderService, ConnectionStatusService};
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Attributes\{Computed, Title};
|
use Livewire\Attributes\{Computed, Title};
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Mary\Traits\Toast;
|
use Mary\Traits\Toast;
|
||||||
@ -60,12 +60,6 @@ public function connectionStatuses(): DataCollection
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function searchProvider(string $name)
|
|
||||||
{
|
|
||||||
$providerService = app(ConnectionProviderService::class);
|
|
||||||
$this->providers = $providerService->search($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function save(bool $goBack = true): void
|
public function save(bool $goBack = true): void
|
||||||
{
|
{
|
||||||
$this->form->validate();
|
$this->form->validate();
|
||||||
@ -111,15 +105,13 @@ public function goBack(): void
|
|||||||
:options="$this->protocols"
|
:options="$this->protocols"
|
||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
/>
|
/>
|
||||||
<x-mary-choices required
|
<x-mary-choices
|
||||||
|
required
|
||||||
wire:model="form.providerId"
|
wire:model="form.providerId"
|
||||||
:label="__('Provider')"
|
:label="__('Provider')"
|
||||||
placeholder="Search ..."
|
placeholder="Select"
|
||||||
:single="true"
|
:single="true"
|
||||||
:options="$this->providers"
|
:options="$this->providers"
|
||||||
searchable
|
|
||||||
search-function="searchProvider"
|
|
||||||
debounce="300"
|
|
||||||
/>
|
/>
|
||||||
<x-mary-choices
|
<x-mary-choices
|
||||||
required
|
required
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
use App\Services\ConnectionProtocolService;
|
use App\Services\ConnectionProtocolService;
|
||||||
use App\Services\ConnectionProviderService;
|
use App\Services\ConnectionProviderService;
|
||||||
use App\Services\ConnectionStatusService;
|
use App\Services\ConnectionStatusService;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Attributes\Lazy;
|
use Livewire\Attributes\Lazy;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -79,12 +79,6 @@ public function connectionStatuses(): DataCollection
|
|||||||
return $statusService->getAll();
|
return $statusService->getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function searchProvider(string $name): void
|
|
||||||
{
|
|
||||||
$providerService = app(ConnectionProviderService::class);
|
|
||||||
$this->providers = $providerService->search($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function save(): void
|
public function save(): void
|
||||||
{
|
{
|
||||||
$this->attempt(
|
$this->attempt(
|
||||||
@ -119,15 +113,13 @@ public function save(): void
|
|||||||
:options="$this->protocols"
|
:options="$this->protocols"
|
||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
/>
|
/>
|
||||||
<x-mary-choices required
|
<x-mary-choices
|
||||||
|
required
|
||||||
wire:model="form.providerId"
|
wire:model="form.providerId"
|
||||||
:label="__('Provider')"
|
:label="__('Provider')"
|
||||||
placeholder="Search ..."
|
placeholder="Select"
|
||||||
:single="true"
|
:single="true"
|
||||||
:options="$this->providers"
|
:options="$this->providers"
|
||||||
searchable
|
|
||||||
search-function="searchProvider"
|
|
||||||
debounce="300"
|
|
||||||
/>
|
/>
|
||||||
<x-mary-choices
|
<x-mary-choices
|
||||||
required
|
required
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user