Feat: Add modular OIDC app creation with logo support
- Introduced new components and services for OIDC app creation, enabling dynamic handling of app types, URIs, and user roles. - Added support for uploading and managing logos for connected apps. - Implemented `ClientCreationFactory` and related factories for modular OIDC client generation. - Updated UI to enhance app creation flow with post-creation credential display and improved validations. - Refactored backend with `OidcService` for streamlined OIDC app creation and management.
This commit is contained in:
parent
fb7b1e3721
commit
0b77f69818
18
app/Data/Application/ClientCredentialsData.php
Normal file
18
app/Data/Application/ClientCredentialsData.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Data\Application;
|
||||||
|
|
||||||
|
use Spatie\LaravelData\Data;
|
||||||
|
|
||||||
|
final class ClientCredentialsData extends Data
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $clientId,
|
||||||
|
public string $grantType,
|
||||||
|
public ?string $clientSecret = null,
|
||||||
|
public ?array $redirectUris = null,
|
||||||
|
public ?array $scopes = null,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
16
app/Data/Application/CreatedApplicationData.php
Normal file
16
app/Data/Application/CreatedApplicationData.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Data\Application;
|
||||||
|
|
||||||
|
use App\Data\ConnectedApp\ConnectedAppData;
|
||||||
|
use Spatie\LaravelData\Data;
|
||||||
|
|
||||||
|
class CreatedApplicationData extends Data
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ConnectedAppData $application,
|
||||||
|
public ClientCredentialsData $clientCredentials,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
38
app/Data/Application/StoreOIDCAppData.php
Normal file
38
app/Data/Application/StoreOIDCAppData.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Data\Application;
|
||||||
|
|
||||||
|
use App\Enums\{ApplicationTypeEnum, UserAccessTypeEnum};
|
||||||
|
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||||
|
use Spatie\LaravelData\Data;
|
||||||
|
|
||||||
|
class StoreOIDCAppData extends Data
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ApplicationTypeEnum $type,
|
||||||
|
public string $name,
|
||||||
|
public UserAccessTypeEnum $userAccessType,
|
||||||
|
/** @var array<string> */
|
||||||
|
public array $signInUris = [],
|
||||||
|
/** @var array<string> */
|
||||||
|
public array $signOutUris = [],
|
||||||
|
public ?TemporaryUploadedFile $logo = null,
|
||||||
|
/** @var array<int> */
|
||||||
|
public array $roleIds = [],
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function fromArray(ApplicationTypeEnum $applicationType, array $value): StoreOIDCAppData
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
type: $applicationType,
|
||||||
|
name: $value['name'],
|
||||||
|
userAccessType: UserAccessTypeEnum::from($value['userAccessOption']),
|
||||||
|
signInUris: $value['signInUris'] ?? [],
|
||||||
|
signOutUris: $value['signOutUris'] ?? [],
|
||||||
|
logo: $value['logo'] ?? null,
|
||||||
|
roleIds: $value['roleIds'] ?? [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,10 +13,11 @@ class ConnectAppRequest extends Data
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $name,
|
public string $name,
|
||||||
public int $connectionProviderId,
|
|
||||||
public int $connectionProtocolId,
|
public int $connectionProtocolId,
|
||||||
|
public ?int $connectionProviderId = null,
|
||||||
public ?string $slug = null,
|
public ?string $slug = null,
|
||||||
public ?int $connectionStatusId = null,
|
public ?int $connectionStatusId = null,
|
||||||
public ?array $settings = null,
|
public ?array $settings = null,
|
||||||
|
public ?string $logo = null,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,5 +20,6 @@ public function __construct(
|
|||||||
public int $statusId,
|
public int $statusId,
|
||||||
public ?string $slug = null,
|
public ?string $slug = null,
|
||||||
public ?array $settings = null,
|
public ?array $settings = null,
|
||||||
|
public ?string $logo = null
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
25
app/Services/ApplicationLogoUploader.php
Normal file
25
app/Services/ApplicationLogoUploader.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
final readonly class ApplicationLogoUploader
|
||||||
|
{
|
||||||
|
public function store(?UploadedFile $logo): ?string
|
||||||
|
{
|
||||||
|
return $logo?->store('application-logos', 'public');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(?string $path): bool
|
||||||
|
{
|
||||||
|
if (null !== $path) {
|
||||||
|
return Storage::disk('public')->delete($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Applications\OIDC\Factory;
|
||||||
|
|
||||||
|
use App\Data\Application\{ClientCredentialsData, StoreOIDCAppData};
|
||||||
|
use App\Enums\ApplicationTypeEnum;
|
||||||
|
use Exception;
|
||||||
|
use Laravel\Passport\ClientRepository;
|
||||||
|
|
||||||
|
readonly class AuthorizationCodeClientFactory implements ClientCreationContract
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private ClientRepository $client,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function create(
|
||||||
|
StoreOIDCAppData $data
|
||||||
|
): ClientCredentialsData {
|
||||||
|
$client = $this->client->createAuthorizationCodeGrantClient(
|
||||||
|
name: $data->name,
|
||||||
|
redirectUris: $data->signInUris,
|
||||||
|
confidential: ApplicationTypeEnum::Web === $data->type,
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ClientCredentialsData(
|
||||||
|
clientId: $client->id,
|
||||||
|
grantType: 'authorization_code',
|
||||||
|
clientSecret: $client->plainSecret,
|
||||||
|
redirectUris: $client->getAttribute('redirect_uris'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Applications\OIDC\Factory;
|
||||||
|
|
||||||
|
use App\Data\Application\{ClientCredentialsData, StoreOIDCAppData};
|
||||||
|
|
||||||
|
interface ClientCreationContract
|
||||||
|
{
|
||||||
|
public function create(StoreOIDCAppData $data): ClientCredentialsData;
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Applications\OIDC\Factory;
|
||||||
|
|
||||||
|
use App\Enums\ApplicationTypeEnum;
|
||||||
|
|
||||||
|
class ClientCreationFactory
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ClientCredentialsClientFactory $clientCredentialsClient,
|
||||||
|
private readonly AuthorizationCodeClientFactory $authorizationCodeClient,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function for(ApplicationTypeEnum $applicationType): ClientCreationContract
|
||||||
|
{
|
||||||
|
return match ($applicationType) {
|
||||||
|
ApplicationTypeEnum::Web, ApplicationTypeEnum::SPA => $this->authorizationCodeClient,
|
||||||
|
ApplicationTypeEnum::Machine => $this->clientCredentialsClient,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Applications\OIDC\Factory;
|
||||||
|
|
||||||
|
use App\Data\Application\{ClientCredentialsData, StoreOIDCAppData};
|
||||||
|
use Laravel\Passport\ClientRepository;
|
||||||
|
|
||||||
|
class ClientCredentialsClientFactory implements ClientCreationContract
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ClientRepository $client,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function create(StoreOIDCAppData $data): ClientCredentialsData
|
||||||
|
{
|
||||||
|
$client = $this->client->createClientCredentialsGrantClient(
|
||||||
|
name: $data->name,
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ClientCredentialsData(
|
||||||
|
clientId: $client->id,
|
||||||
|
grantType: 'client_credentials',
|
||||||
|
clientSecret: $client->plainSecret,
|
||||||
|
redirectUris: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/Services/Applications/OIDC/OidcService.php
Normal file
59
app/Services/Applications/OIDC/OidcService.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Applications\OIDC;
|
||||||
|
|
||||||
|
use App\Data\Application\{CreatedApplicationData, StoreOIDCAppData};
|
||||||
|
use App\Data\ConnectedApp\ConnectAppRequest;
|
||||||
|
use App\Enums\{ConnectionProtocolEnum, ConnectionStatusEnum};
|
||||||
|
use App\Models\{ConnectionProtocol, ConnectionStatus};
|
||||||
|
use App\Services\{ApplicationLogoUploader, ConnectedAppService};
|
||||||
|
use App\Services\Applications\OIDC\Factory\ClientCreationFactory;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
readonly class OidcService
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private ClientCreationFactory $clientCreationFactory,
|
||||||
|
private ApplicationLogoUploader $logoUploader,
|
||||||
|
private ConnectedAppService $connectedAppService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function createApp(StoreOIDCAppData $data): CreatedApplicationData
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
$logo = $this->logoUploader->store($data->logo);
|
||||||
|
$appData = $this->connectedAppService->create(
|
||||||
|
new ConnectAppRequest(
|
||||||
|
name: $data->name,
|
||||||
|
connectionProtocolId: ConnectionProtocol::query()->where('name', ConnectionProtocolEnum::OIDC->value)->first()->id,
|
||||||
|
connectionProviderId: 0,
|
||||||
|
slug: Str::slug($data->name),
|
||||||
|
connectionStatusId: ConnectionStatus::query()->where('name', ConnectionStatusEnum::Connected->value)->first()->id,
|
||||||
|
logo: $logo,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$clientData = $this->clientCreationFactory->for($data->type)->create($data);
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
// remove sensitive data
|
||||||
|
$appData->settings = null;
|
||||||
|
|
||||||
|
return new CreatedApplicationData(
|
||||||
|
application: $appData,
|
||||||
|
clientCredentials: $clientData,
|
||||||
|
);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,15 +26,17 @@ public function __construct(
|
|||||||
*
|
*
|
||||||
* @throws Throwable when an error occurs during the creation.
|
* @throws Throwable when an error occurs during the creation.
|
||||||
*/
|
*/
|
||||||
public function create(ConnectAppRequest $data): void
|
public function create(ConnectAppRequest $data): ConnectedAppData
|
||||||
{
|
{
|
||||||
DB::transaction(
|
return DB::transaction(
|
||||||
function () use ($data): void {
|
function () use ($data): ConnectedAppData {
|
||||||
$connectedApp = ConnectedApp::query()->create(
|
$connectedApp = ConnectedApp::query()->create(
|
||||||
$data->toArray()
|
$data->toArray()
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->permissionService->add(ConnectedAppData::from($connectedApp), ConnectedAppPermissonEnum::Use);
|
$this->permissionService->add(ConnectedAppData::from($connectedApp), ConnectedAppPermissonEnum::Use);
|
||||||
|
|
||||||
|
return ConnectedAppData::from($connectedApp);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -95,7 +97,7 @@ public function getConnectedApp(int $id): ConnectedAppData
|
|||||||
if ($id <= 0) {
|
if ($id <= 0) {
|
||||||
throw new InvalidArgumentException('Invalid id');
|
throw new InvalidArgumentException('Invalid id');
|
||||||
}
|
}
|
||||||
$data = ConnectedApp::with('provider:id', 'protocol:id', 'status:id')
|
$data = ConnectedApp::with(['provider:id', 'protocol:id', 'status:id'])
|
||||||
->findOrFail($id);
|
->findOrFail($id);
|
||||||
|
|
||||||
return ConnectedAppData::from($data);
|
return ConnectedAppData::from($data);
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class() extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('connected_apps', function (Blueprint $table): void {
|
||||||
|
$table->string('logo')->nullable()->after('name');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('connected_apps', function (Blueprint $table): void {
|
||||||
|
$table->dropColumn('logo');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<x-shared.card title="Connected Apps" icon="lucide-grid-2x2-plus" class="p-0">
|
<x-shared.card title="Connected Apps" icon="lucide-grid-2x2-plus" class="p-0">
|
||||||
<x-slot:actions>
|
<x-slot:actions>
|
||||||
<x-shared.button :link="route('apps.create')">
|
<x-shared.button>
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex items-center gap-x-2">
|
||||||
<x-lucide-plus class="w-5 h-5"/>
|
<x-lucide-plus class="w-5 h-5"/>
|
||||||
{{ __('Connect App') }}
|
{{ __('Connect App') }}
|
||||||
|
|||||||
@ -1,26 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Concerns\HandlesOperations;
|
use App\Concerns\HandlesOperations;
|
||||||
use App\Data\ConnectedApp\{ConnectAppRequest, ConnectionStatusData};
|
use App\Data\Application\StoreOIDCAppData;
|
||||||
use App\Enums\{ApplicationTypeEnum, ConnectionProtocolEnum, ConnectionStatusEnum, UserAccessTypeEnum};
|
use App\Data\ConnectedApp\ConnectedAppData;
|
||||||
use App\Livewire\Forms\StoreOIDCAppForm;
|
use App\Enums\{ApplicationTypeEnum, ConnectionProtocolEnum, UserAccessTypeEnum};
|
||||||
use App\Livewire\Concerns\Choices\{ChoiceField, WithSearchableChoices};
|
use App\Livewire\Concerns\Choices\{ChoiceField, WithSearchableChoices};
|
||||||
use App\Livewire\Concerns\WithRepeaterFields;
|
use App\Livewire\Concerns\WithRepeaterFields;
|
||||||
use App\Models\{ConnectionProtocol, ConnectionProvider, ConnectionStatus};
|
use App\Livewire\Forms\StoreOIDCAppForm;
|
||||||
use App\Services\{ConnectedAppService,
|
use App\Services\{Applications\OIDC\OidcService, RoleService};
|
||||||
ConnectionProtocolService,
|
use Livewire\Attributes\{Url};
|
||||||
ConnectionProviderService,
|
|
||||||
ConnectionStatusService,
|
|
||||||
RoleService
|
|
||||||
};
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Livewire\Attributes\{Computed, Title, Url};
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\WithFileUploads;
|
use Livewire\WithFileUploads;
|
||||||
use Mary\Traits\Toast;
|
|
||||||
use Spatie\LaravelData\DataCollection;
|
|
||||||
|
|
||||||
new #[Title("Create a service")]
|
|
||||||
|
new
|
||||||
class extends Component {
|
class extends Component {
|
||||||
use HandlesOperations, WithRepeaterFields, WithSearchableChoices, WithFileUploads;
|
use HandlesOperations, WithRepeaterFields, WithSearchableChoices, WithFileUploads;
|
||||||
|
|
||||||
@ -30,25 +23,49 @@ class extends Component {
|
|||||||
#[Url]
|
#[Url]
|
||||||
public ?string $type = null;
|
public ?string $type = null;
|
||||||
|
|
||||||
|
// For creation states
|
||||||
|
public bool $created = false;
|
||||||
|
public ?string $clientId = null;
|
||||||
|
public ?string $clientSecret = null;
|
||||||
|
public ?ConnectedAppData $app = null;
|
||||||
|
|
||||||
public StoreOIDCAppForm $form;
|
public StoreOIDCAppForm $form;
|
||||||
public ConnectionProtocolEnum $connectionProtocol = ConnectionProtocolEnum::OIDC;
|
public ConnectionProtocolEnum $connectionProtocol = ConnectionProtocolEnum::OIDC;
|
||||||
public ?ApplicationTypeEnum $applicationType = null;
|
public ApplicationTypeEnum $applicationType;
|
||||||
private RoleService $roleService;
|
private RoleService $roleService;
|
||||||
|
private OidcService $oidcService;
|
||||||
|
|
||||||
public function boot(RoleService $roleService): void
|
|
||||||
|
public function boot(RoleService $roleService, OidcService $oidcService): void
|
||||||
{
|
{
|
||||||
$this->roleService = $roleService;
|
$this->roleService = $roleService;
|
||||||
|
$this->oidcService = $oidcService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
$this->applicationType = ApplicationTypeEnum::tryFrom($this->type);
|
$this->applicationType = ApplicationTypeEnum::from($this->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
|
return $this->view()->title($this->title());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save(bool $goBack = true): void
|
public function save(bool $goBack = true): void
|
||||||
{
|
{
|
||||||
$this->form->validate();
|
$this->form->validate();
|
||||||
ds($this->form->pull());
|
$this->attempt(
|
||||||
|
function () {
|
||||||
|
$data = StoreOIDCAppData::fromArray($this->applicationType, $this->form->pull());
|
||||||
|
$appdata = $this->oidcService->createApp($data);
|
||||||
|
$this->created = true;
|
||||||
|
$this->clientId = $appdata->clientCredentials->clientId;
|
||||||
|
$this->clientSecret = $appdata->clientCredentials->clientSecret;
|
||||||
|
$this->app = $appdata->application;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -64,7 +81,6 @@ public function title(): string
|
|||||||
ApplicationTypeEnum::Web => "Web Application ",
|
ApplicationTypeEnum::Web => "Web Application ",
|
||||||
ApplicationTypeEnum::SPA => "Single Page Application ",
|
ApplicationTypeEnum::SPA => "Single Page Application ",
|
||||||
ApplicationTypeEnum::Machine => "Microservice Application ",
|
ApplicationTypeEnum::Machine => "Microservice Application ",
|
||||||
null => ""
|
|
||||||
};
|
};
|
||||||
$title .= "Integration";
|
$title .= "Integration";
|
||||||
return $title;
|
return $title;
|
||||||
@ -99,6 +115,49 @@ protected function choiceFields(): array
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@if($created && $clientId && $clientSecret)
|
||||||
|
<x-shared.card
|
||||||
|
title="Application Created"
|
||||||
|
icon="lucide-check-circle"
|
||||||
|
>
|
||||||
|
<div class="flex w-full justify-center items-center pt-6">
|
||||||
|
<article class="mx-auto flex items-center gap-4">
|
||||||
|
<x-mary-avatar :image="$app->logo" alt="App logo" class="w-14! rounded-lg! p-2"/>
|
||||||
|
<h1 class="text-2xl font-medium">{{$app->name}}</h1>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-6 p-6">
|
||||||
|
|
||||||
|
<x-mary-alert
|
||||||
|
title="Save these credentials"
|
||||||
|
description="The client secret will only be displayed once."
|
||||||
|
class="alert-warning alert-soft text-yellow-700"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<x-mary-input
|
||||||
|
label="Client ID"
|
||||||
|
:value="$clientId"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
|
||||||
|
<x-mary-input
|
||||||
|
label="Client Secret"
|
||||||
|
:value="$clientSecret"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="flex justify-end gap-4">
|
||||||
|
<x-mary-button
|
||||||
|
wire:click="goBack"
|
||||||
|
class="btn-primary"
|
||||||
|
>
|
||||||
|
Done
|
||||||
|
</x-mary-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</x-shared.card>
|
||||||
|
@else
|
||||||
<x-shared.card :title="$this->title()" icon="lucide-package-plus">
|
<x-shared.card :title="$this->title()" icon="lucide-package-plus">
|
||||||
<x-slot:actions>
|
<x-slot:actions>
|
||||||
<x-mary-button wire:click="goBack" icon="lucide.corner-up-left" class="btn-ghost">Go Back
|
<x-mary-button wire:click="goBack" icon="lucide.corner-up-left" class="btn-ghost">Go Back
|
||||||
@ -111,7 +170,8 @@ protected function choiceFields(): array
|
|||||||
<h2 class="font-bold">General Settings</h2>
|
<h2 class="font-bold">General Settings</h2>
|
||||||
</article>
|
</article>
|
||||||
<div class="w-full md:w-3/5">
|
<div class="w-full md:w-3/5">
|
||||||
<x-mary-input required label="App Name" wire:model="form.name" placeholder="Keka, MS 365 etc."
|
<x-mary-input required label="App Name" wire:model="form.name"
|
||||||
|
placeholder="Keka, MS 365 etc."
|
||||||
hint="A basic name that matches the actual application"/>
|
hint="A basic name that matches the actual application"/>
|
||||||
<x-mary-file wire:model="form.logo" label="Logo" hint="Optional"
|
<x-mary-file wire:model="form.logo" label="Logo" hint="Optional"
|
||||||
accept="application/jpg, application/png, image/jpeg, image/png"/>
|
accept="application/jpg, application/png, image/jpeg, image/png"/>
|
||||||
@ -193,4 +253,5 @@ class="btn-primary">
|
|||||||
</div>
|
</div>
|
||||||
</x-mary-form>
|
</x-mary-form>
|
||||||
</x-shared.card>
|
</x-shared.card>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user