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

View File

@ -20,7 +20,7 @@ protected function rules() {
}
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;
return $carry;
}, []);

View File

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

View File

@ -11,6 +11,9 @@ body {
@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) {
@apply bg-red-600 hover:bg-red-700;
}
@ -19,9 +22,7 @@ button[isHighlighted]:not(:disabled) {
@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 {
@apply text-2xl font-bold dark:text-white text-neutral-800;
@ -82,6 +83,7 @@ tr td:first-child {
input {
@apply pr-10;
}
.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;
}
@ -89,12 +91,15 @@ .input {
option {
@apply text-white;
}
.alert-success {
@apply flex items-center gap-2 text-success;
}
.alert-error {
@apply flex items-center gap-2 text-error;
}
.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
}
@ -115,8 +120,6 @@ .badge-error {
@apply bg-error;
}
[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");
}
@ -126,11 +129,11 @@ .menu {
}
.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 {
@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 {
@ -170,15 +173,21 @@ .icon:hover {
}
.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 {
@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 {
@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 {
@ -224,8 +233,9 @@ .buyme {
}
.title {
@apply hidden pb-0 lg:block lg:pb-8 ;
@apply hidden pb-0 lg:block lg:pb-8;
}
.subtitle {
@apply pt-2 pb-10;
}
@ -237,4 +247,3 @@ .fullscreen {
.toast {
z-index: 1;
}

View File

@ -1,29 +1,15 @@
<x-layout-simple>
<div class="min-h-screen hero">
<div class="w-96 min-w-fit">
<div class="flex flex-col items-center pb-8">
<div class="text-5xl font-extrabold tracking-tight text-center text-white">Coolify</div>
</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>
@else
<a href="/register"
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.register_now') }}
</a>
@endif
@endif
</div>
<div>
<form action="/login" method="POST" class="flex flex-col gap-2">
@csrf
@env('local')
<section class="bg-gray-50 dark:bg-base">
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
<a class="flex items-center mb-6 text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
Coolify
</a>
<div
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">
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
<form action="/login" method="POST" class="flex flex-col gap-2">
@csrf
@env('local')
<x-forms.input value="test@example.com" type="email" name="email" required
label="{{ __('input.email') }}" autofocus />
@ -33,40 +19,59 @@ class="text-xs text-center text-white normal-case bg-transparent border-none rou
{{ __('auth.forgot_password') }}?
</a>
@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') }}" />
<a href="/forgot-password" class="text-xs">
{{ __('auth.forgot_password') }}?
</a>
@endenv
<x-forms.button type="submit">{{ __('auth.login') }}</x-forms.button>
@foreach ($enabled_oauth_providers as $provider_setting)
<x-forms.button type="button" onclick="document.location.href='/auth/{{$provider_setting->provider}}/redirect'">
{{ __("auth.login.$provider_setting->provider") }}
</x-forms.button>
@endforeach
@if (!$is_registration_enabled)
<div class="text-center ">{{ __('auth.registration_disabled') }}</div>
@endif
@if ($errors->any())
<div class="text-xs text-center text-error">
@foreach ($errors->all() as $error)
<p>{{ $error }}</p>
@endforeach
</div>
@endif
@if (session('status'))
<div class="mb-4 font-medium text-green-600">
{{ session('status') }}
</div>
@endif
@if (session('error'))
<div class="mb-4 font-medium text-red-600">
{{ session('error') }}
</div>
@endif
</form>
@endenv
<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)
<x-forms.button type="button"
onclick="document.location.href='/auth/{{ $provider_setting->provider }}/redirect'">
{{ __("auth.login.$provider_setting->provider") }}
</x-forms.button>
@endforeach
@if (!$is_registration_enabled)
<div class="text-center text-neutral-500">{{ __('auth.registration_disabled') }}</div>
@endif
@if ($errors->any())
<div class="text-xs text-center text-error">
@foreach ($errors->all() as $error)
<p>{{ $error }}</p>
@endforeach
</div>
@endif
@if (session('status'))
<div class="mb-4 font-medium text-green-600">
{{ session('status') }}
</div>
@endif
@if (session('error'))
<div class="mb-4 font-medium text-red-600">
{{ session('error') }}
</div>
@endif
</form>
</div>
</div>
</div>
</div>
</section>
</x-layout-simple>

View File

@ -1,48 +1,53 @@
<x-layout-simple>
<div class="flex items-center justify-center min-h-screen ">
<div class="w-1/2">
<div class="flex flex-col items-center pb-8">
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
<x-version />
<section class="bg-gray-50 dark:bg-base">
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
<a class="flex items-center mb-6 text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
Coolify
</a>
<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">
@csrf
@env('local')
<x-forms.input required value="test3 normal user" type="text" name="name"
label="{{ __('input.name') }}" />
<x-forms.input required value="test3@example.com" type="email" name="email"
label="{{ __('input.email') }}" />
<div class="flex gap-2">
<x-forms.input required value="password" type="password" name="password"
label="{{ __('input.password') }}" />
<x-forms.input required value="password" type="password" name="password_confirmation"
label="{{ __('input.password.again') }}" />
</div>
@else
<x-forms.input required type="text" name="name" label="{{ __('input.name') }}" />
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" />
<div class="flex gap-2">
<x-forms.input required type="password" name="password"
label="{{ __('input.password') }}" />
<x-forms.input required type="password" name="password_confirmation"
label="{{ __('input.password.again') }}" />
</div>
@endenv
<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>
@if ($errors->any())
<div class="text-xs text-center text-error">
@foreach ($errors->all() as $error)
<p>{{ $error }}</p>
@endforeach
</div>
@endif
</div>
</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>
</div>
<form action="/register" method="POST" class="flex flex-col gap-2">
@csrf
@env('local')
<x-forms.input required value="test3 normal user" type="text" name="name"
label="{{ __('input.name') }}" />
<x-forms.input required value="test3@example.com" type="email" name="email"
label="{{ __('input.email') }}" />
<div class="flex gap-2">
<x-forms.input required value="password" type="password" name="password"
label="{{ __('input.password') }}" />
<x-forms.input required value="password" type="password" name="password_confirmation"
label="{{ __('input.password.again') }}" />
</div>
@else
<x-forms.input required type="text" name="name" label="{{ __('input.name') }}" />
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" />
<div class="flex gap-2">
<x-forms.input required type="password" name="password" label="{{ __('input.password') }}" />
<x-forms.input required type="password" name="password_confirmation"
label="{{ __('input.password.again') }}" />
</div>
@endenv
<x-forms.button type="submit">{{ __('auth.register') }}</x-forms.button>
</form>
@if ($errors->any())
<div class="text-xs text-center text-error">
@foreach ($errors->all() as $error)
<p>{{ $error }}</p>
@endforeach
</div>
@endif
</div>
</div>
</section>
</x-layout-simple>

View File

@ -1,9 +1,9 @@
<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]">
<h1 class="text-5xl font-bold">{{ $title }}</h1>
<div class="py-6 ">
<div class="py-6">
@isset($question)
<p class="text-base">
<p class="text-base dark:text-neutral-400">
{{ $question }}
</p>
@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])
<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"
viewBox="0 0 24 24">
<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">
</path>
</svg>
<div>{{ $text }}</div>
</div>

View File

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

View File

@ -10,7 +10,7 @@
<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 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">
<button type="button" class="-m-2.5 p-2.5" x-on:click="open = !open">
<span class="sr-only">Close sidebar</span>
@ -21,21 +21,20 @@
</button>
</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 />
</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="flex flex-col px-4 overflow-y-auto grow gap-y-5 scrollbar">
<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 overflow-y-auto grow gap-y-5 scrollbar">
<x-navbar />
</div>
</div>
<div
class="sticky top-0 z-40 flex items-center px-4 py-4 gap-x-6 sm:px-6 lg:hidden">
<div 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">
<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">
@ -52,7 +51,7 @@ class="sticky top-0 z-40 flex items-center px-4 py-4 gap-x-6 sm:px-6 lg:hidden">
</a> --}}
</div>
<main class="lg:pl-56">
<main class="lg:pl-48">
<div>
<div class="p-4 sm:px-6 lg:px-8 lg:py-6">
{{ $slot }}

View File

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

View File

@ -1,350 +1,335 @@
@php use App\Enums\ProxyTypes; @endphp
<div>
<div>
@if ($currentState === 'welcome')
<section class="bg-gray-50 dark:bg-base">
<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 ($state === 'welcome')
<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>
<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
</x-forms.button>
</div>
@endif
</div>
<div>
@if ($currentState === 'explanation')
<x-boarding-step title="What is Coolify?">
<x-slot:question>
Coolify is an all-in-one application to automate tasks on your servers, deploy application with Git
integrations, deploy databases and services, monitor these resources with notifications and alerts
without vendor lock-in
and <a href="https://coolify.io" class="text-white hover:underline">much much more</a>.
<br><br>
<span class="text-xl">
<x-highlighted text="Self-hosting with superpowers!" /></span>
</x-slot:question>
<x-slot:explanation>
<p><x-highlighted text="Task automation:" /> You don't need to manage your servers anymore. Coolify does
it for you.</p>
<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>
<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>
</x-slot:explanation>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:click="explanation">Next
</x-forms.button>
</x-slot:actions>
</x-boarding-step>
@endif
@if ($currentState === 'select-server-type')
<x-boarding-step title="Server">
<x-slot:question>
Do you want to deploy your resources to your <x-highlighted text="Localhost" />
or to a <x-highlighted text="Remote Server" />?
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:target="setServerType('localhost')"
wire:click="setServerType('localhost')">Localhost
</x-forms.button>
<div>
@if ($state === 'explanation')
<x-boarding-step title="What is Coolify?">
<x-slot:question>
Coolify is an all-in-one application to automate tasks on your servers, deploy application with
Git
integrations, deploy databases and services, monitor these resources with notifications and
alerts
without vendor lock-in
and <a href="https://coolify.io" class="text-white hover:underline">much much more</a>.
<br><br>
<span class="text-xl">
<x-highlighted text="Self-hosting with superpowers!" /></span>
</x-slot:question>
<x-slot:explanation>
<p><x-highlighted text="Task automation:" /> You don't need to manage your servers anymore.
Coolify does
it for you.</p>
<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>
<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>
</x-slot:explanation>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:click="explanation">Next
</x-forms.button>
</x-slot:actions>
</x-boarding-step>
@endif
@if ($state === 'select-server-type')
<x-boarding-step title="Server">
<x-slot:question>
Do you want to deploy your resources to your <x-highlighted text="Localhost" />
or to a <x-highlighted text="Remote Server" />?
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:target="setServerType('localhost')"
wire:click="setServerType('localhost')">Localhost
</x-forms.button>
<x-forms.button class="justify-center w-64 box " wire:target="setServerType('remote')"
wire:click="setServerType('remote')">Remote Server
</x-forms.button>
@if (!$serverReachable)
Localhost is not reachable with the following public key.
<br /> <br />
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
server.
<br />
Check this <a target="_blank" class="underline"
href="https://coolify.io/docs/server/openssh">documentation</a> for further help.
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="w-64 box" wire:target="setServerType('localhost')"
wire:click="setServerType('localhost')">Check again
<x-forms.button class="justify-center w-64 box " wire:target="setServerType('remote')"
wire:click="setServerType('remote')">Remote Server
</x-forms.button>
@endif
</x-slot:actions>
<x-slot:explanation>
<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
are deploying your resources.</p>
<p>Localhost is the server where Coolify is running on. It is not recommended to use one server
for everything.</p>
<p>Remote Server is a server reachable through SSH. It can be hosted at home, or from any cloud
provider.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($currentState === 'private-key')
<x-boarding-step title="SSH Key">
<x-slot:question>
Do you have your own SSH Private Key?
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:target="setPrivateKey('own')"
wire:click="setPrivateKey('own')">Yes
</x-forms.button>
<x-forms.button class="justify-center w-64 box" wire:target="setPrivateKey('create')"
wire:click="setPrivateKey('create')">No (create one for me)
</x-forms.button>
@if (count($privateKeys) > 0)
<form wire:submit='selectExistingPrivateKey' class="flex flex-col w-full gap-4 pr-10">
<x-forms.select label="Existing SSH Keys" id='selectedExistingPrivateKey'>
@foreach ($privateKeys as $privateKey)
<option wire:key="{{ $loop->index }}" value="{{ $privateKey->id }}">
{{ $privateKey->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this SSH Key</x-forms.button>
</form>
@endif
</x-slot:actions>
<x-slot:explanation>
<p>SSH Keys are used to connect to a remote server through a secure shell, called SSH.</p>
<p>You can use your own ssh private key, or you can let Coolify to create one for you.</p>
<p>In both ways, you need to add the public version of your ssh private key to the remote
server's
<code class="text-warning">~/.ssh/authorized_keys</code> file.
</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($currentState === 'select-existing-server')
<x-boarding-step title="Select a server">
<x-slot:question>
There are already servers available for your Team. Do you want to use one of them?
</x-slot:question>
<x-slot:actions>
<div class="flex flex-col gap-4">
<div>
<x-forms.button class="justify-center w-64 box" wire:click="createNewServer">No (create one
for
me)
@if (!$serverReachable)
Localhost is not reachable with the following public key.
<br /> <br />
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
server.
<br />
Check this <a target="_blank" class="underline"
href="https://coolify.io/docs/server/openssh">documentation</a> for further help.
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="w-64 box" wire:target="setServerType('localhost')"
wire:click="setServerType('localhost')">Check again
</x-forms.button>
</div>
<div>
<form wire:submit='selectExistingServer' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing servers" class="w-96" id='selectedExistingServer'>
@foreach ($servers as $server)
<option wire:key="{{ $loop->index }}" value="{{ $server->id }}">
{{ $server->name }}</option>
@endif
</x-slot:actions>
<x-slot:explanation>
<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
are deploying your resources.</p>
<p><x-highlighted text="Localhost" /> is the server where Coolify is running on. It is not
recommended to use one server
for everything.</p>
<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>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($state === 'private-key')
<x-boarding-step title="SSH Key">
<x-slot:question>
Do you have your own SSH Private Key?
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:target="setPrivateKey('own')"
wire:click="setPrivateKey('own')">Yes
</x-forms.button>
<x-forms.button class="justify-center w-64 box" wire:target="setPrivateKey('create')"
wire:click="setPrivateKey('create')">No (create one for me)
</x-forms.button>
@if (count($privateKeys) > 0)
<form wire:submit='selectExistingPrivateKey' class="flex flex-col w-full gap-4 pr-10">
<x-forms.select label="Existing SSH Keys" id='selectedExistingPrivateKey'>
@foreach ($privateKeys as $privateKey)
<option wire:key="{{ $loop->index }}" value="{{ $privateKey->id }}">
{{ $privateKey->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this Server</x-forms.button>
<x-forms.button type="submit">Use this SSH Key</x-forms.button>
</form>
</div>
</div>
@if (!$serverReachable)
This server is not reachable with the following public key.
<br /> <br />
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
server.
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="w-64 box" wire:target="validateServer" wire:click="validateServer">Check
again
</x-forms.button>
@endif
</x-slot:actions>
<x-slot:explanation>
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
<p>You can use your own private key, or you can let Coolify to create one for you.</p>
<p>In both ways, you need to add the public version of your private key to the remote server's
<code>~/.ssh/authorized_keys</code> file.
</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($currentState === 'create-private-key')
<x-boarding-step title="Create Private Key">
<x-slot:question>
Please let me know your key details.
</x-slot:question>
<x-slot:actions>
<form wire:submit='savePrivateKey' class="flex flex-col w-full gap-4 pr-10">
<x-forms.input required placeholder="Choose a name for your Private Key. Could be anything."
label="Name" id="privateKeyName" />
<x-forms.input placeholder="Description, so others will know more about this."
label="Description" id="privateKeyDescription" />
<x-forms.textarea required placeholder="-----BEGIN OPENSSH PRIVATE KEY-----" label="Private Key"
id="privateKey" />
@if ($privateKeyType === 'create')
<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
~/.ssh/authorized_keys
file.</span>
@endif
<x-forms.button type="submit">Save</x-forms.button>
</form>
</x-slot:actions>
<x-slot:explanation>
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
<p>You can use your own private key, or you can let Coolify to create one for you.</p>
<p>In both ways, you need to add the public version of your private key to the remote server's
<code>~/.ssh/authorized_keys</code> file.
</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($currentState === 'create-server')
<x-boarding-step title="Create Server">
<x-slot:question>
Please let me know your server details.
</x-slot:question>
<x-slot:actions>
<form wire:submit='saveServer' class="flex flex-col w-full gap-4 pr-10">
<div class="flex gap-2">
<x-forms.input required placeholder="Choose a name for your Server. Could be anything."
label="Name" id="remoteServerName" />
</x-slot:actions>
<x-slot:explanation>
<p>SSH Keys are used to connect to a remote server through a secure shell, called SSH.</p>
<p>You can use your own ssh private key, or you can let Coolify to create one for you.</p>
<p>In both ways, you need to add the public version of your ssh private key to the remote
server's
<code class="text-warning">~/.ssh/authorized_keys</code> file.
</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($state === 'select-existing-server')
<x-boarding-step title="Select a server">
<x-slot:question>
There are already servers available for your Team. Do you want to use one of them?
</x-slot:question>
<x-slot:actions>
<div class="flex flex-col gap-4">
<div>
<x-forms.button class="justify-center w-64 box" wire:click="createNewServer">No (create
one
for
me)
</x-forms.button>
</div>
<div>
<form wire:submit='selectExistingServer' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing servers" class="w-96" id='selectedExistingServer'>
@foreach ($servers as $server)
<option wire:key="{{ $loop->index }}" value="{{ $server->id }}">
{{ $server->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this Server</x-forms.button>
</form>
</div>
</div>
@if (!$serverReachable)
This server is not reachable with the following public key.
<br /> <br />
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
server.
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="w-64 box" wire:target="validateServer"
wire:click="validateServer">Check
again
</x-forms.button>
@endif
</x-slot:actions>
<x-slot:explanation>
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
<p>You can use your own private key, or you can let Coolify to create one for you.</p>
<p>In both ways, you need to add the public version of your private key to the remote server's
<code>~/.ssh/authorized_keys</code> file.
</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($state === 'create-private-key')
<x-boarding-step title="Create Private Key">
<x-slot:question>
Please let me know your key details.
</x-slot:question>
<x-slot:actions>
<form wire:submit='savePrivateKey' class="flex flex-col w-full gap-4 pr-10">
<x-forms.input required placeholder="Choose a name for your Private Key. Could be anything."
label="Name" id="privateKeyName" />
<x-forms.input placeholder="Description, so others will know more about this."
label="Description" id="remoteServerDescription" />
</div>
<div class="flex gap-2">
<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."
label="Port" id="remoteServerPort" />
<x-forms.input required readonly
placeholder="Username to connect to your server. Default is root." label="Username"
id="remoteServerUser" />
</div>
<div class="w-64">
<x-forms.checkbox
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
id="isCloudflareTunnel" label="Cloudflare Tunnel" />
</div>
<x-forms.button type="submit">Continue</x-forms.button>
</form>
</x-slot:actions>
<x-slot:explanation>
<p>Username should be <x-highlighted text="root" /> for now. We are working on to use
non-root users.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($currentState === 'validate-server')
<x-boarding-step title="Validate & Configure Server">
<x-slot:question>
I need to validate your server (connection, Docker Engine, etc) and configure if something is
missing for me. Are you okay with this?
</x-slot:question>
<x-slot:actions>
<x-slide-over closeWithX fullScreen>
<x-slot:title>Validate & configure</x-slot:title>
<x-slot:content>
<livewire:server.validate-and-install :server="$this->createdServer" />
</x-slot:content>
<x-forms.button @click="slideOverOpen=true" class="font-bold box w-96"
wire:click.prevent='installServer' isHighlighted>
Let's do it!
</x-forms.button>
</x-slide-over>
</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.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install Docker
Engine, check <a target="_blank" class="underline text-warning"
href="https://docs.docker.com/engine/install/#server">this
documentation</a>.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</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>
@if ($currentState === 'create-project')
<x-boarding-step title="Project">
<x-slot:question>
@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?
@else
Let's create an initial project for you. You can change all the details later on.
@endif
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:click="createNewProject">Create new
project!</x-forms.button>
<div>
label="Description" id="privateKeyDescription" />
<x-forms.textarea required placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"
label="Private Key" id="privateKey" />
@if ($privateKeyType === 'create')
<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
~/.ssh/authorized_keys
file.</span>
@endif
<x-forms.button type="submit">Save</x-forms.button>
</form>
</x-slot:actions>
<x-slot:explanation>
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
<p>You can use your own private key, or you can let Coolify to create one for you.</p>
<p>In both ways, you need to add the public version of your private key to the remote server's
<code>~/.ssh/authorized_keys</code> file.
</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($state === 'create-server')
<x-boarding-step title="Create Server">
<x-slot:question>
Please let me know your server details.
</x-slot:question>
<x-slot:actions>
<form wire:submit='saveServer' class="flex flex-col w-full gap-4 pr-10">
<div class="flex gap-2">
<x-forms.input required placeholder="Choose a name for your Server. Could be anything."
label="Name" id="remoteServerName" />
<x-forms.input placeholder="Description, so others will know more about this."
label="Description" id="remoteServerDescription" />
</div>
<div class="flex gap-2">
<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."
label="Port" id="remoteServerPort" />
<x-forms.input required readonly
placeholder="Username to connect to your server. Default is root."
label="Username" id="remoteServerUser" />
</div>
<div class="w-64">
<x-forms.checkbox
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
id="isCloudflareTunnel" label="Cloudflare Tunnel" />
</div>
<x-forms.button type="submit">Continue</x-forms.button>
</form>
</x-slot:actions>
<x-slot:explanation>
<p>Username should be <x-highlighted text="root" /> for now. We are working on to use
non-root users.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($state === 'validate-server')
<x-boarding-step title="Validate & Configure Server">
<x-slot:question>
I need to validate your server (connection, Docker Engine, etc) and configure if something is
missing for me. Are you okay with this?
</x-slot:question>
<x-slot:actions>
<x-slide-over closeWithX fullScreen>
<x-slot:title>Validate & configure</x-slot:title>
<x-slot:content>
<livewire:server.validate-and-install :server="$this->createdServer" />
</x-slot:content>
<x-forms.button @click="slideOverOpen=true" class="font-bold box w-96"
wire:click.prevent='installServer' isHighlighted>
Let's do it!
</x-forms.button>
</x-slide-over>
</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.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install
Docker
Engine, check <a target="_blank" class="underline text-warning"
href="https://docs.docker.com/engine/install/#server">this
documentation</a>.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($state === 'create-project')
<x-boarding-step title="Project">
<x-slot:question>
@if (count($projects) > 0)
<form wire:submit='selectExistingProject' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing projects" class="w-96"
id='selectedExistingProject'>
@foreach ($projects as $project)
<option wire:key="{{ $loop->index }}" value="{{ $project->id }}">
{{ $project->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this Project</x-forms.button>
</form>
You already have some projects. Do you want to use one of them or should I create a new one
for
you?
@else
Let's create an initial project for you. You can change all the details later on.
@endif
</div>
</x-slot:actions>
<x-slot:explanation>
<p>Projects contain several resources combined into one virtual group. There are no
limitations on the number of projects you can add.</p>
<p>Each project should have at least one environment, this allows you to create a production &
staging version of the same application, but grouped separately.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:click="createNewProject">Create new
project!</x-forms.button>
<div>
@if (count($projects) > 0)
<form wire:submit='selectExistingProject' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing projects" class="w-96" id='selectedProject'>
@foreach ($projects as $project)
<option wire:key="{{ $loop->index }}" value="{{ $project->id }}">
{{ $project->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this Project</x-forms.button>
</form>
@endif
</div>
</x-slot:actions>
<x-slot:explanation>
<p>Projects contain several resources combined into one virtual group. There are no
limitations on the number of projects you can add.</p>
<p>Each project should have at least one environment, this allows you to create a production &
staging version of the same application, but grouped separately.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div>
@if ($state === 'create-resource')
<x-boarding-step title="Resources">
<x-slot:question>
Let's go to the new resource page, where you can create your first resource.
</x-slot:question>
<x-slot:actions>
<div class="items-center justify-center w-64 box" wire:click="showNewResource">Let's do
it!</div>
</x-slot:actions>
<x-slot:explanation>
<p>A resource could be an application, a database or a service (like WordPress).</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div class="flex justify-center gap-2 pt-4">
<a wire:click='skipBoarding'>Skip boarding process</a>
<a wire:click='restartBoarding'>Restart boarding process</a>
</div>
</div>
<div>
@if ($currentState === 'create-resource')
<x-boarding-step title="Resources">
<x-slot:question>
Let's go to the new resource page, where you can create your first resource.
</x-slot:question>
<x-slot:actions>
<div class="items-center justify-center w-64 box" wire:click="showNewResource">Let's do
it!</div>
</x-slot:actions>
<x-slot:explanation>
<p>A resource could be an application, a database or a service (like WordPress).</p>
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
<div class="flex justify-center gap-2 pt-4">
<a wire:click='skipBoarding'>Skip boarding process</a>
<a wire:click='restartBoarding'>Restart boarding process</a>
</div>
</div>
</section>

View File

@ -5,7 +5,7 @@
<h1 class="title">Dashboard</h1>
{{-- <div class="subtitle">Your self-hosted environment</div> --}}
@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"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
@ -22,26 +22,26 @@
@if (data_get($project, 'environments')->count() === 1)
<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')]) }}">
<div class="font-bold text-white">{{ $project->name }}</div>
<div class="description">
<div class="box-title">{{ $project->name }}</div>
<div class="box-description">
{{ $project->description }}</div>
</a>
@else
<a class="flex flex-col flex-1 mx-6 hover:no-underline"
href="{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}">
<div class="font-bold text-white">{{ $project->name }}</div>
<div class="description">
<div class="box-title">{{ $project->name }}</div>
<div class="box-description">
{{ $project->description }}</div>
</a>
@endif
<div class="flex items-center">
<a class="mx-4 rounded group-hover:text-white hover:no-underline"
<div class="flex items-center group">
<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')]) }}">
<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 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')]) }}">
<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-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
@ -54,9 +54,9 @@
</div>
@empty
<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
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 />
</div>
@endforelse
@ -71,10 +71,10 @@
'border-red-500' => !$server->settings->is_reachable,
])>
<div class="flex flex-col mx-6">
<div class="font-bold text-white">
<div class="box-title">
{{ $server->name }}
</div>
<div class="description">
<div class="box-description">
{{ $server->description }}</div>
<div class="flex gap-1 text-xs text-error">
@if (!$server->settings->is_reachable)
@ -93,8 +93,9 @@
@empty
<div>
No servers found.
Add your first server <a class="text-white underline" href="{{ route('server.create') }}">here</a> or
go to the <a class="text-white underline" href="{{ route('onboarding') }}">onboarding page.</a>
Add your first server <a class="underline dark:text-white" href="{{ route('server.create') }}">here</a>
or
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding page.</a>
</div>
@endforelse
</div>
@ -116,15 +117,15 @@
'border-yellow-500' => data_get($deployment, 'status') === 'in_progress',
])>
<div class="flex flex-col mx-6">
<div class="font-bold text-white">
<div class="box-title">
{{ data_get($deployment, 'application_name') }}
</div>
@if (data_get($deployment, 'pull_request_id') !== 0)
<div class="description">
<div class="box-description">
PR #{{ data_get($deployment, 'pull_request_id') }}
</div>
@endif
<div class="description">
<div class="box-description">
{{ str(data_get($deployment, 'status'))->headline() }}
</div>
</div>

View File

@ -12,7 +12,7 @@
</x-slide-over>
@endif
</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">
@if ($servers === 0)
<div>

View File

@ -52,7 +52,7 @@ class="items-center justify-center box">+ Add New Resource</a>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full px-4 mx-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')">
<div title="running" class="mt-1 bg-success badge "></div>
</template>
@ -66,16 +66,16 @@ class="items-center justify-center box">+ Add New Resource</a>
<div title="degraded" class="mt-1 bg-warning badge "></div>
</template>
</div>
<div class="max-w-full truncate 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.description"></div>
<div class="max-w-full truncate box-description" x-text="item.fqdn"></div>
</div>
</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">
<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>
</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>
</div>
</span>
@ -85,7 +85,7 @@ class="items-center justify-center box">+ Add New Resource</a>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col px-4 mx-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')">
<div title="running" class="mt-1 bg-success badge "></div>
</template>
@ -99,10 +99,10 @@ class="items-center justify-center box">+ Add New Resource</a>
<div title="degraded" class="mt-1 bg-warning badge "></div>
</template>
</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>
</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">
<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>

View File

@ -5,7 +5,7 @@
<x-forms.button >+ Add</x-forms.button>
</a>
</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">
@forelse ($servers as $server)

View File

@ -4,14 +4,14 @@
</div>
<div class="pb-4 ">Email settings for password resets, invitations, shared with Pro+ subscribers etc.</div>
<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_address" helper="Email address used in emails."
label="From Address" />
<x-forms.button type="submit">
Save
</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">
<form wire:submit='submit' class="flex flex-col gap-2">
<x-forms.input placeholder="test@example.com" id="emails" label="Recipients" required />
@ -21,16 +21,21 @@
</x-forms.button>
</form>
</x-modal-input>
@endif
@endif --}}
</div>
</form>
<div class="flex flex-col gap-4">
<div class="p-4 border border-coolgray-500">
<h3>SMTP Server</h3>
<div class="w-32">
<x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled" />
</div>
<form wire:submit='submit' class="flex flex-col">
<div class="flex gap-2">
<h3>SMTP Server</h3>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="w-32">
<x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled" />
</div>
<div class="flex flex-col gap-4">
<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" />
@ -45,30 +50,25 @@
label="Timeout" />
</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">
Save
</x-forms.button>
</div>
</form>
</div>
<div class="p-4 border border-coolgray-500">
<h3>Resend</h3>
<div class="w-32">
<x-forms.checkbox instantSave='instantSaveResend' id="settings.resend_enabled" label="Enabled" />
</div>
<form wire:submit='submitResend' class="flex flex-col">
<div class="w-32">
<x-forms.checkbox instantSave='instantSaveResend' id="settings.resend_enabled" label="Enabled" />
</div>
<div class="flex flex-col gap-4">
<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
label="Host" />
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
</div>
</div>

View File

@ -10,7 +10,7 @@
@click.prevent="activeTab = 'smtp'; window.location.hash = 'smtp'" href="#">Transactional
Email</a>
<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 class="w-full pl-8">
<div x-cloak x-show="activeTab === 'general'" class="h-full">