Update UI elements and text content

This commit is contained in:
Andras Bacsai 2024-03-20 15:46:59 +01:00
parent 6b49d32102
commit fafc4fb71e
20 changed files with 576 additions and 539 deletions

View File

@ -2,22 +2,27 @@
namespace App\Livewire\Boarding; namespace App\Livewire\Boarding;
use App\Actions\Server\InstallDocker;
use App\Enums\ProxyTypes; use App\Enums\ProxyTypes;
use App\Models\PrivateKey; use App\Models\PrivateKey;
use App\Models\Project; use App\Models\Project;
use App\Models\Server; use App\Models\Server;
use App\Models\Team; use App\Models\Team;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Livewire\Attributes\Url;
use Livewire\Component; use Livewire\Component;
class Index extends Component class Index extends Component
{ {
protected $listeners = ['serverInstalled' => 'validateServer']; protected $listeners = ['serverInstalled' => 'validateServer'];
public string $currentState = 'welcome';
#[Url()]
public string $state = 'welcome';
#[Url()]
public ?string $selectedServerType = null; public ?string $selectedServerType = null;
public ?Collection $privateKeys = null; public ?Collection $privateKeys = null;
#[Url()]
public ?int $selectedExistingPrivateKey = null; public ?int $selectedExistingPrivateKey = null;
public ?string $privateKeyType = null; public ?string $privateKeyType = null;
public ?string $privateKey = null; public ?string $privateKey = null;
@ -27,6 +32,8 @@ class Index extends Component
public ?PrivateKey $createdPrivateKey = null; public ?PrivateKey $createdPrivateKey = null;
public ?Collection $servers = null; public ?Collection $servers = null;
#[Url()]
public ?int $selectedExistingServer = null; public ?int $selectedExistingServer = null;
public ?string $remoteServerName = null; public ?string $remoteServerName = null;
public ?string $remoteServerDescription = null; public ?string $remoteServerDescription = null;
@ -38,7 +45,9 @@ class Index extends Component
public ?Server $createdServer = null; public ?Server $createdServer = null;
public Collection $projects; public Collection $projects;
public ?int $selectedExistingProject = null;
#[Url()]
public ?int $selectedProject = null;
public ?Project $createdProject = null; public ?Project $createdProject = null;
public bool $dockerInstallationStarted = false; public bool $dockerInstallationStarted = false;
@ -62,13 +71,33 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->remoteServerDescription = 'Created by Coolify'; $this->remoteServerDescription = 'Created by Coolify';
$this->remoteServerHost = 'coolify-testing-host'; $this->remoteServerHost = 'coolify-testing-host';
} }
if ($this->state === 'create-project') {
$this->getProjects();
}
if ($this->state === 'create-resource') {
$this->selectExistingServer();
$this->selectExistingProject();
}
if ($this->state === 'private-key') {
$this->setServerType('remote');
}
if ($this->state === 'create-server') {
$this->selectExistingPrivateKey();
}
if ($this->state === 'validate-server') {
$this->selectExistingServer();
}
if ($this->state === 'select-existing-server') {
$this->selectExistingServer();
}
} }
public function explanation() public function explanation()
{ {
if (isCloud()) { if (isCloud()) {
return $this->setServerType('remote'); return $this->setServerType('remote');
} }
$this->currentState = 'select-server-type'; $this->state = 'select-server-type';
} }
public function restartBoarding() public function restartBoarding()
@ -89,6 +118,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->selectedServerType = $type; $this->selectedServerType = $type;
if ($this->selectedServerType === 'localhost') { if ($this->selectedServerType === 'localhost') {
$this->createdServer = Server::find(0); $this->createdServer = Server::find(0);
$this->selectedExistingServer = 0;
if (!$this->createdServer) { if (!$this->createdServer) {
return $this->dispatch('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.'); return $this->dispatch('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
} }
@ -106,10 +136,10 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->servers = Server::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get(); $this->servers = Server::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get();
if ($this->servers->count() > 0) { if ($this->servers->count() > 0) {
$this->selectedExistingServer = $this->servers->first()->id; $this->selectedExistingServer = $this->servers->first()->id;
$this->currentState = 'select-existing-server'; $this->state = 'select-existing-server';
return; return;
} }
$this->currentState = 'private-key'; $this->state = 'private-key';
} }
} }
public function selectExistingServer() public function selectExistingServer()
@ -117,12 +147,12 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->createdServer = Server::find($this->selectedExistingServer); $this->createdServer = Server::find($this->selectedExistingServer);
if (!$this->createdServer) { if (!$this->createdServer) {
$this->dispatch('error', 'Server is not found.'); $this->dispatch('error', 'Server is not found.');
$this->currentState = 'private-key'; $this->state = 'private-key';
return; return;
} }
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id; $this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
$this->serverPublicKey = $this->createdServer->privateKey->publicKey(); $this->serverPublicKey = $this->createdServer->privateKey->publicKey();
$this->currentState = 'validate-server'; $this->state = 'validate-server';
} }
public function getProxyType() public function getProxyType()
{ {
@ -130,7 +160,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->selectProxy(ProxyTypes::TRAEFIK_V2->value); $this->selectProxy(ProxyTypes::TRAEFIK_V2->value);
// $proxyTypeSet = $this->createdServer->proxy->type; // $proxyTypeSet = $this->createdServer->proxy->type;
// if (!$proxyTypeSet) { // if (!$proxyTypeSet) {
// $this->currentState = 'select-proxy'; // $this->state = 'select-proxy';
// return; // return;
// } // }
$this->getProjects(); $this->getProjects();
@ -139,12 +169,12 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
{ {
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey); $this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
$this->privateKey = $this->createdPrivateKey->private_key; $this->privateKey = $this->createdPrivateKey->private_key;
$this->currentState = 'create-server'; $this->state = 'create-server';
} }
public function createNewServer() public function createNewServer()
{ {
$this->selectedExistingServer = null; $this->selectedExistingServer = null;
$this->currentState = 'private-key'; $this->state = 'private-key';
} }
public function setPrivateKey(string $type) public function setPrivateKey(string $type)
{ {
@ -153,7 +183,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
if ($type === 'create') { if ($type === 'create') {
$this->createNewPrivateKey(); $this->createNewPrivateKey();
} }
$this->currentState = 'create-private-key'; $this->state = 'create-private-key';
} }
public function savePrivateKey() public function savePrivateKey()
{ {
@ -168,7 +198,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
'team_id' => currentTeam()->id 'team_id' => currentTeam()->id
]); ]);
$this->createdPrivateKey->save(); $this->createdPrivateKey->save();
$this->currentState = 'create-server'; $this->state = 'create-server';
} }
public function saveServer() public function saveServer()
{ {
@ -196,7 +226,8 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel; $this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
$this->createdServer->settings->save(); $this->createdServer->settings->save();
$this->createdServer->addInitialNetwork(); $this->createdServer->addInitialNetwork();
$this->currentState = 'validate-server'; $this->selectedExistingServer = $this->createdServer->id;
$this->state = 'validate-server';
} }
public function installServer() public function installServer()
{ {
@ -223,7 +254,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true); $dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true);
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion); $dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
if (is_null($dockerVersion)) { if (is_null($dockerVersion)) {
$this->currentState = 'validate-server'; $this->state = 'validate-server';
throw new \Exception('Docker not found or old version is installed.'); throw new \Exception('Docker not found or old version is installed.');
} }
$this->createdServer->settings()->update([ $this->createdServer->settings()->update([
@ -249,14 +280,14 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
{ {
$this->projects = Project::ownedByCurrentTeam(['name'])->get(); $this->projects = Project::ownedByCurrentTeam(['name'])->get();
if ($this->projects->count() > 0) { if ($this->projects->count() > 0) {
$this->selectedExistingProject = $this->projects->first()->id; $this->selectedProject = $this->projects->first()->id;
} }
$this->currentState = 'create-project'; $this->state = 'create-project';
} }
public function selectExistingProject() public function selectExistingProject()
{ {
$this->createdProject = Project::find($this->selectedExistingProject); $this->createdProject = Project::find($this->selectedProject);
$this->currentState = 'create-resource'; $this->state = 'create-resource';
} }
public function createNewProject() public function createNewProject()
{ {
@ -264,7 +295,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
'name' => "My first project", 'name' => "My first project",
'team_id' => currentTeam()->id 'team_id' => currentTeam()->id
]); ]);
$this->currentState = 'create-resource'; $this->state = 'create-resource';
} }
public function showNewResource() public function showNewResource()
{ {

View File

@ -20,7 +20,7 @@ class Auth extends Component {
} }
public function mount() { public function mount() {
$this->oauth_settings_map = OauthSetting::all()->reduce(function($carry, $setting) { $this->oauth_settings_map = OauthSetting::all()->sortBy('provider')->reduce(function($carry, $setting) {
$carry[$setting->provider] = $setting; $carry[$setting->provider] = $setting;
return $carry; return $carry;
}, []); }, []);

View File

@ -9,7 +9,7 @@
"auth.confirm_password": "Confirm password", "auth.confirm_password": "Confirm password",
"auth.forgot_password": "Forgot password", "auth.forgot_password": "Forgot password",
"auth.forgot_password_send_email": "Send password reset email", "auth.forgot_password_send_email": "Send password reset email",
"auth.register_now": "Register a new account", "auth.register_now": "Register",
"auth.logout": "Logout", "auth.logout": "Logout",
"auth.register": "Register", "auth.register": "Register",
"auth.registration_disabled": "Registration is disabled. Please contact the administrator.", "auth.registration_disabled": "Registration is disabled. Please contact the administrator.",

View File

@ -11,6 +11,9 @@ body {
@apply text-sm antialiased scrollbar; @apply text-sm antialiased scrollbar;
} }
.button {
@apply px-3 py-1.5 text-sm font-normal normal-case rounded bg-neutral-200 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:bg-coolgray-100 dark:disabled:bg-coolgray-100/40 dark:disabled:text-neutral-800 min-w-fit flex items-center justify-center;
}
button[isError]:not(:disabled) { button[isError]:not(:disabled) {
@apply bg-red-600 hover:bg-red-700; @apply bg-red-600 hover:bg-red-700;
} }
@ -19,9 +22,7 @@ button[isHighlighted]:not(:disabled) {
@apply bg-coollabs hover:bg-coollabs-100; @apply bg-coollabs hover:bg-coollabs-100;
} }
.button {
@apply px-3 py-1.5 text-sm font-normal normal-case rounded dark:bg-coolgray-200 dark:text-white dark:hover:bg-coolgray-100 dark:disabled:bg-coolgray-100/40 dark:disabled:text-neutral-800 min-w-fit flex items-center justify-center;
}
h1 { h1 {
@apply text-2xl font-bold dark:text-white text-neutral-800; @apply text-2xl font-bold dark:text-white text-neutral-800;
@ -82,6 +83,7 @@ tr td:first-child {
input { input {
@apply pr-10; @apply pr-10;
} }
.input { .input {
@apply block w-full py-1.5 rounded border-0 text-sm ring-inset ring-1 dark:bg-coolgray-100 dark:text-white text-black focus:ring-2 dark:focus:ring-coolgray-300 dark:ring-coolgray-300 dark:read-only:text-neutral-500 dark:read-only:ring-0 dark:read-only:bg-coolgray-100/40 dark:placeholder:text-neutral-700; @apply block w-full py-1.5 rounded border-0 text-sm ring-inset ring-1 dark:bg-coolgray-100 dark:text-white text-black focus:ring-2 dark:focus:ring-coolgray-300 dark:ring-coolgray-300 dark:read-only:text-neutral-500 dark:read-only:ring-0 dark:read-only:bg-coolgray-100/40 dark:placeholder:text-neutral-700;
} }
@ -89,12 +91,15 @@ input {
option { option {
@apply text-white; @apply text-white;
} }
.alert-success { .alert-success {
@apply flex items-center gap-2 text-success; @apply flex items-center gap-2 text-success;
} }
.alert-error { .alert-error {
@apply flex items-center gap-2 text-error; @apply flex items-center gap-2 text-error;
} }
.dropdown-item { .dropdown-item {
@apply relative flex cursor-pointer select-none dark:hover:text-white dark:hover:bg-coollabs items-center px-2 py-1.5 text-xs justify-center outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 gap-2 @apply relative flex cursor-pointer select-none dark:hover:text-white dark:hover:bg-coollabs items-center px-2 py-1.5 text-xs justify-center outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 gap-2
} }
@ -115,8 +120,6 @@ option {
@apply bg-error; @apply bg-error;
} }
[type='checkbox']:checked { [type='checkbox']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='black' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='black' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
} }
@ -126,11 +129,11 @@ option {
} }
.menu-item { .menu-item {
@apply flex items-center w-full gap-2 px-4 py-1 min-w-48 hover:bg-coolgray-100 dark:hover:text-white; @apply flex items-center w-full gap-2 py-1 pl-2 dark:hover:bg-coolgray-100 dark:hover:text-white hover:bg-neutral-300;
} }
.menu-item-active { .menu-item-active {
@apply rounded-none dark:bg-coolgray-200 dark:text-warning; @apply text-black rounded-none dark:bg-coolgray-200 dark:text-warning bg-neutral-200;
} }
.icon { .icon {
@ -170,15 +173,21 @@ option {
} }
.box { .box {
@apply flex p-2 transition-colors cursor-pointer min-h-[4rem] bg-coolgray-100 hover:bg-coollabs-100 hover:text-white hover:no-underline; @apply flex p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 bg-neutral-50 hover:bg-neutral-200 dark:hover:bg-coollabs-100 dark:hover:text-white hover:no-underline;
} }
.box-without-bg { .box-without-bg {
@apply flex p-2 transition-colors hover:text-white hover:no-underline min-h-[4rem]; @apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem];
} }
.box-title {
@apply font-bold text-black dark:text-white group-hover:dark:text-white;
}
.box-description {
@apply text-xs font-bold text-neutral-500 group-hover:dark:text-white group-hover:text-black;
}
.description { .description {
@apply text-xs font-bold text-neutral-500 group-hover:text-white; @apply text-xs font-bold text-neutral-500 group-hover:dark:text-white group-hover:text-black;
} }
.lds-heart { .lds-heart {
@ -224,8 +233,9 @@ option {
} }
.title { .title {
@apply hidden pb-0 lg:block lg:pb-8 ; @apply hidden pb-0 lg:block lg:pb-8;
} }
.subtitle { .subtitle {
@apply pt-2 pb-10; @apply pt-2 pb-10;
} }
@ -237,4 +247,3 @@ option {
.toast { .toast {
z-index: 1; z-index: 1;
} }

View File

@ -1,26 +1,12 @@
<x-layout-simple> <x-layout-simple>
<div class="min-h-screen hero"> <section class="bg-gray-50 dark:bg-base">
<div class="w-96 min-w-fit"> <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
<div class="flex flex-col items-center pb-8"> <a class="flex items-center mb-6 text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
<div class="text-5xl font-extrabold tracking-tight text-center text-white">Coolify</div> Coolify
</div>
<div class="flex items-center gap-2">
<h1>{{ __('auth.login') }}</h1>
@if ($is_registration_enabled)
@if (config('coolify.waitlist'))
<a href="/waitlist"
class="text-xs text-center text-white normal-case bg-transparent border-none rounded no-animation hover:no-underline btn btn-sm bg-coollabs-gradient">
Join the waitlist
</a> </a>
@else <div
<a href="/register" class="w-full bg-white rounded shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-base dark:border-coolgray-200">
class="text-xs text-center text-white normal-case bg-transparent border-none rounded no-animation hover:no-underline btn btn-sm bg-coollabs-gradient"> <div class="p-6 space-y-4 md:space-y-6 sm:p-8">
{{ __('auth.register_now') }}
</a>
@endif
@endif
</div>
<div>
<form action="/login" method="POST" class="flex flex-col gap-2"> <form action="/login" method="POST" class="flex flex-col gap-2">
@csrf @csrf
@env('local') @env('local')
@ -33,20 +19,38 @@
{{ __('auth.forgot_password') }}? {{ __('auth.forgot_password') }}?
</a> </a>
@else @else
<x-forms.input type="email" name="email" required label="{{ __('input.email') }}" autofocus /> <x-forms.input type="email" name="email" required label="{{ __('input.email') }}"
autofocus />
<x-forms.input type="password" name="password" required label="{{ __('input.password') }}" /> <x-forms.input type="password" name="password" required label="{{ __('input.password') }}" />
<a href="/forgot-password" class="text-xs"> <a href="/forgot-password" class="text-xs">
{{ __('auth.forgot_password') }}? {{ __('auth.forgot_password') }}?
</a> </a>
@endenv @endenv
<x-forms.button type="submit">{{ __('auth.login') }}</x-forms.button> <x-forms.button class="mt-10" type="submit">{{ __('auth.login') }}</x-forms.button>
@if ($is_registration_enabled)
<a href="/register"
class="button bg-coollabs-gradient">
{{ __('auth.register_now') }}
</a>
@endif
@if ($enabled_oauth_providers->isNotEmpty())
<div class="relative">
<div class="absolute inset-0 flex items-center" aria-hidden="true">
<div class="w-full border-t dark:border-coolgray-200"></div>
</div>
<div class="relative flex justify-center">
<span class="px-2 text-sm dark:text-neutral-500 dark:bg-base">or</span>
</div>
</div>
@endif
@foreach ($enabled_oauth_providers as $provider_setting) @foreach ($enabled_oauth_providers as $provider_setting)
<x-forms.button type="button" onclick="document.location.href='/auth/{{$provider_setting->provider}}/redirect'"> <x-forms.button type="button"
onclick="document.location.href='/auth/{{ $provider_setting->provider }}/redirect'">
{{ __("auth.login.$provider_setting->provider") }} {{ __("auth.login.$provider_setting->provider") }}
</x-forms.button> </x-forms.button>
@endforeach @endforeach
@if (!$is_registration_enabled) @if (!$is_registration_enabled)
<div class="text-center ">{{ __('auth.registration_disabled') }}</div> <div class="text-center text-neutral-500">{{ __('auth.registration_disabled') }}</div>
@endif @endif
@if ($errors->any()) @if ($errors->any())
<div class="text-xs text-center text-error"> <div class="text-xs text-center text-error">
@ -69,4 +73,5 @@
</div> </div>
</div> </div>
</div> </div>
</section>
</x-layout-simple> </x-layout-simple>

View File

@ -1,17 +1,15 @@
<x-layout-simple> <x-layout-simple>
<div class="flex items-center justify-center min-h-screen "> <section class="bg-gray-50 dark:bg-base">
<div class="w-1/2"> <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
<div class="flex flex-col items-center pb-8"> <a class="flex items-center mb-6 text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div> Coolify
<x-version />
</div>
<div class="flex items-center gap-2">
<h1>{{ __('auth.register') }}</h1>
<a href="/login"
class="text-xs text-center text-white normal-case bg-transparent border-none rounded no-animation hover:no-underline btn btn-sm bg-coollabs-gradient">
{{ __('auth.already_registered') }}
</a> </a>
</div> <div
class="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-base dark:border-coolgray-200">
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
<h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white">
Create an account
</h1>
<form action="/register" method="POST" class="flex flex-col gap-2"> <form action="/register" method="POST" class="flex flex-col gap-2">
@csrf @csrf
@env('local') @env('local')
@ -29,12 +27,16 @@
<x-forms.input required type="text" name="name" label="{{ __('input.name') }}" /> <x-forms.input required type="text" name="name" label="{{ __('input.name') }}" />
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" /> <x-forms.input required type="email" name="email" label="{{ __('input.email') }}" />
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input required type="password" name="password" label="{{ __('input.password') }}" /> <x-forms.input required type="password" name="password"
label="{{ __('input.password') }}" />
<x-forms.input required type="password" name="password_confirmation" <x-forms.input required type="password" name="password_confirmation"
label="{{ __('input.password.again') }}" /> label="{{ __('input.password.again') }}" />
</div> </div>
@endenv @endenv
<x-forms.button type="submit">{{ __('auth.register') }}</x-forms.button> <x-forms.button class="mb-4" type="submit">Register</x-forms.button>
<a href="/login" class="button bg-coollabs-gradient">
{{ __('auth.already_registered') }}
</a>
</form> </form>
@if ($errors->any()) @if ($errors->any())
<div class="text-xs text-center text-error"> <div class="text-xs text-center text-error">
@ -43,6 +45,9 @@
@endforeach @endforeach
</div> </div>
@endif @endif
</div> </div>
</div> </div>
</div>
</section>
</x-layout-simple> </x-layout-simple>

View File

@ -1,9 +1,9 @@
<div class="grid grid-cols-1 gap-4 md:grid-cols-3"> <div class="grid grid-cols-1 gap-4 md:grid-cols-3">
<div class="box-border col-span-2 min-w-[24rem] min-h-[21rem]"> <div class="box-border col-span-2 min-w-[24rem] min-h-[21rem]">
<h1 class="text-5xl font-bold">{{ $title }}</h1> <h1 class="text-5xl font-bold">{{ $title }}</h1>
<div class="py-6 "> <div class="py-6">
@isset($question) @isset($question)
<p class="text-base"> <p class="text-base dark:text-neutral-400">
{{ $question }} {{ $question }}
</p> </p>
@endisset @endisset

View File

@ -1 +1 @@
<span class="inline-block text-warning">{{ $text }}</span> <span class="inline-block font-bold text-warning">{{ $text }}</span>

View File

@ -1,5 +1,6 @@
@props(['text' => null]) @props(['text' => null])
<div class="inline-flex items-center justify-center" {{ $attributes }}> <div class="inline-flex items-center justify-center" {{ $attributes }}>
<div>{{ $text }}</div>
<svg class="w-4 h-4 mx-1 ml-3 text-warning animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" <svg class="w-4 h-4 mx-1 ml-3 text-warning animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24"> viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
@ -7,5 +8,4 @@
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"> d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path> </path>
</svg> </svg>
<div>{{ $text }}</div>
</div> </div>

View File

@ -1,11 +1,14 @@
<div class="flex items-center h-16 pt-2 lg:pt-4 shrink-0"> <nav class="flex flex-col flex-1 pl-4 border-r dark:border-coolgray-300 dark:bg-coolgray-100 bg-neutral-50">
<a href="/" class="flex items-center w-12 h-12 -mx-1 text-xl font-bold text-white"><img <div class="flex w-full px-2 pt-6 pb-4">
class="transition rounded " src="{{ asset('coolify-transparent.png') }}"></a> <div class="flex flex-col">
</div> <div class="text-2xl font-bold tracking-wide dark:text-white">Coolify</div>
<nav class="flex flex-col flex-1"> <x-version />
</div>
<button onclick="changeTheme()">Dark/light</button>
</div>
<ul role="list" class="flex flex-col flex-1 gap-y-7"> <ul role="list" class="flex flex-col flex-1 gap-y-7">
<li class="flex-1"> <li class="flex-1 ">
<ul role="list" class="flex flex-col h-full -mx-2 space-y-1"> <ul role="list" class="flex flex-col h-full space-y-1.5">
<li> <li>
<a title="Dashboard" href="/" <a title="Dashboard" href="/"
class="{{ request()->is('/') ? 'menu-item-active menu-item' : 'menu-item' }}"> class="{{ request()->is('/') ? 'menu-item-active menu-item' : 'menu-item' }}">
@ -278,8 +281,8 @@
</a> </a>
</li> </li>
</ul> </ul>
</li> --}} </li>
{{-- <li class="mt-auto -mx-6"> <li class="mt-auto -mx-6">
<a href="#" <a href="#"
class="flex items-center px-6 py-3 text-sm font-semibold leading-6 text-gray-900 gap-x-4 hover:bg-gray-50"> class="flex items-center px-6 py-3 text-sm font-semibold leading-6 text-gray-900 gap-x-4 hover:bg-gray-50">
<img class="w-8 h-8 rounded-full bg-gray-50" <img class="w-8 h-8 rounded-full bg-gray-50"

View File

@ -1,2 +1,2 @@
<a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-60 hover:opacity-100 hover:text-white z-[60]']) }} <a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-60 hover:opacity-100 dark:hover:text-white hover:text-black z-[60]']) }}
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('version') }}">v{{ config('version') }}</a> href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('version') }}">v{{ config('version') }}</a>

View File

@ -10,7 +10,7 @@
<div class="relative z-50 lg:hidden" :class="open ? 'block' : 'hidden'" role="dialog" aria-modal="true"> <div class="relative z-50 lg:hidden" :class="open ? 'block' : 'hidden'" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-black/80"></div> <div class="fixed inset-0 bg-black/80"></div>
<div class="fixed inset-0 flex"> <div class="fixed inset-0 flex">
<div class="relative flex flex-1 w-full mr-16 max-w-56 "> <div class="relative flex flex-1 w-full mr-16 max-w-48 ">
<div class="absolute top-0 flex justify-center w-16 pt-5 left-full"> <div class="absolute top-0 flex justify-center w-16 pt-5 left-full">
<button type="button" class="-m-2.5 p-2.5" x-on:click="open = !open"> <button type="button" class="-m-2.5 p-2.5" x-on:click="open = !open">
<span class="sr-only">Close sidebar</span> <span class="sr-only">Close sidebar</span>
@ -21,21 +21,20 @@
</button> </button>
</div> </div>
<div class="flex flex-col px-6 pb-2 overflow-y-auto dark:bg-coolgray-100 gap-y-5 scrollbar"> <div class="flex flex-col pb-2 overflow-y-auto min-w-48 dark:bg-coolgray-100 gap-y-5 scrollbar">
<x-navbar /> <x-navbar />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-56 lg:flex-col"> <div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-48 lg:flex-col">
<div class="flex flex-col px-4 overflow-y-auto grow gap-y-5 scrollbar"> <div class="flex flex-col overflow-y-auto grow gap-y-5 scrollbar">
<x-navbar /> <x-navbar />
</div> </div>
</div> </div>
<div <div class="sticky top-0 z-40 flex items-center px-4 py-4 gap-x-6 sm:px-6 lg:hidden">
class="sticky top-0 z-40 flex items-center px-4 py-4 gap-x-6 sm:px-6 lg:hidden">
<button type="button" class="-m-2.5 p-2.5 dark:text-warning lg:hidden" x-on:click="open = !open"> <button type="button" class="-m-2.5 p-2.5 dark:text-warning lg:hidden" x-on:click="open = !open">
<span class="sr-only">Open sidebar</span> <span class="sr-only">Open sidebar</span>
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24"> <svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24">
@ -52,7 +51,7 @@
</a> --}} </a> --}}
</div> </div>
<main class="lg:pl-56"> <main class="lg:pl-48">
<div> <div>
<div class="p-4 sm:px-6 lg:px-8 lg:py-6"> <div class="p-4 sm:px-6 lg:px-8 lg:py-6">
{{ $slot }} {{ $slot }}

View File

@ -38,10 +38,9 @@
@section('body') @section('body')
<body> <body>
{{-- <button onclick="changeTheme()" class="fixed z-50 left-52">Dark/light</button> --}}
@livewire('wire-elements-modal') @livewire('wire-elements-modal')
<x-toast /> <x-toast />
<x-version class="fixed left-7 bottom-1" /> {{-- <x-version class="fixed left-7 bottom-1" /> --}}
<script data-navigate-once> <script data-navigate-once>
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia( if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia(
'(prefers-color-scheme: dark)').matches)) { '(prefers-color-scheme: dark)').matches)) {

View File

@ -1,22 +1,23 @@
@php use App\Enums\ProxyTypes; @endphp @php use App\Enums\ProxyTypes; @endphp
<div> <section class="bg-gray-50 dark:bg-base">
<div> <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto max-w-7xl md:h-screen lg:py-0">
@if ($currentState === 'welcome') @if ($state === 'welcome')
<h1 class="text-5xl font-bold">Welcome to Coolify</h1> <h1 class="text-5xl font-bold">Welcome to Coolify</h1>
<p class="py-6 text-xl text-center">Let me help you set up the basics.</p> <p class="py-6 text-xl text-center">Let me help you set up the basics.</p>
<div class="flex justify-center "> <div class="flex justify-center ">
<x-forms.button class="justify-center w-64 box" wire:click="$set('currentState','explanation')">Get <x-forms.button class="justify-center w-64 box" wire:click="$set('state','explanation')">Get
Started Started
</x-forms.button> </x-forms.button>
</div> </div>
@endif @endif
</div>
<div> <div>
@if ($currentState === 'explanation') @if ($state === 'explanation')
<x-boarding-step title="What is Coolify?"> <x-boarding-step title="What is Coolify?">
<x-slot:question> <x-slot:question>
Coolify is an all-in-one application to automate tasks on your servers, deploy application with Git Coolify is an all-in-one application to automate tasks on your servers, deploy application with
integrations, deploy databases and services, monitor these resources with notifications and alerts Git
integrations, deploy databases and services, monitor these resources with notifications and
alerts
without vendor lock-in without vendor lock-in
and <a href="https://coolify.io" class="text-white hover:underline">much much more</a>. and <a href="https://coolify.io" class="text-white hover:underline">much much more</a>.
<br><br> <br><br>
@ -24,11 +25,13 @@
<x-highlighted text="Self-hosting with superpowers!" /></span> <x-highlighted text="Self-hosting with superpowers!" /></span>
</x-slot:question> </x-slot:question>
<x-slot:explanation> <x-slot:explanation>
<p><x-highlighted text="Task automation:" /> You don't need to manage your servers anymore. Coolify does <p><x-highlighted text="Task automation:" /> You don't need to manage your servers anymore.
Coolify does
it for you.</p> it for you.</p>
<p><x-highlighted text="No vendor lock-in:" /> All configurations are stored on your servers, so <p><x-highlighted text="No vendor lock-in:" /> All configurations are stored on your servers, so
everything works without a connection to Coolify (except integrations and automations).</p> everything works without a connection to Coolify (except integrations and automations).</p>
<p><x-highlighted text="Monitoring:" />You can get notified on your favourite platforms (Discord, <p><x-highlighted text="Monitoring:" />You can get notified on your favourite platforms
(Discord,
Telegram, Email, etc.) when something goes wrong, or an action is needed from your side.</p> Telegram, Email, etc.) when something goes wrong, or an action is needed from your side.</p>
</x-slot:explanation> </x-slot:explanation>
<x-slot:actions> <x-slot:actions>
@ -37,7 +40,7 @@
</x-slot:actions> </x-slot:actions>
</x-boarding-step> </x-boarding-step>
@endif @endif
@if ($currentState === 'select-server-type') @if ($state === 'select-server-type')
<x-boarding-step title="Server"> <x-boarding-step title="Server">
<x-slot:question> <x-slot:question>
Do you want to deploy your resources to your <x-highlighted text="Localhost" /> Do you want to deploy your resources to your <x-highlighted text="Localhost" />
@ -54,7 +57,8 @@
@if (!$serverReachable) @if (!$serverReachable)
Localhost is not reachable with the following public key. Localhost is not reachable with the following public key.
<br /> <br /> <br /> <br />
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for
user
'root' or skip the boarding process and add a new private key manually to Coolify and to the 'root' or skip the boarding process and add a new private key manually to Coolify and to the
server. server.
<br /> <br />
@ -70,16 +74,18 @@
<p>Servers are the main building blocks, as they will host your applications, databases, <p>Servers are the main building blocks, as they will host your applications, databases,
services, called resources. Any CPU intensive process will use the server's CPU where you services, called resources. Any CPU intensive process will use the server's CPU where you
are deploying your resources.</p> are deploying your resources.</p>
<p>Localhost is the server where Coolify is running on. It is not recommended to use one server <p><x-highlighted text="Localhost" /> is the server where Coolify is running on. It is not
recommended to use one server
for everything.</p> for everything.</p>
<p>Remote Server is a server reachable through SSH. It can be hosted at home, or from any cloud <p><x-highlighted text="A remote server" /> is a server reachable through SSH. It can be hosted
at home, or from any cloud
provider.</p> provider.</p>
</x-slot:explanation> </x-slot:explanation>
</x-boarding-step> </x-boarding-step>
@endif @endif
</div> </div>
<div> <div>
@if ($currentState === 'private-key') @if ($state === 'private-key')
<x-boarding-step title="SSH Key"> <x-boarding-step title="SSH Key">
<x-slot:question> <x-slot:question>
Do you have your own SSH Private Key? Do you have your own SSH Private Key?
@ -115,7 +121,7 @@
@endif @endif
</div> </div>
<div> <div>
@if ($currentState === 'select-existing-server') @if ($state === 'select-existing-server')
<x-boarding-step title="Select a server"> <x-boarding-step title="Select a server">
<x-slot:question> <x-slot:question>
There are already servers available for your Team. Do you want to use one of them? There are already servers available for your Team. Do you want to use one of them?
@ -123,7 +129,8 @@
<x-slot:actions> <x-slot:actions>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div> <div>
<x-forms.button class="justify-center w-64 box" wire:click="createNewServer">No (create one <x-forms.button class="justify-center w-64 box" wire:click="createNewServer">No (create
one
for for
me) me)
</x-forms.button> </x-forms.button>
@ -143,11 +150,13 @@
@if (!$serverReachable) @if (!$serverReachable)
This server is not reachable with the following public key. This server is not reachable with the following public key.
<br /> <br /> <br /> <br />
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for
user
'root' or skip the boarding process and add a new private key manually to Coolify and to the 'root' or skip the boarding process and add a new private key manually to Coolify and to the
server. server.
<x-forms.input readonly id="serverPublicKey"></x-forms.input> <x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="w-64 box" wire:target="validateServer" wire:click="validateServer">Check <x-forms.button class="w-64 box" wire:target="validateServer"
wire:click="validateServer">Check
again again
</x-forms.button> </x-forms.button>
@endif @endif
@ -163,7 +172,7 @@
@endif @endif
</div> </div>
<div> <div>
@if ($currentState === 'create-private-key') @if ($state === 'create-private-key')
<x-boarding-step title="Create Private Key"> <x-boarding-step title="Create Private Key">
<x-slot:question> <x-slot:question>
Please let me know your key details. Please let me know your key details.
@ -174,11 +183,12 @@
label="Name" id="privateKeyName" /> label="Name" id="privateKeyName" />
<x-forms.input placeholder="Description, so others will know more about this." <x-forms.input placeholder="Description, so others will know more about this."
label="Description" id="privateKeyDescription" /> label="Description" id="privateKeyDescription" />
<x-forms.textarea required placeholder="-----BEGIN OPENSSH PRIVATE KEY-----" label="Private Key" <x-forms.textarea required placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"
id="privateKey" /> label="Private Key" id="privateKey" />
@if ($privateKeyType === 'create') @if ($privateKeyType === 'create')
<x-forms.textarea rows="7" readonly label="Public Key" id="publicKey" /> <x-forms.textarea rows="7" readonly label="Public Key" id="publicKey" />
<span class="font-bold text-warning">ACTION REQUIRED: Copy the 'Public Key' to your server's <span class="font-bold text-warning">ACTION REQUIRED: Copy the 'Public Key' to your
server's
~/.ssh/authorized_keys ~/.ssh/authorized_keys
file.</span> file.</span>
@endif @endif
@ -196,7 +206,7 @@
@endif @endif
</div> </div>
<div> <div>
@if ($currentState === 'create-server') @if ($state === 'create-server')
<x-boarding-step title="Create Server"> <x-boarding-step title="Create Server">
<x-slot:question> <x-slot:question>
Please let me know your server details. Please let me know your server details.
@ -210,12 +220,13 @@
label="Description" id="remoteServerDescription" /> label="Description" id="remoteServerDescription" />
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost" /> <x-forms.input required placeholder="127.0.0.1" label="IP Address"
id="remoteServerHost" />
<x-forms.input required placeholder="Port number of your server. Default is 22." <x-forms.input required placeholder="Port number of your server. Default is 22."
label="Port" id="remoteServerPort" /> label="Port" id="remoteServerPort" />
<x-forms.input required readonly <x-forms.input required readonly
placeholder="Username to connect to your server. Default is root." label="Username" placeholder="Username to connect to your server. Default is root."
id="remoteServerUser" /> label="Username" id="remoteServerUser" />
</div> </div>
<div class="w-64"> <div class="w-64">
<x-forms.checkbox <x-forms.checkbox
@ -233,7 +244,7 @@
@endif @endif
</div> </div>
<div> <div>
@if ($currentState === 'validate-server') @if ($state === 'validate-server')
<x-boarding-step title="Validate & Configure Server"> <x-boarding-step title="Validate & Configure Server">
<x-slot:question> <x-slot:question>
I need to validate your server (connection, Docker Engine, etc) and configure if something is I need to validate your server (connection, Docker Engine, etc) and configure if something is
@ -253,7 +264,8 @@
</x-slot:actions> </x-slot:actions>
<x-slot:explanation> <x-slot:explanation>
<p>This will install the latest Docker Engine on your server, configure a few things to be able <p>This will install the latest Docker Engine on your server, configure a few things to be able
to run optimal.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install Docker to run optimal.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install
Docker
Engine, check <a target="_blank" class="underline text-warning" Engine, check <a target="_blank" class="underline text-warning"
href="https://docs.docker.com/engine/install/#server">this href="https://docs.docker.com/engine/install/#server">this
documentation</a>.</p> documentation</a>.</p>
@ -261,40 +273,13 @@
</x-boarding-step> </x-boarding-step>
@endif @endif
</div> </div>
{{-- <div>
@if ($currentState === 'select-proxy')
<x-boarding-step title="Select a Proxy">
<x-slot:question>
If you would like to attach any kind of domain to your resources, you need a proxy.
</x-slot:question>
<x-slot:actions>
<x-forms.button wire:click="selectProxy" class="w-64 box">
Decide later
</x-forms.button>
<x-forms.button class="w-32 box" wire:click="selectProxy('{{ ProxyTypes::TRAEFIK_V2 }}')">
Traefik
v2
</x-forms.button>
<x-forms.button disabled class="w-32 box">
Nginx
</x-forms.button>
<x-forms.button disabled class="w-32 box">
Caddy
</x-forms.button>
</x-slot:actions>
<x-slot:explanation>
<p>This will install the latest Docker Engine on your server, configure a few things to be able
to run optimal.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div> --}}
<div> <div>
@if ($currentState === 'create-project') @if ($state === 'create-project')
<x-boarding-step title="Project"> <x-boarding-step title="Project">
<x-slot:question> <x-slot:question>
@if (count($projects) > 0) @if (count($projects) > 0)
You already have some projects. Do you want to use one of them or should I create a new one for You already have some projects. Do you want to use one of them or should I create a new one
for
you? you?
@else @else
Let's create an initial project for you. You can change all the details later on. Let's create an initial project for you. You can change all the details later on.
@ -306,8 +291,7 @@
<div> <div>
@if (count($projects) > 0) @if (count($projects) > 0)
<form wire:submit='selectExistingProject' class="flex flex-col w-full gap-4 lg:w-96"> <form wire:submit='selectExistingProject' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing projects" class="w-96" <x-forms.select label="Existing projects" class="w-96" id='selectedProject'>
id='selectedExistingProject'>
@foreach ($projects as $project) @foreach ($projects as $project)
<option wire:key="{{ $loop->index }}" value="{{ $project->id }}"> <option wire:key="{{ $loop->index }}" value="{{ $project->id }}">
{{ $project->name }}</option> {{ $project->name }}</option>
@ -328,7 +312,7 @@
@endif @endif
</div> </div>
<div> <div>
@if ($currentState === 'create-resource') @if ($state === 'create-resource')
<x-boarding-step title="Resources"> <x-boarding-step title="Resources">
<x-slot:question> <x-slot:question>
Let's go to the new resource page, where you can create your first resource. Let's go to the new resource page, where you can create your first resource.
@ -347,4 +331,5 @@
<a wire:click='skipBoarding'>Skip boarding process</a> <a wire:click='skipBoarding'>Skip boarding process</a>
<a wire:click='restartBoarding'>Restart boarding process</a> <a wire:click='restartBoarding'>Restart boarding process</a>
</div> </div>
</div> </div>
</section>

View File

@ -5,7 +5,7 @@
<h1 class="title">Dashboard</h1> <h1 class="title">Dashboard</h1>
{{-- <div class="subtitle">Your self-hosted environment</div> --}} {{-- <div class="subtitle">Your self-hosted environment</div> --}}
@if (request()->query->get('success')) @if (request()->query->get('success'))
<div class="mb-10 text-white rounded alert-success"> <div class="mb-10 rounded dark:text-white alert-success">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none" <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
viewBox="0 0 24 24"> viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
@ -22,26 +22,26 @@
@if (data_get($project, 'environments')->count() === 1) @if (data_get($project, 'environments')->count() === 1)
<a class="flex flex-col flex-1 mx-6 hover:no-underline" <a class="flex flex-col flex-1 mx-6 hover:no-underline"
href="{{ route('project.resource.index', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}"> href="{{ route('project.resource.index', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
<div class="font-bold text-white">{{ $project->name }}</div> <div class="box-title">{{ $project->name }}</div>
<div class="description"> <div class="box-description">
{{ $project->description }}</div> {{ $project->description }}</div>
</a> </a>
@else @else
<a class="flex flex-col flex-1 mx-6 hover:no-underline" <a class="flex flex-col flex-1 mx-6 hover:no-underline"
href="{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}"> href="{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}">
<div class="font-bold text-white">{{ $project->name }}</div> <div class="box-title">{{ $project->name }}</div>
<div class="description"> <div class="box-description">
{{ $project->description }}</div> {{ $project->description }}</div>
</a> </a>
@endif @endif
<div class="flex items-center"> <div class="flex items-center group">
<a class="mx-4 rounded group-hover:text-white hover:no-underline" <a class="mx-4 rounded hover:no-underline"
href="{{ route('project.resource.create', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}"> href="{{ route('project.resource.create', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
<span class="font-bold hover:text-warning">+ Add Resource</span> <span class="p-2 font-bold group-hover:dark:text-white group-hover:text-black dark:hover:bg-coollabs hover:bg-neutral-300">+ Add Resource</span>
</a> </a>
<a class="mx-4 rounded group-hover:text-white" <a class="mx-4 rounded group-hover:dark:text-white group-hover:text-black"
href="{{ route('project.edit', ['project_uuid' => data_get($project, 'uuid')]) }}"> href="{{ route('project.edit', ['project_uuid' => data_get($project, 'uuid')]) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning" viewBox="0 0 24 24" <svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-coollabs" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round"> stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path stroke="none" d="M0 0h24v24H0z" fill="none" />
@ -54,9 +54,9 @@
</div> </div>
@empty @empty
<div> <div>
No projects found. Add your first server <a class="text-white underline" No projects found. Add your first server <a class="underline dark:text-white"
onclick="newEmptyProject.showModal()">here</a> or onclick="newEmptyProject.showModal()">here</a> or
go to the <a class="text-white underline" href="{{ route('onboarding') }}">onboarding page.</a> go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding page.</a>
<livewire:project.add-empty /> <livewire:project.add-empty />
</div> </div>
@endforelse @endforelse
@ -71,10 +71,10 @@
'border-red-500' => !$server->settings->is_reachable, 'border-red-500' => !$server->settings->is_reachable,
])> ])>
<div class="flex flex-col mx-6"> <div class="flex flex-col mx-6">
<div class="font-bold text-white"> <div class="box-title">
{{ $server->name }} {{ $server->name }}
</div> </div>
<div class="description"> <div class="box-description">
{{ $server->description }}</div> {{ $server->description }}</div>
<div class="flex gap-1 text-xs text-error"> <div class="flex gap-1 text-xs text-error">
@if (!$server->settings->is_reachable) @if (!$server->settings->is_reachable)
@ -93,8 +93,9 @@
@empty @empty
<div> <div>
No servers found. No servers found.
Add your first server <a class="text-white underline" href="{{ route('server.create') }}">here</a> or Add your first server <a class="underline dark:text-white" href="{{ route('server.create') }}">here</a>
go to the <a class="text-white underline" href="{{ route('onboarding') }}">onboarding page.</a> or
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding page.</a>
</div> </div>
@endforelse @endforelse
</div> </div>
@ -116,15 +117,15 @@
'border-yellow-500' => data_get($deployment, 'status') === 'in_progress', 'border-yellow-500' => data_get($deployment, 'status') === 'in_progress',
])> ])>
<div class="flex flex-col mx-6"> <div class="flex flex-col mx-6">
<div class="font-bold text-white"> <div class="box-title">
{{ data_get($deployment, 'application_name') }} {{ data_get($deployment, 'application_name') }}
</div> </div>
@if (data_get($deployment, 'pull_request_id') !== 0) @if (data_get($deployment, 'pull_request_id') !== 0)
<div class="description"> <div class="box-description">
PR #{{ data_get($deployment, 'pull_request_id') }} PR #{{ data_get($deployment, 'pull_request_id') }}
</div> </div>
@endif @endif
<div class="description"> <div class="box-description">
{{ str(data_get($deployment, 'status'))->headline() }} {{ str(data_get($deployment, 'status'))->headline() }}
</div> </div>
</div> </div>

View File

@ -12,7 +12,7 @@
</x-slide-over> </x-slide-over>
@endif @endif
</div> </div>
<div class="subtitle">All your projects.</div> <div class="subtitle">All your projects are here.</div>
<div class="grid gap-2 lg:grid-cols-2"> <div class="grid gap-2 lg:grid-cols-2">
@if ($servers === 0) @if ($servers === 0)
<div> <div>

View File

@ -52,7 +52,7 @@
<a class="h-24 box group" :href="item.hrefLink"> <a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full px-4 mx-2"> <div class="flex flex-col w-full px-4 mx-2">
<div class="flex gap-2"> <div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div> <div class="pb-2 box-title" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')"> <template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge "></div> <div title="running" class="mt-1 bg-success badge "></div>
</template> </template>
@ -66,16 +66,16 @@
<div title="degraded" class="mt-1 bg-warning badge "></div> <div title="degraded" class="mt-1 bg-warning badge "></div>
</template> </template>
</div> </div>
<div class="max-w-full truncate description" x-text="item.description"></div> <div class="max-w-full truncate box-description" x-text="item.description"></div>
<div class="max-w-full truncate description" x-text="item.fqdn"></div> <div class="max-w-full truncate box-description" x-text="item.fqdn"></div>
</div> </div>
</a> </a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6"> <div class="flex gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
<template x-for="tag in item.tags"> <template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300" <div class="px-2 py-1 cursor-pointer box-description dark:bg-coolgray-100 dark:hover:bg-coolgray-300 bg-neutral-100 hover:bg-neutral-200"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div> @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template> </template>
<div class="flex items-center px-2 text-xs cursor-pointer text-neutral-500/20 group-hover:text-white hover:bg-coolgray-300" <div class="flex items-center px-2 text-xs cursor-pointer dark:text-neutral-500/20 text-neutral-500 group-hover:text-neutral-700 group-hover:dark:text-white dark:hover:bg-coolgray-300 hover:bg-neutral-200"
@click.prevent="goto(item)">Add tag</div> @click.prevent="goto(item)">Add tag</div>
</div> </div>
</span> </span>
@ -85,7 +85,7 @@
<a class="h-24 box group" :href="item.hrefLink"> <a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col px-4 mx-2"> <div class="flex flex-col px-4 mx-2">
<div class="flex gap-2"> <div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div> <div class="pb-2 font-bold box-title" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')"> <template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge "></div> <div title="running" class="mt-1 bg-success badge "></div>
</template> </template>
@ -99,10 +99,10 @@
<div title="degraded" class="mt-1 bg-warning badge "></div> <div title="degraded" class="mt-1 bg-warning badge "></div>
</template> </template>
</div> </div>
<div class="max-w-full truncate description" x-text="item.description"></div> <div class="max-w-full truncate box-description" x-text="item.description"></div>
</div> </div>
</a> </a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6"> <div class="flex gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
<template x-for="tag in item.tags"> <template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300" <div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div> @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>

View File

@ -5,7 +5,7 @@
<x-forms.button >+ Add</x-forms.button> <x-forms.button >+ Add</x-forms.button>
</a> </a>
</div> </div>
<div class="subtitle">All your projects.</div> <div class="subtitle">All your servers are here.</div>
<div class="grid gap-2 lg:grid-cols-2"> <div class="grid gap-2 lg:grid-cols-2">
@forelse ($servers as $server) @forelse ($servers as $server)

View File

@ -4,14 +4,14 @@
</div> </div>
<div class="pb-4 ">Email settings for password resets, invitations, shared with Pro+ subscribers etc.</div> <div class="pb-4 ">Email settings for password resets, invitations, shared with Pro+ subscribers etc.</div>
<form wire:submit='submitFromFields' class="pb-4"> <form wire:submit='submitFromFields' class="pb-4">
<div class="flex flex-col items-end w-full gap-2 xl:flex-row"> <div class="flex gap-2">
<x-forms.input required id="settings.smtp_from_name" helper="Name used in emails." label="From Name" /> <x-forms.input required id="settings.smtp_from_name" helper="Name used in emails." label="From Name" />
<x-forms.input required id="settings.smtp_from_address" helper="Email address used in emails." <x-forms.input required id="settings.smtp_from_address" helper="Email address used in emails."
label="From Address" /> label="From Address" />
<x-forms.button type="submit"> <x-forms.button type="submit">
Save Save
</x-forms.button> </x-forms.button>
@if (isEmailEnabled($settings) && auth()->user()->isAdminFromSession() && isTestEmailEnabled($settings)) {{-- @if (isEmailEnabled($settings) && auth()->user()->isAdminFromSession() && isTestEmailEnabled($settings))
<x-modal-input buttonTitle="Send Test Email" title="Send Test Email"> <x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
<form wire:submit='submit' class="flex flex-col gap-2"> <form wire:submit='submit' class="flex flex-col gap-2">
<x-forms.input placeholder="test@example.com" id="emails" label="Recipients" required /> <x-forms.input placeholder="test@example.com" id="emails" label="Recipients" required />
@ -21,16 +21,21 @@
</x-forms.button> </x-forms.button>
</form> </form>
</x-modal-input> </x-modal-input>
@endif @endif --}}
</div> </div>
</form> </form>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="p-4 border border-coolgray-500"> <div class="p-4 border border-coolgray-500">
<form wire:submit='submit' class="flex flex-col">
<div class="flex gap-2">
<h3>SMTP Server</h3> <h3>SMTP Server</h3>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="w-32"> <div class="w-32">
<x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled" /> <x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled" />
</div> </div>
<form wire:submit='submit' class="flex flex-col">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row"> <div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input required id="settings.smtp_host" placeholder="smtp.mailgun.org" label="Host" /> <x-forms.input required id="settings.smtp_host" placeholder="smtp.mailgun.org" label="Host" />
@ -45,30 +50,25 @@
label="Timeout" /> label="Timeout" />
</div> </div>
</div> </div>
<div class="flex justify-end gap-4 pt-6"> </form>
</div>
<div class="p-4 border border-coolgray-500">
<form wire:submit='submitResend' class="flex flex-col">
<div class="flex gap-2">
<h3>Resend</h3>
<x-forms.button type="submit"> <x-forms.button type="submit">
Save Save
</x-forms.button> </x-forms.button>
</div> </div>
</form>
</div>
<div class="p-4 border border-coolgray-500">
<h3>Resend</h3>
<div class="w-32"> <div class="w-32">
<x-forms.checkbox instantSave='instantSaveResend' id="settings.resend_enabled" label="Enabled" /> <x-forms.checkbox instantSave='instantSaveResend' id="settings.resend_enabled" label="Enabled" />
</div> </div>
<form wire:submit='submitResend' class="flex flex-col">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row"> <div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input type="password" id="settings.resend_api_key" placeholder="API key" required <x-forms.input type="password" id="settings.resend_api_key" placeholder="API key" required
label="Host" /> label="Host" />
</div> </div>
</div> </div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -10,7 +10,7 @@
@click.prevent="activeTab = 'smtp'; window.location.hash = 'smtp'" href="#">Transactional @click.prevent="activeTab = 'smtp'; window.location.hash = 'smtp'" href="#">Transactional
Email</a> Email</a>
<a :class="activeTab === 'auth' && 'text-white'" <a :class="activeTab === 'auth' && 'text-white'"
@click.prevent="activeTab = 'auth'; window.location.hash = 'auth'" href="#">Authentication</a> @click.prevent="activeTab = 'auth'; window.location.hash = 'auth'" href="#">Authentication (OAuth)</a>
</div> </div>
<div class="w-full pl-8"> <div class="w-full pl-8">
<div x-cloak x-show="activeTab === 'general'" class="h-full"> <div x-cloak x-show="activeTab === 'general'" class="h-full">