feature: implement ConnectedApp management with creation and editing support
- Added `ConnectedAppData` DTO for structured data representation. - Implemented Livewire components for ConnectedApp list (`index`) and edit (`edit`). - Integrated `ConnectedAppService` with CRUD operations. - Updated routing to include ConnectedApp management paths. - Enhanced UI with reusable components and dynamic dropdowns for protocols, providers, and statuses.
This commit is contained in:
parent
7bff74a039
commit
c0086ddb6a
@ -13,12 +13,14 @@ trait HandlesOperations
|
|||||||
{
|
{
|
||||||
use Toast;
|
use Toast;
|
||||||
|
|
||||||
public function attempt(callable $action, ?callable $onError = null, string $successMessage = 'Success !', string $errorMessage = 'Something gone wrong!'): void
|
public function attempt(callable $action, ?callable $onError = null, string $successMessage = 'Success !', string $errorMessage = 'Something gone wrong!', bool $showSuccess = true): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// call the action callback
|
// call the action callback
|
||||||
$action();
|
$action();
|
||||||
$this->success($successMessage);
|
if ($showSuccess) {
|
||||||
|
$this->success($successMessage);
|
||||||
|
}
|
||||||
} catch (ValidationException $exception) {
|
} catch (ValidationException $exception) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,7 +38,6 @@ public function attempt(callable $action, ?callable $onError = null, string $suc
|
|||||||
}
|
}
|
||||||
$this->error($errorMessage);
|
$this->error($errorMessage);
|
||||||
Log::error($exception->getMessage());
|
Log::error($exception->getMessage());
|
||||||
Log::error($exception->getTraceAsString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,6 @@ public function __construct(
|
|||||||
public string $name,
|
public string $name,
|
||||||
public int $connectionProviderId,
|
public int $connectionProviderId,
|
||||||
public int $connectionProtocolId,
|
public int $connectionProtocolId,
|
||||||
public ?string $connectionService = null,
|
|
||||||
public ?string $slug = null,
|
public ?string $slug = null,
|
||||||
public ?int $connectionStatusId = null,
|
public ?int $connectionStatusId = null,
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
22
app/Data/ConnectedApp/ConnectedAppData.php
Normal file
22
app/Data/ConnectedApp/ConnectedAppData.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Data\ConnectedApp;
|
||||||
|
|
||||||
|
use Spatie\LaravelData\Attributes\MapInputName;
|
||||||
|
use Spatie\LaravelData\Data;
|
||||||
|
|
||||||
|
final class ConnectedAppData extends Data
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public int $id,
|
||||||
|
public string $name,
|
||||||
|
#[MapInputName('connection_protocol_id')]
|
||||||
|
public int $protocolId,
|
||||||
|
#[MapInputName('connection_provider_id')]
|
||||||
|
public int $providerId,
|
||||||
|
#[MapInputName('connection_status_id')]
|
||||||
|
public int $statusId,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
3
app/Data/Ui/README.md
Normal file
3
app/Data/Ui/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This folder includes DTO that are shared to UI. This DTO should not contains any sensitive data, like model Id if the
|
||||||
|
UI
|
||||||
|
doesn't need it.
|
||||||
@ -6,11 +6,6 @@
|
|||||||
Validations that require the db, should not be live (realtime),
|
Validations that require the db, should not be live (realtime),
|
||||||
they should be done on the only once, preferably after the form is submitted.
|
they should be done on the only once, preferably after the form is submitted.
|
||||||
|
|
||||||
Form can be used to both create and update data. Name should start with:
|
Forms should not be used to create, update or any database operation.
|
||||||
|
|
||||||
- `Store` for both cases - preferred
|
|
||||||
- `Create` for creating a new record
|
|
||||||
- `Update` for updating an existing record
|
|
||||||
|
|
||||||
When handle both cases, use separate `save` and `update` methods.
|
|
||||||
Livewire form docs: https://livewire.laravel.com/docs/4.x/forms
|
Livewire form docs: https://livewire.laravel.com/docs/4.x/forms
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Forms;
|
namespace App\Livewire\Forms;
|
||||||
|
|
||||||
use App\Data\ConnectedApp\ConnectAppRequest;
|
use App\Data\ConnectedApp\ConnectedAppData;
|
||||||
use App\Services\ConnectedAppService;
|
use App\Services\ConnectedAppService;
|
||||||
use Livewire\Attributes\Validate;
|
use Livewire\Attributes\Validate;
|
||||||
use Livewire\Form;
|
use Livewire\Form;
|
||||||
@ -23,18 +23,22 @@ class StoreConnectedAppFrom extends Form
|
|||||||
#[Validate('required|numeric|min:1|exists:connection_statuses,id')]
|
#[Validate('required|numeric|min:1|exists:connection_statuses,id')]
|
||||||
public int $statusId = 0;
|
public int $statusId = 0;
|
||||||
|
|
||||||
public function save(): void
|
protected ConnectedAppService $service;
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
|
|
||||||
$serviceData = new ConnectAppRequest(
|
public function boot(ConnectedAppService $service): void
|
||||||
name: $this->name,
|
{
|
||||||
connectionProviderId: $this->providerId,
|
$this->service = $service;
|
||||||
connectionProtocolId: $this->protocolId,
|
}
|
||||||
slug: str($this->name)->slug()->toString(),
|
|
||||||
connectionStatusId: $this->statusId,
|
/**
|
||||||
);
|
* This sets the form to edit mode, after fetching the connected app of
|
||||||
app(ConnectedAppService::class)->create($serviceData);
|
* the given id.
|
||||||
$this->reset();
|
*/
|
||||||
|
public function set(ConnectedAppData $data): void
|
||||||
|
{
|
||||||
|
$this->name = $data->name;
|
||||||
|
$this->protocolId = $data->protocolId;
|
||||||
|
$this->providerId = $data->providerId;
|
||||||
|
$this->statusId = $data->statusId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Data\ConnectedApp\ConnectAppRequest;
|
use App\Data\ConnectedApp\{ConnectAppRequest, ConnectedAppData};
|
||||||
use App\Data\Ui\ConnectedApps\ConnectedAppData;
|
use App\Data\Ui\ConnectedApps\ConnectedAppData as UiConnectedAppData;
|
||||||
use App\Models\ConnectedApp;
|
use App\Models\ConnectedApp;
|
||||||
|
use http\Exception\InvalidArgumentException;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Spatie\LaravelData\DataCollection;
|
use Spatie\LaravelData\PaginatedDataCollection;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
final class ConnectedAppService
|
final class ConnectedAppService
|
||||||
@ -27,13 +29,37 @@ public function create(ConnectAppRequest $data): void
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function update(int $id, ConnectAppRequest $data): void
|
||||||
* @return DataCollection<int, ConnectedAppData>
|
|
||||||
*/
|
|
||||||
public function getAll(): DataCollection
|
|
||||||
{
|
{
|
||||||
$data = ConnectedApp::with('provider', 'protocol', 'status')->get();
|
if (0 === $id) {
|
||||||
|
throw new InvalidArgumentException('Invalid id');
|
||||||
|
}
|
||||||
|
DB::transaction(
|
||||||
|
fn () => ConnectedApp::query()
|
||||||
|
->where('id', $id)
|
||||||
|
->update($data->toArray())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return ConnectedAppData::collect($data, DataCollection::class);
|
/**
|
||||||
|
* @return PaginatedDataCollection<int, UiConnectedAppData>
|
||||||
|
*/
|
||||||
|
public function getAll(): PaginatedDataCollection
|
||||||
|
{
|
||||||
|
$data = ConnectedApp::with('provider:name,id', 'protocol:id,name', 'status:id,name')
|
||||||
|
->paginate();
|
||||||
|
|
||||||
|
return UiConnectedAppData::collect($data, PaginatedDataCollection::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ModelNotFoundException<ConnectedApp>
|
||||||
|
*/
|
||||||
|
public function getConnectedApp(int $id): ConnectedAppData
|
||||||
|
{
|
||||||
|
$data = ConnectedApp::with('provider:id', 'protocol:id', 'status:id')
|
||||||
|
->findOrFail($id);
|
||||||
|
|
||||||
|
return ConnectedAppData::from($data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,20 +4,11 @@
|
|||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Data\ConnectedApp\StoreConnectionProviderRequestData;
|
|
||||||
use App\Models\ConnectionProvider;
|
use App\Models\ConnectionProvider;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class ConnectionProviderService
|
class ConnectionProviderService
|
||||||
{
|
{
|
||||||
public function create(StoreConnectionProviderRequestData $requestData): void
|
|
||||||
{
|
|
||||||
DB::transaction(function () use ($requestData): void {
|
|
||||||
ConnectionProvider::query()->create($requestData->toArray());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection<int, ConnectionProvider>
|
* @return Collection<int, ConnectionProvider>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -118,15 +118,22 @@ @plugin "daisyui/theme" {
|
|||||||
/* Theme toggle */
|
/* Theme toggle */
|
||||||
@custom-variant dark (&:where(.dark, .dark *));
|
@custom-variant dark (&:where(.dark, .dark *));
|
||||||
|
|
||||||
/**
|
/*pagination style*/
|
||||||
* Paginator - Traditional style
|
.mary-table-pagination {
|
||||||
* Because Laravel defaults does not match well the design of daisyUI.
|
button, span {
|
||||||
*/
|
@apply text-xs border-none
|
||||||
|
}
|
||||||
|
|
||||||
.mary-table-pagination span[aria-current="page"] > span {
|
button {
|
||||||
@apply bg-primary text-base-100
|
@apply cursor-pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
span[aria-current="page"] > span {
|
||||||
|
@apply bg-gray-200!
|
||||||
|
}
|
||||||
|
|
||||||
|
button, span[aria-current="page"] > span, span[aria-disabled="true"] span {
|
||||||
|
@apply py-1 px-2 bg-gray-100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mary-table-pagination button {
|
|
||||||
@apply cursor-pointer
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,18 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\Attributes\On;
|
|
||||||
|
|
||||||
new class extends Component {
|
new class extends Component {
|
||||||
/** Collapsed = icons-only mode */
|
/** Collapsed = icons-only mode */
|
||||||
public bool $collapsed = false;
|
public bool $collapsed = false;
|
||||||
|
|
||||||
/** Active nav key — passed as a prop from the parent layout */
|
public function mount(): void
|
||||||
public string $active = 'dashboard';
|
|
||||||
|
|
||||||
public function mount(string $active = 'dashboard'): void
|
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
|
||||||
$this->collapsed = session('sidebar_collapsed', false);
|
$this->collapsed = session('sidebar_collapsed', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,16 +21,23 @@ public function navItems(): array
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
'key' => 'dashboard',
|
|
||||||
'label' => 'Dashboard',
|
'label' => 'Dashboard',
|
||||||
'icon' => 'lucide-house',
|
'icon' => 'lucide.house',
|
||||||
'route' => 'home',
|
'route' => 'dashboard',
|
||||||
|
'active_pattern' => 'dashboard',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'key' => 'users',
|
|
||||||
'label' => 'Users',
|
'label' => 'Users',
|
||||||
'icon' => 'lucide-users',
|
'icon' => 'lucide.users',
|
||||||
'route' => 'home',
|
'route' => 'home',
|
||||||
|
'active_pattern' => 'home',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'label' => 'Apps',
|
||||||
|
'icon' => 'lucide.grid-2x2-plus',
|
||||||
|
'route' => 'connected-apps.index',
|
||||||
|
// Using a wildcard ensures the tab stays active on nested routes like connected-apps.edit
|
||||||
|
'active_pattern' => 'connected-apps.*',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -50,7 +52,6 @@ class="relative flex flex-col h-screen bg-white border-r border-gray-100
|
|||||||
>
|
>
|
||||||
|
|
||||||
<div class="flex items-center h-20 px-2 border-b border-gray-100 shrink-0">
|
<div class="flex items-center h-20 px-2 border-b border-gray-100 shrink-0">
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="{{ route('dashboard') }}"
|
href="{{ route('dashboard') }}"
|
||||||
wire:navigate
|
wire:navigate
|
||||||
@ -60,9 +61,7 @@ class="shrink-0 flex items-center justify-center w-8 h-8
|
|||||||
focus-visible:ring-2 focus-visible:ring-cyan-400"
|
focus-visible:ring-2 focus-visible:ring-cyan-400"
|
||||||
aria-label="Go to dashboard"
|
aria-label="Go to dashboard"
|
||||||
>
|
>
|
||||||
<span class="w-4 h-4">
|
<x-mary-icon name='lucide.shield-cog' class="w-6 h-6"/>
|
||||||
@svg('lucide-shield-cog')
|
|
||||||
</span>
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
@ -86,10 +85,11 @@ class="flex-1 py-3 px-2 space-y-0.5 overflow-y-auto overflow-x-hidden"
|
|||||||
aria-label="Main navigation"
|
aria-label="Main navigation"
|
||||||
>
|
>
|
||||||
@foreach ($this->navItems() as $item)
|
@foreach ($this->navItems() as $item)
|
||||||
@php $isActive = $active === $item['key']; @endphp
|
{{-- Check if the current route matches the pattern --}}
|
||||||
|
@php $isActive = request()->routeIs($item['active_pattern']); @endphp
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href=""
|
href="{{ route($item['route']) }}"
|
||||||
wire:navigate
|
wire:navigate
|
||||||
@class([
|
@class([
|
||||||
'group relative flex items-center gap-3 px-2 py-2.5 rounded-lg',
|
'group relative flex items-center gap-3 px-2 py-2.5 rounded-lg',
|
||||||
@ -107,9 +107,7 @@ class="absolute left-0 inset-y-2 w-0.5 rounded-r-full bg-blue-600"
|
|||||||
></span>
|
></span>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<span class="w-4 h-4">
|
<x-mary-icon :name="$item['icon']" class="w-4 h-4"/>
|
||||||
@svg($item['icon'])
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{{-- Label — hidden when collapsed --}}
|
{{-- Label — hidden when collapsed --}}
|
||||||
<span
|
<span
|
||||||
@ -188,5 +186,4 @@ class="whitespace-nowrap"
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
147
resources/views/pages/connected-apps/⚡edit.blade.php
Normal file
147
resources/views/pages/connected-apps/⚡edit.blade.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Concerns\HandlesOperations;
|
||||||
|
use App\Data\ConnectedApp\ConnectAppRequest;
|
||||||
|
use App\Data\ConnectedApp\ConnectionStatusData;
|
||||||
|
use App\Enums\ConnectionStatusEnum;
|
||||||
|
use App\Livewire\Forms\StoreConnectedAppFrom;
|
||||||
|
use App\Models\ConnectionProtocol;
|
||||||
|
use App\Models\ConnectionProvider;
|
||||||
|
use App\Models\ConnectionStatus;
|
||||||
|
use App\Services\ConnectedAppService;
|
||||||
|
use App\Services\ConnectionProtocolService;
|
||||||
|
use App\Services\ConnectionProviderService;
|
||||||
|
use App\Services\ConnectionStatusService;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Livewire\Attributes\Computed;
|
||||||
|
use Livewire\Attributes\Lazy;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\Attributes\Title;
|
||||||
|
use Mary\Traits\Toast;
|
||||||
|
use Spatie\LaravelData\DataCollection;
|
||||||
|
|
||||||
|
new
|
||||||
|
#[Lazy]
|
||||||
|
#[Title('Edit a connected app')]
|
||||||
|
class extends Component {
|
||||||
|
use HandlesOperations;
|
||||||
|
|
||||||
|
public StoreConnectedAppFrom $form;
|
||||||
|
protected ConnectedAppService $service;
|
||||||
|
public int $recordId = 0;
|
||||||
|
|
||||||
|
public function boot(ConnectedAppService $service): void
|
||||||
|
{
|
||||||
|
$this->service = $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount(int $id): void
|
||||||
|
{
|
||||||
|
$this->attempt(
|
||||||
|
action: function () use ($id) {
|
||||||
|
$record = $this->service->getConnectedApp($id);
|
||||||
|
$this->form->set($record);
|
||||||
|
$this->recordId = $id;
|
||||||
|
},
|
||||||
|
onError: function () {
|
||||||
|
$this->redirectRoute('connected-apps.index', navigate: true);
|
||||||
|
},
|
||||||
|
showSuccess: false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Computed]
|
||||||
|
public function providers()
|
||||||
|
{
|
||||||
|
return ConnectionProvider::query()->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[Computed]
|
||||||
|
public function protocols(): Collection
|
||||||
|
{
|
||||||
|
$protocolService = app(ConnectionProtocolService::class);
|
||||||
|
|
||||||
|
return $protocolService->getAll()->map(function (ConnectionProtocol $protocol) {
|
||||||
|
$protocol->name = strtoupper($protocol->name);
|
||||||
|
return $protocol;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns DataCollection<int, ConnectionStatus>
|
||||||
|
*/
|
||||||
|
#[Computed]
|
||||||
|
public function connectionStatuses(): DataCollection
|
||||||
|
{
|
||||||
|
$statusService = app(ConnectionStatusService::class);
|
||||||
|
|
||||||
|
return $statusService->getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchProvider(string $name): void
|
||||||
|
{
|
||||||
|
$providerService = app(ConnectionProviderService::class);
|
||||||
|
$this->providers = $providerService->search($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save(): void
|
||||||
|
{
|
||||||
|
$this->attempt(
|
||||||
|
action: function () {
|
||||||
|
$this->form->validate();
|
||||||
|
$serviceData = new ConnectAppRequest(
|
||||||
|
name: $this->form->name,
|
||||||
|
connectionProviderId: $this->form->providerId,
|
||||||
|
connectionProtocolId: $this->form->protocolId,
|
||||||
|
slug: str($this->form->name)->slug()->toString(),
|
||||||
|
connectionStatusId: $this->form->statusId,
|
||||||
|
);
|
||||||
|
$this->service->update($this->recordId, $serviceData);
|
||||||
|
$this->redirectRoute('connected-apps.index', navigate: true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<x-shared.card :title="__('Edit an connected app')" class="">
|
||||||
|
<form wire:submit="save" class="p-4 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<x-input wire:model.live.blur="form.name" required :label="__('Name')"/>
|
||||||
|
<x-choices
|
||||||
|
required
|
||||||
|
wire:model="form.protocolId"
|
||||||
|
:label="__('Protocol')"
|
||||||
|
:single="true"
|
||||||
|
:options="$this->protocols"
|
||||||
|
placeholder="Select"
|
||||||
|
/>
|
||||||
|
<x-choices required
|
||||||
|
wire:model="form.providerId"
|
||||||
|
:label="__('Provider')"
|
||||||
|
placeholder="Search ..."
|
||||||
|
:single="true"
|
||||||
|
:options="$this->providers"
|
||||||
|
searchable
|
||||||
|
search-function="searchProvider"
|
||||||
|
debounce="300"
|
||||||
|
/>
|
||||||
|
<x-choices
|
||||||
|
required
|
||||||
|
wire:model="form.statusId"
|
||||||
|
:label="__('Status')"
|
||||||
|
:single="true"
|
||||||
|
:options="$this->connectionStatuses->toArray()"
|
||||||
|
placeholder="Select"
|
||||||
|
/>
|
||||||
|
<div class="md:col-span-2 flex gap-x-4 font-medium justify-end">
|
||||||
|
<x-shared.button>Cancel</x-shared.button>
|
||||||
|
<x-shared.button type="submit"
|
||||||
|
class="bg-blue-600 text-blue-50 hover:bg-blue-700 border-blue-600 min-w-20 flex justify-center items-center">
|
||||||
|
Save
|
||||||
|
</x-shared.button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</x-shared.card>
|
||||||
|
</div>
|
||||||
76
resources/views/pages/connected-apps/⚡index.blade.php
Normal file
76
resources/views/pages/connected-apps/⚡index.blade.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Data\Ui\ConnectedApps\ConnectedAppData;
|
||||||
|
use App\Data\Ui\TableHeader;
|
||||||
|
use App\Services\ConnectedAppService;
|
||||||
|
use Livewire\Attributes\Computed;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\WithPagination;
|
||||||
|
use Spatie\LaravelData\Attributes\DataCollectionOf;
|
||||||
|
use Spatie\LaravelData\DataCollection;
|
||||||
|
use Spatie\LaravelData\PaginatedDataCollection;
|
||||||
|
|
||||||
|
new class extends Component {
|
||||||
|
use WithPagination;
|
||||||
|
|
||||||
|
#[DataCollectionOf(TableHeader::class)]
|
||||||
|
public DataCollection $headers;
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->initTableHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initTableHeaders(): void
|
||||||
|
{
|
||||||
|
$this->headers = TableHeader::collect([
|
||||||
|
new TableHeader(key: 'name', label: 'Name',),
|
||||||
|
new TableHeader(key: 'provider', label: 'Provider'),
|
||||||
|
new TableHeader(key: 'protocol', label: 'Protocol'),
|
||||||
|
new TableHeader(key: 'status', label: 'Status'),
|
||||||
|
], DataCollection::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PaginatedDataCollection<int, ConnectedAppData>
|
||||||
|
*/
|
||||||
|
#[Computed]
|
||||||
|
public function getConnectedApps(): PaginatedDataCollection
|
||||||
|
{
|
||||||
|
$service = app(ConnectedAppService::class);
|
||||||
|
return $service->getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $appId): void
|
||||||
|
{
|
||||||
|
$this->redirectRoute('connected-apps.edit', ['id' => $appId], navigate: true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<x-shared.card title="Connected Apps" class="pb-2">
|
||||||
|
<x-slot:actions>
|
||||||
|
<x-mary-button icon="lucide.plus">Add App</x-mary-button>
|
||||||
|
</x-slot:actions>
|
||||||
|
<x-mary-table
|
||||||
|
:headers="$headers->toArray()"
|
||||||
|
:rows="$this->getConnectedApps->items()"
|
||||||
|
with-pagination
|
||||||
|
>
|
||||||
|
@scope('cell_protocol', $row)
|
||||||
|
{{$row->protocol->name}}
|
||||||
|
@endscope
|
||||||
|
|
||||||
|
@scope('cell_status', $row)
|
||||||
|
<x-shared.connection-status :status="$row->status"/>
|
||||||
|
@endscope
|
||||||
|
@scope('actions', $row)
|
||||||
|
<div class="flex">
|
||||||
|
<x-mary-button icon="lucide.edit" wire:click="edit({{$row->id}})" class="mr-2 btn-sm text-gray-500"/>
|
||||||
|
<x-mary-button icon="lucide.trash" variant="danger" class="btn-sm btn-error btn-soft"/>
|
||||||
|
</div>
|
||||||
|
@endscope
|
||||||
|
</x-mary-table>
|
||||||
|
</x-shared.card>
|
||||||
|
</div>
|
||||||
@ -11,9 +11,11 @@
|
|||||||
|
|
||||||
Route::prefix('connected-apps')->name('connected-apps.')->group(function (): void {
|
Route::prefix('connected-apps')->name('connected-apps.')->group(function (): void {
|
||||||
Route::livewire('create', 'pages::connected-apps.create')->name('create');
|
Route::livewire('create', 'pages::connected-apps.create')->name('create');
|
||||||
|
Route::livewire('{id}/edit', 'pages::connected-apps.edit')->name('edit');
|
||||||
|
Route::livewire('/', 'pages::connected-apps.index')->name('index');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::prefix('connection-providers')->name('users.')->group(function (): void {
|
Route::prefix('connection-providers')->name('connectionProviders.')->group(function (): void {
|
||||||
Route::livewire('create', 'pages::connecton-providers.create')->name('create');
|
Route::livewire('create', 'pages::connecton-providers.create')->name('create');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user