ui ui ui ui ui ui ux ux ux ux ux ux

This commit is contained in:
Andras Bacsai 2024-03-21 12:44:32 +01:00
parent 8f66a41c09
commit b5775ff9d2
61 changed files with 597 additions and 430 deletions

View File

@ -42,19 +42,21 @@ public function mount()
public function delete() public function delete()
{ {
// TODO: Delete backup from server and add a confirmation modal try {
$this->backup->delete(); $this->backup->delete();
if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') { if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
$previousUrl = url()->previous(); $previousUrl = url()->previous();
$url = Url::fromString($previousUrl); $url = Url::fromString($previousUrl);
$url = $url->withoutQueryParameter('selectedBackupId'); $url = $url->withoutQueryParameter('selectedBackupId');
$url = $url->withFragment('backups'); $url = $url->withFragment('backups');
$url = $url->getPath() . "#{$url->getFragment()}"; $url = $url->getPath() . "#{$url->getFragment()}";
return redirect($url); return redirect($url);
} else { } else {
return redirect()->route('project.database.backup.index', $this->parameters); return redirect()->route('project.database.backup.index', $this->parameters);
}
} catch (\Throwable $e) {
return handleError($e, $this);
} }
} }
public function instantSave() public function instantSave()
@ -63,7 +65,7 @@ public function instantSave()
$this->custom_validate(); $this->custom_validate();
$this->backup->save(); $this->backup->save();
$this->backup->refresh(); $this->backup->refresh();
$this->dispatch('success', 'Backup updated successfully'); $this->dispatch('success', 'Backup updated successfully.');
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->dispatch('error', $e->getMessage()); $this->dispatch('error', $e->getMessage());
} }

View File

@ -34,7 +34,12 @@ public function mount()
} }
$this->refreshFileStorages(); $this->refreshFileStorages();
} }
public function instantSaveAdvanced() public function instantSaveExclude()
{
$this->submit();
}
public function instantSaveLogDrain()
{ {
if (!$this->database->service->destination->server->isLogDrainEnabled()) { if (!$this->database->service->destination->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false; $this->database->is_log_drain_enabled = false;
@ -74,6 +79,7 @@ public function refreshFileStorages()
public function submit() public function submit()
{ {
try { try {
ray('asdf');
$this->validate(); $this->validate();
$this->database->save(); $this->database->save();
updateCompose($this->database); updateCompose($this->database);

View File

@ -1,11 +1,11 @@
<?php <?php
namespace App\Livewire\Modal; namespace App\Livewire\Project\Service;
use App\Models\Service; use App\Models\Service;
use LivewireUI\Modal\ModalComponent; use Livewire\Component;
class EditCompose extends ModalComponent class EditCompose extends Component
{ {
public Service $service; public Service $service;
public $serviceId; public $serviceId;
@ -16,13 +16,13 @@ class EditCompose extends ModalComponent
public function mount() { public function mount() {
$this->service = Service::find($this->serviceId); $this->service = Service::find($this->serviceId);
} }
public function render()
{ public function saveEditedCompose() {
return view('livewire.modal.edit-compose');
}
public function submit() {
$this->dispatch('warning', "Saving new docker compose..."); $this->dispatch('warning', "Saving new docker compose...");
$this->dispatch('saveCompose', $this->service->docker_compose_raw); $this->dispatch('saveCompose', $this->service->docker_compose_raw);
$this->closeModal(); }
public function render()
{
return view('livewire.project.service.edit-compose');
} }
} }

View File

@ -1,13 +0,0 @@
<?php
namespace App\Livewire\Project\Service;
use Livewire\Component;
class Modal extends Component
{
public function render()
{
return view('livewire.project.service.modal');
}
}

View File

@ -16,17 +16,24 @@ class Navbar extends Component
public array $parameters; public array $parameters;
public array $query; public array $query;
public $isDeploymentProgress = false; public $isDeploymentProgress = false;
public function getListeners() public function getListeners()
{ {
$userId = auth()->user()->id;
return [ return [
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
"serviceStatusChanged" "serviceStatusChanged"
]; ];
} }
public function serviceStarted() {
$this->dispatch('success', 'Service started.');
}
public function serviceStatusChanged() public function serviceStatusChanged()
{ {
$this->dispatch('refresh')->self(); $this->dispatch('refresh')->self();
} }
public function check_status() { public function check_status()
{
$this->dispatch('check_status'); $this->dispatch('check_status');
$this->dispatch('success', 'Service status updated.'); $this->dispatch('success', 'Service status updated.');
} }
@ -44,7 +51,7 @@ public function checkDeployments()
$this->isDeploymentProgress = false; $this->isDeploymentProgress = false;
} }
} }
public function deploy() public function start()
{ {
$this->checkDeployments(); $this->checkDeployments();
if ($this->isDeploymentProgress) { if ($this->isDeploymentProgress) {
@ -73,9 +80,9 @@ public function restart()
return; return;
} }
PullImage::run($this->service); PullImage::run($this->service);
$this->dispatch('image-pulled');
StopService::run($this->service); StopService::run($this->service);
$this->service->parse(); $this->service->parse();
$this->dispatch('imagePulled');
$activity = StartService::run($this->service); $activity = StartService::run($this->service);
$this->dispatch('activityMonitor', $activity->id); $this->dispatch('activityMonitor', $activity->id);
} }

View File

@ -2,11 +2,12 @@
namespace App\Livewire\Project\Service; namespace App\Livewire\Project\Service;
use App\Models\Service;
use Livewire\Component; use Livewire\Component;
class StackForm extends Component class StackForm extends Component
{ {
public $service; public Service $service;
public $fields = []; public $fields = [];
protected $listeners = ["saveCompose"]; protected $listeners = ["saveCompose"];
public $rules = [ public $rules = [

View File

@ -50,6 +50,7 @@ public function changePrivateKey()
$this->private_key->private_key = formatPrivateKey($this->private_key->private_key); $this->private_key->private_key = formatPrivateKey($this->private_key->private_key);
$this->private_key->save(); $this->private_key->save();
refresh_server_connection($this->private_key); refresh_server_connection($this->private_key);
$this->dispatch('success', 'Private key updated.');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@ -49,7 +49,8 @@ public function proxyStatusUpdated()
{ {
$this->server->refresh(); $this->server->refresh();
} }
public function restart() { public function restart()
{
try { try {
$this->stop(); $this->stop();
$this->dispatch('checkProxy'); $this->dispatch('checkProxy');

View File

@ -19,7 +19,7 @@ public function __construct(
public ?string $helper = null, public ?string $helper = null,
public string|bool $instantSave = false, public string|bool $instantSave = false,
public bool $disabled = false, public bool $disabled = false,
public string $defaultClass = "border-coolgray-500 text-warning focus:ring-warning dark:bg-coolgray-100 rounded cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed", public string $defaultClass = "dark:border-neutral-700 text-warning focus:ring-warning dark:bg-coolgray-100 rounded cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed",
) { ) {
// //
} }

View File

@ -94,7 +94,7 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$resource->service->docker_compose_raw = $dockerComposeRaw; $resource->service->docker_compose_raw = $dockerComposeRaw;
$resource->service->save(); $resource->service->save();
if (!str($resource->fqdn)->contains(',')) { if ($resource->fqdn && !str($resource->fqdn)->contains(',')) {
// Update FQDN // Update FQDN
$variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper(); $variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper();
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();

View File

@ -4,7 +4,7 @@
html, html,
body { body {
@apply text-black bg-white dark:bg-base dark:text-neutral-400; @apply bg-white text-neutral-600 dark:bg-base dark:text-neutral-400;
} }
body { body {
@ -12,7 +12,7 @@ body {
} }
.button { .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; @apply flex items-center justify-center px-3 py-1 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;
} }
button[isError]:not(:disabled) { button[isError]:not(:disabled) {
@apply bg-red-600 hover:bg-red-700; @apply bg-red-600 hover:bg-red-700;
@ -23,7 +23,6 @@ button[isHighlighted]:not(:disabled) {
} }
h1 { h1 {
@apply text-2xl font-bold dark:text-white text-neutral-800; @apply text-2xl font-bold dark:text-white text-neutral-800;
} }
@ -41,7 +40,7 @@ h4 {
} }
a { a {
@apply dark:hover:text-white dark:text-neutral-400 text-neutral-600; @apply hover:text-black dark:hover:text-white;
} }
label { label {
@ -101,7 +100,7 @@ .alert-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 pr-4 pl-2 py-1 text-xs justify-center outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 gap-2 w-full;
} }
.badge { .badge {

View File

@ -11,15 +11,13 @@ class="inline-flex items-center justify-center py-1 pr-12 text-sm font-medium tr
stroke-width="1.5" stroke="currentColor"> stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" <path stroke-linecap="round" stroke-linejoin="round"
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" /> d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
</svg> </svg>
</button> </button>
<div x-show="dropdownOpen" @click.away="dropdownOpen=false" x-transition:enter="ease-out duration-200" <div x-show="dropdownOpen" @click.away="dropdownOpen=false" x-transition:enter="ease-out duration-200"
x-transition:enter-start="-translate-y-2" x-transition:enter-end="translate-y-0" x-transition:enter-start="-translate-y-2" x-transition:enter-end="translate-y-0"
class="absolute top-0 z-50 w-64 mt-6 -translate-x-1/2 left-1/2" x-cloak> class="absolute top-0 z-50 mt-6 min-w-max" x-cloak>
<div class="p-1 mt-1 dark:bg-coolgray-100 "> <div class="p-1 mt-1 dark:bg-coolgray-200">
{{-- <div class="px-2 py-1.5 text-sm font-semibold">Domains</div> --}}
{{-- <div class="h-px my-1 -mx-1 "></div> --}}
@if ( @if (
(data_get($application, 'fqdn') || (data_get($application, 'fqdn') ||
collect(json_decode($this->application->docker_compose_domains))->count() > 0 || collect(json_decode($this->application->docker_compose_domains))->count() > 0 ||

View File

@ -1,46 +0,0 @@
<div class="navbar-main">
<a class="{{ request()->routeIs('project.database.configuration') ? 'text-white' : '' }}"
href="{{ route('project.database.configuration', $parameters) }}">
<button>Configuration</button>
</a>
<a class="{{ request()->routeIs('project.database.command') ? 'text-white' : '' }}"
href="{{ route('project.database.command', $parameters) }}">
<button>Execute Command</button>
</a>
<a class="{{ request()->routeIs('project.database.logs') ? 'text-white' : '' }}"
href="{{ route('project.database.logs', $parameters) }}">
<button>Logs</button>
</a>
@if (
$database->getMorphClass() === 'App\Models\StandalonePostgresql' ||
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
<a class="{{ request()->routeIs('project.database.backup.index') ? 'text-white' : '' }}"
href="{{ route('project.database.backup.index', $parameters) }}">
<button>Backups</button>
</a>
@endif
<div class="flex-1"></div>
@if (!str($database->status)->startsWith('exited'))
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
</svg>
Stop
</button>
@else
<button wire:click='start' onclick="startDatabase.showModal()"
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" 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" />
<path d="M7 4v16l13 -8z" />
</svg>
Start
</button>
@endif
</div>

View File

@ -1,4 +1,4 @@
<div class="flex flex-row items-center gap-4 px-2 py-1 my-2 form-control min-w-fit dark:hover:bg-coolgray-100"> <div class="flex flex-row items-center gap-4 px-2 py-1 form-control min-w-fit dark:hover:bg-coolgray-100">
<label class="flex gap-4 px-0 min-w-fit label"> <label class="flex gap-4 px-0 min-w-fit label">
<span class="flex gap-2"> <span class="flex gap-2">
@if ($label) @if ($label)

View File

@ -1,6 +1,8 @@
@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> @if (isset($text))
<div>{{ $text }}</div>
@endif
<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>

View File

@ -14,11 +14,17 @@ class="relative w-auto h-auto">
</div> </div>
@else @else
@if ($disabled) @if ($disabled)
<x-forms.button isError disabled>{{ $buttonTitle }}</x-forms.button> <x-forms.button isError disabled>
{{ $buttonTitle }}
</x-forms.button>
@elseif ($isErrorButton) @elseif ($isErrorButton)
<x-forms.button isError @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button> <x-forms.button isError @click="modalOpen=true">
{{ $buttonTitle }}
</x-forms.button>
@else @else
<x-forms.button @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button> <x-forms.button @click="modalOpen=true" class="flex gap-2">
{{ $buttonTitle }}
</x-forms.button>
@endif @endif
@endif @endif
<template x-teleport="body"> <template x-teleport="body">
@ -53,14 +59,36 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
type="button">Cancel type="button">Cancel
</x-forms.button> </x-forms.button>
<div class="flex-1"></div> <div class="flex-1"></div>
@if ($isErrorButton) @if ($attributes->whereStartsWith('wire:click')->first())
<x-forms.button @click="modalOpen=false" class="w-24" isError type="button" @if ($isErrorButton)
wire:click.prevent="{{ $action }}">Continue <x-forms.button @click="modalOpen=false" class="w-24" isError type="button"
</x-forms.button> wire:click.prevent="{{ $attributes->get('wire:click') }}">Continue
@else </x-forms.button>
<x-forms.button @click="modalOpen=false" class="w-24" isHighlighted type="button" @else
wire:click.prevent="{{ $action }}">Continue <x-forms.button @click="modalOpen=false" class="w-24" isHighlighted type="button"
</x-forms.button> wire:click.prevent="{{ $attributes->get('wire:click') }}">Continue
</x-forms.button>
@endif
@elseif ($attributes->whereStartsWith('@click')->first())
@if ($isErrorButton)
<x-forms.button class="w-24" isError type="button"
@click="modalOpen=false;{{ $attributes->get('@click') }}">Continue
</x-forms.button>
@else
<x-forms.button class="w-24" isHighlighted type="button"
@click="modalOpen=false;{{ $attributes->get('@click') }}">Continue
</x-forms.button>
@endif
@elseif ($action)
@if ($isErrorButton)
<x-forms.button @click="modalOpen=false" class="w-24" isError type="button"
wire:click.prevent="{{ $action }}">Continue
</x-forms.button>
@else
<x-forms.button @click="modalOpen=false" class="w-24" isHighlighted type="button"
wire:click.prevent="{{ $action }}">Continue
</x-forms.button>
@endif
@endif @endif
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
<nav class="flex flex-col flex-1 pl-4 border-r dark:border-coolgray-300 dark:bg-coolgray-100 bg-neutral-50"> <nav class="flex flex-col flex-1 pl-4 border-r dark:border-coolgray-200 dark:bg-base bg-neutral-50">
<div class="flex w-full px-2 pt-6 pb-4"> <div class="flex w-full px-2 pt-6 pb-4">
<div class="flex flex-col"> <div class="flex flex-col">
<div class="text-2xl font-bold tracking-wide dark:text-white">Coolify</div> <div class="text-2xl font-bold tracking-wide dark:text-white">Coolify</div>

View File

@ -10,7 +10,7 @@
{{ $title }} {{ $title }}
</div> </div>
@if($upgrade) @if($upgrade)
<div class="text-neutral-500">{{ $upgrade }}</div> <div>{{ $upgrade }}</div>
@else @else
<div class="hidden text-xs font-bold text-neutral-500 group-hover:flex"> <div class="hidden text-xs font-bold text-neutral-500 group-hover:flex">
{{ $description }} {{ $description }}

View File

@ -1,31 +1,36 @@
@if ($links->count() > 0) @if ($links->count() > 0)
<div class="group"> <div x-data="{
<label tabindex="0" class="flex items-center gap-2 cursor-pointer hover:text-white"> Open Application dropdownOpen: false
<x-chevron-down /> }" class="relative" @click.outside="dropdownOpen = false">
</label> <button @click="dropdownOpen=true"
class="inline-flex items-center justify-center py-1 pr-12 text-sm font-medium transition-colors focus:outline-none disabled:opacity-50 disabled:pointer-events-none">
<span class="flex flex-col items-start flex-shrink-0 h-full ml-2 leading-none translate-y-px">
Open Application
</span>
<svg class="absolute right-0 w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
</svg>
</button>
<div class="absolute z-50 hidden group-hover:block"> <div x-show="dropdownOpen" @click.away="dropdownOpen=false" x-transition:enter="ease-out duration-200"
<ul tabindex="0" x-transition:enter-start="-translate-y-2" x-transition:enter-end="translate-y-0"
class="relative -ml-24 text-xs text-white normal-case rounded min-w-max menu bg-coolgray-200"> class="absolute top-0 z-50 mt-6 min-w-max" x-cloak>
@if ($links->count() > 0) <div class="p-1 mt-1 dark:bg-coolgray-200">
@foreach ($links as $link) @foreach ($links as $link)
<li> <a class="dropdown-item" target="_blank" href="{{ $link }}">
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white" <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5"
target="_blank" href="{{ $link }}"> stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" <path stroke="none" d="M0 0h24v24H0z" fill="none" />
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" <path d="M9 15l6 -6" />
stroke-linejoin="round"> <path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path
<path d="M9 15l6 -6" /> d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" /> </svg>{{ $link }}
<path </a>
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" /> @endforeach
</svg>{{ $link }} </div>
</a>
</li>
@endforeach
@endif
</ul>
</div> </div>
</div> </div>
@endif @endif

View File

@ -1,94 +0,0 @@
<div class="navbar-main" x-data>
<a class="{{ request()->routeIs('project.service.configuration') ? 'text-white' : '' }}"
href="{{ route('project.service.configuration', $parameters) }}">
<button>Configuration</button>
</a>
<x-services.links />
<div class="flex-1"></div>
@if (str($service->status())->contains('running'))
<button wire:click='restart' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</g>
</svg>
Pull Latest Images & Restart
</button>
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
</svg>
Stop
</button>
@elseif (str($service->status())->contains('degraded'))
<button wire:click='deploy' onclick="startService.showModal()"
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</g>
</svg>
Restart Degraded Services
</button>
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
</svg>
Stop
</button>
@elseif (str($service->status())->contains('exited'))
<button wire:click='stop(true)'
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg class="w-5 h-5 " viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
<path fill="red"
d="M17.003 20a4.895 4.895 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.699 5.699 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2Zm-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848ZM15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.617 16.617 0 0 1 10 24H8a17.342 17.342 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13.025 13.025 0 0 0 17.596 28Z" />
</svg>
Force Cleanup Containers
</button>
<button wire:click='deploy' onclick="startService.showModal()"
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" 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" />
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
</button>
@else
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
</svg>
Stop
</button>
<button wire:click='deploy' onclick="startService.showModal()"
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" 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" />
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
</button>
@endif
</div>
@script
<script>
$wire.on('image-pulled', () => {
startService.showModal();
});
</script>
@endscript

View File

@ -1,15 +1,16 @@
@props(['closeWithX' => false, 'fullScreen' => false]) @props(['closeWithX' => false, 'fullScreen' => false])
<div x-data="{ <div x-data="{
slideOverOpen: false slideOverOpen: false
}" class="relative w-auto h-auto"> }" class="relative w-auto h-auto" {{ $attributes }}
>
{{ $slot }} {{ $slot }}
<template x-teleport="body"> <template x-teleport="body">
<div x-show="slideOverOpen" @if (!$closeWithX) @keydown.window.escape="slideOverOpen=false" @endif <div x-show="slideOverOpen" @if (!$closeWithX) @keydown.window.escape="slideOverOpen=false" @endif
class="relative z-[99]"> class="relative z-[99] ">
<div x-show="slideOverOpen" @if (!$closeWithX) @click="slideOverOpen = false" @endif <div x-show="slideOverOpen" @if (!$closeWithX) @click="slideOverOpen = false" @endif
class="fixed inset-0 bg-black bg-opacity-60"></div> class="fixed inset-0 bg-black bg-opacity-60"></div>
<div class="fixed inset-0 overflow-hidden"> <div class="fixed inset-0 overflow-hidden">
<div class="absolute inset-0 overflow-hidden"> <div class="absolute inset-0 overflow-hidden ">
<div class="fixed inset-y-0 right-0 flex max-w-full pl-10"> <div class="fixed inset-y-0 right-0 flex max-w-full pl-10">
<div x-show="slideOverOpen" <div x-show="slideOverOpen"
@if (!$closeWithX) @click.away="slideOverOpen = false" @endif @if (!$closeWithX) @click.away="slideOverOpen = false" @endif
@ -39,7 +40,7 @@ class="absolute top-0 right-0 z-30 flex items-center justify-center px-3 py-2 mt
</div> </div>
</div> </div>
</div> </div>
<div class="relative flex-1 px-4 mt-5 sm:px-5"> <div class="relative flex-1 px-4 mt-5 overflow-auto sm:px-5 scrollbar">
<div class="absolute inset-0 px-4 sm:px-5"> <div class="absolute inset-0 px-4 sm:px-5">
{{ $content }} {{ $content }}
</div> </div>

View File

@ -312,7 +312,7 @@
} }
} }
});" });"
class="fixed block w-full group z-[99] sm:max-w-xs" class="fixed block w-full group z-[9999] sm:max-w-xs"
:class="{ 'right-0 top-0 sm:mt-6 sm:mr-6': position=='top-right', 'left-0 top-0 sm:mt-6 sm:ml-6': position=='top-left', 'left-1/2 -translate-x-1/2 top-0 sm:mt-6': position=='top-center', 'right-0 bottom-0 sm:mr-6 sm:mb-6': position=='bottom-right', 'left-0 bottom-0 sm:ml-6 sm:mb-6': position=='bottom-left', 'left-1/2 -translate-x-1/2 bottom-0 sm:mb-6': position=='bottom-center' }" :class="{ 'right-0 top-0 sm:mt-6 sm:mr-6': position=='top-right', 'left-0 top-0 sm:mt-6 sm:ml-6': position=='top-left', 'left-1/2 -translate-x-1/2 top-0 sm:mt-6': position=='top-center', 'right-0 bottom-0 sm:mr-6 sm:mb-6': position=='bottom-right', 'left-0 bottom-0 sm:ml-6 sm:mb-6': position=='bottom-left', 'left-1/2 -translate-x-1/2 bottom-0 sm:mb-6': position=='bottom-center' }"
x-cloak> x-cloak>

View File

@ -153,7 +153,19 @@ function copyToClipboard(text) {
} }
}) })
window.Livewire.on('info', (message) => { window.Livewire.on('info', (message) => {
if (message.length > 0) { if (typeof message === 'string') {
window.toast('Info', {
type: 'info',
description: message,
})
return;
}
if (message.length == 1) {
window.toast('Info', {
type: 'info',
description: message[0],
})
} else if (message.length == 2) {
window.toast(message[0], { window.toast(message[0], {
type: 'info', type: 'info',
description: message[1], description: message[1],
@ -161,6 +173,13 @@ function copyToClipboard(text) {
} }
}) })
window.Livewire.on('error', (message) => { window.Livewire.on('error', (message) => {
if (typeof message === 'string') {
window.toast('Error', {
type: 'danger',
description: message,
})
return;
}
if (message.length == 1) { if (message.length == 1) {
window.toast('Error', { window.toast('Error', {
type: 'danger', type: 'danger',
@ -174,7 +193,19 @@ function copyToClipboard(text) {
} }
}) })
window.Livewire.on('warning', (message) => { window.Livewire.on('warning', (message) => {
if (message.length > 0) { if (typeof message === 'string') {
window.toast('Warning', {
type: 'warning',
description: message,
})
return;
}
if (message.length == 1) {
window.toast('Warning', {
type: 'warning',
description: message[0],
})
} else if (message.length == 2) {
window.toast(message[0], { window.toast(message[0], {
type: 'warning', type: 'warning',
description: message[1], description: message[1],
@ -182,6 +213,13 @@ function copyToClipboard(text) {
} }
}) })
window.Livewire.on('success', (message) => { window.Livewire.on('success', (message) => {
if (typeof message === 'string') {
window.toast('Success', {
type: 'success',
description: message,
})
return;
}
if (message.length == 1) { if (message.length == 1) {
window.toast('Success', { window.toast('Success', {
type: 'success', type: 'success',

View File

@ -1,12 +1,19 @@
@extends('layouts.base') @extends('layouts.base')
@section('body') @section('body')
<div title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs"> <x-modal-input title="How can we help?">
<button class="flex items-center justify-center gap-2" wire:click="help" onclick="help.showModal()"> <x-slot:content>
<svg class="icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"> <div title="Send us feedback or get help!" class="cursor-pointer menu-item" wire:click="help"
<path fill="currentColor" d="M140 180a12 12 0 1 1-12-12a12 12 0 0 1 12 12M128 72c-22.06 0-40 16.15-40 36v4a8 8 0 0 0 16 0v-4c0-11 10.77-20 24-20s24 9 24 20s-10.77 20-24 20a8 8 0 0 0-8 8v8a8 8 0 0 0 16 0v-.72c18.24-3.35 32-17.9 32-35.28c0-19.85-17.94-36-40-36m104 56A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88"/> onclick="help.showModal()">
</svg> <svg class="icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
</button> <path fill="currentColor"
</div> d="M140 180a12 12 0 1 1-12-12a12 12 0 0 1 12 12M128 72c-22.06 0-40 16.15-40 36v4a8 8 0 0 0 16 0v-4c0-11 10.77-20 24-20s24 9 24 20s-10.77 20-24 20a8 8 0 0 0-8 8v8a8 8 0 0 0 16 0v-.72c18.24-3.35 32-17.9 32-35.28c0-19.85-17.94-36-40-36m104 56A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88" />
</svg>
Feedback
</div>
</x-slot:content>
<livewire:help />
</x-modal-input>
<main class="min-h-screen hero"> <main class="min-h-screen hero">
<div class="hero-content"> <div class="hero-content">
{{ $slot }} {{ $slot }}

View File

@ -3,16 +3,19 @@
@if ($this->activity) @if ($this->activity)
@if (isset($header)) @if (isset($header))
<div class="flex gap-2 pb-2"> <div class="flex gap-2 pb-2">
<h2>{{ $header }}</h2> <h3>{{ $header }}</h3>
@if ($isPollingActive) @if ($isPollingActive)
<x-loading /> <x-loading />
@endif @endif
</div> </div>
@endif @endif
<div <div
class="scrollbar flex flex-col-reverse w-full overflow-y-auto border border-solid rounded border-coolgray-300 max-h-[32rem] p-4 text-xs text-white"> class="flex flex-col-reverse w-full px-4 py-2 overflow-y-auto text-white border border-solid rounded bg-coolgray-100 scrollbar border-coolgray-300 max-h-96">
<pre class="font-mono whitespace-pre-wrap" @if ($isPollingActive) wire:poll.1000ms="polling" @endif>{{ RunRemoteProcess::decodeOutput($this->activity) }}</pre> <pre class="font-mono whitespace-pre-wrap" @if ($isPollingActive) wire:poll.1000ms="polling" @endif>{{ RunRemoteProcess::decodeOutput($this->activity) }}</pre>
</div> </div>
@else
@if (isset($showWaiting))
<x-loading text="Waiting..." />
@endif
@endif @endif
</div> </div>

View File

@ -41,7 +41,7 @@
</a> </a>
<a class="mx-4 rounded group-hover:dark:text-white group-hover:text-black" <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-coollabs" viewBox="0 0 24 24" <svg xmlns="http://www.w3.org/2000/svg" class="icon hover:bg-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" />

View File

@ -17,7 +17,7 @@ class="text-xs text-white underline">OpenCollective</a>.<br><br></span>
</x-slot:button-text> </x-slot:button-text>
</x-popup> </x-popup>
{{-- <div class="toast"> {{-- <div class="toast">
<div class="flex flex-col text-white rounded alert bg-coolgray-200"> <div class="flex flex-col text-white rounded alert-error bg-coolgray-200">
<span>Love Coolify as we do? <a href="https://coolify.io/sponsorships" <span>Love Coolify as we do? <a href="https://coolify.io/sponsorships"
class="underline text-warning">Please class="underline text-warning">Please
consider donating!</a>💜</span> consider donating!</a>💜</span>
@ -40,7 +40,7 @@ class="text-white underline">/subscription</a> to update your subscription or re
@endif @endif
@if (!currentTeam()->isAnyNotificationEnabled()) @if (!currentTeam()->isAnyNotificationEnabled())
<div class="toast"> <div class="toast">
<div class="flex flex-col text-white rounded alert bg-coolgray-200"> <div class="flex flex-col text-white rounded alert-error bg-coolgray-200">
<span><span class="font-bold text-red-500">WARNING:</span> No notifications enabled.<br><br> It is <span><span class="font-bold text-red-500">WARNING:</span> No notifications enabled.<br><br> It is
highly recommended to enable at least highly recommended to enable at least
one one

View File

@ -1,32 +0,0 @@
<form x-data="{ raw: true }" class="custom-modal" wire:submit='submit'>
<div class="flex items-end gap-2">
<h1>Docker Compose</h1>
<div x-cloak x-show="raw">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
</div>
<div x-cloak x-show="raw === false">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
Compose</x-forms.button>
</div>
</div>
<div>Volume names are updated upon save. The service UUID will be added as a prefix to all volumes, to prevent
name collision. <br>To see the actual volume names, check the Deployable Compose file, or go to Storage
menu.</div>
<div x-cloak x-show="raw" class="font-mono">
<x-forms.textarea rows="20" id="service.docker_compose_raw">
</x-forms.textarea>
</div>
<div x-cloak x-show="raw === false" class="font-mono">
<x-forms.textarea rows="20" readonly id="service.docker_compose">
</x-forms.textarea>
</div>
<div class="flex justify-end w-full gap-2">
<x-forms.button class="w-64" type="submit">
Save
</x-forms.button>
<x-forms.button wire:click='closeModal'>
Close
</x-forms.button>
</div>
</form>

View File

@ -8,10 +8,15 @@
<livewire:project.database.backup-now :backup="$backup" /> <livewire:project.database.backup-now :backup="$backup" />
@endif @endif
@if ($backup->database_id !== 0) @if ($backup->database_id !== 0)
<x-forms.button isError wire:click="delete">Delete</x-forms.button> <x-modal-confirmation isErrorButton>
<x-slot:button-title>
Delete
</x-slot:button-title>
This will stop the scheduled backup for this database.<br>Please think again.
</x-modal-confirmation>
@endif @endif
</div> </div>
<div class="w-32 pb-2"> <div class="w-48 pb-2">
<x-forms.checkbox instantSave label="Backup Enabled" id="backup.enabled" /> <x-forms.checkbox instantSave label="Backup Enabled" id="backup.enabled" />
<x-forms.checkbox instantSave label="S3 Enabled" id="backup.save_s3" /> <x-forms.checkbox instantSave label="S3 Enabled" id="backup.save_s3" />
</div> </div>

View File

@ -1,14 +1,14 @@
<div class="flex flex-col-reverse gap-2"> <div class="flex flex-col-reverse gap-2">
@forelse($executions as $execution) @forelse($executions as $execution)
<form wire:key="{{ data_get($execution, 'id') }}" class="relative flex flex-col p-4 border-dotted border-1 bg-coolgray-100" <form wire:key="{{ data_get($execution, 'id') }}"
@class([ class="relative flex flex-col p-4 border-dotted border-1 bg-coolgray-100" @class([
'border-green-500' => data_get($execution, 'status') === 'success', 'border-green-500' => data_get($execution, 'status') === 'success',
'border-red-500' => data_get($execution, 'status') === 'failed', 'border-red-500' => data_get($execution, 'status') === 'failed',
])> ])>
@if (data_get($execution, 'status') === 'running') @if (data_get($execution, 'status') === 'running')
<div class="absolute top-2 right-2"> <div class="absolute top-2 right-2">
<x-loading /> <x-loading />
</div> </div>
@endif @endif
<div>Database: {{ data_get($execution, 'database_name', 'N/A') }}</div> <div>Database: {{ data_get($execution, 'database_name', 'N/A') }}</div>
<div>Status: {{ data_get($execution, 'status') }}</div> <div>Status: {{ data_get($execution, 'status') }}</div>
@ -26,8 +26,12 @@
<x-forms.button class=" hover:bg-coolgray-400" <x-forms.button class=" hover:bg-coolgray-400"
wire:click="download({{ data_get($execution, 'id') }})">Download</x-forms.button> wire:click="download({{ data_get($execution, 'id') }})">Download</x-forms.button>
@endif @endif
<x-forms.button isError wire:click="deleteBackup({{ data_get($execution, 'id') }})" <x-modal-confirmation isErrorButton action="deleteBackup({{ data_get($execution, 'id') }})">
wire:confirm.prompt="Are you sure?\n\nType DELETE to confirm|DELETE">Delete</x-forms.button> <x-slot:button-title>
Delete
</x-slot:button-title>
This will delete this backup. It is not reversible.<br>Please think again.
</x-modal-confirmation>
</div> </div>
</form> </form>
@empty @empty

View File

@ -1,6 +1,7 @@
<div> <div>
<h1>Backups</h1> <h1>Backups</h1>
<livewire:project.database.heading :database="$database" /> <livewire:project.database.heading :database="$database" />
<x-modal modalId="startDatabase"> <x-modal modalId="startDatabase">
<x-slot:modalBody> <x-slot:modalBody>
<livewire:activity-monitor header="Database Startup Logs" /> <livewire:activity-monitor header="Database Startup Logs" />
@ -11,11 +12,17 @@
</x-forms.button> </x-forms.button>
</x-slot:modalSubmit> </x-slot:modalSubmit>
</x-modal> </x-modal>
<livewire:project.database.create-scheduled-backup :database="$database" :s3s="$s3s" />
<div class="pt-6"> <div class="pt-6">
<div class="flex gap-2 "> <div class="flex gap-2 ">
<h2 class="pb-4">Scheduled Backups</h2> <h2 class="pb-4">Scheduled Backups</h2>
<x-forms.button onclick="createScheduledBackup.showModal()">+ Add</x-forms.button> <x-slide-over>
<x-slot:title>New Scheduled Backup</x-slot:title>
<x-slot:content>
<livewire:project.database.create-scheduled-backup :database="$database" :s3s="$s3s" />
</x-slot:content>
<button @click="slideOverOpen=true" class="button">+
Add</button>
</x-slide-over>
</div> </div>
<livewire:project.database.scheduled-backups :database="$database" /> <livewire:project.database.scheduled-backups :database="$database" />
</div> </div>

View File

@ -1,23 +1,16 @@
<dialog id="createScheduledBackup" class="modal"> <form class="flex flex-col gap-2 rounded" wire:submit='submit'>
<form method="dialog" class="flex flex-col gap-2 rounded modal-box" wire:submit='submit'> <x-forms.input placeholder="0 0 * * * or daily" id="frequency" label="Frequency" required />
<h2>New Backup</h2> <x-forms.checkbox id="save_s3" label="Save to S3" />
<x-forms.input placeholder="0 0 * * * or daily" id="frequency" label="Frequency" required /> <x-forms.select id="selected_storage_id">
<h3>S3 Storage</h3> @if ($s3s->count() === 0)
<x-forms.checkbox id="save_s3" label="Save to S3" /> <option value="0">No S3 Storages found.</option>
<x-forms.select id="selected_storage_id"> @else
@if ($s3s->count() === 0) @foreach ($s3s as $s3)
<option value="0">No S3 Storages found.</option> <option value="{{ $s3->id }}">{{ $s3->name }}</option>
@else @endforeach
@foreach ($s3s as $s3) @endif
<option value="{{ $s3->id }}">{{ $s3->name }}</option> </x-forms.select>
@endforeach <x-forms.button type="submit" @click="slideOverOpen=false">
@endif Save
</x-forms.select> </x-forms.button>
<x-forms.button onclick="createScheduledBackup.close()" type="submit"> </form>
Save
</x-forms.button>
</form>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>

View File

@ -1,4 +1,73 @@
<nav wire:poll.5000ms="check_status"> <nav wire:poll.5000ms="check_status">
<x-resources.breadcrumbs :resource="$database" :parameters="$parameters" /> <x-resources.breadcrumbs :resource="$database" :parameters="$parameters" />
<x-databases.navbar :database="$database" :parameters="$parameters" /> <x-slide-over @startdatabase.window="slideOverOpen = true" closeWithX fullScreen>
<x-slot:title>Database Startup Logs</x-slot:title>
<x-slot:content>
<livewire:activity-monitor showWaiting />
</x-slot:content>
</x-slide-over>
<div class="navbar-main">
<a class="{{ request()->routeIs('project.database.configuration') ? 'text-white' : '' }}"
href="{{ route('project.database.configuration', $parameters) }}">
<button>Configuration</button>
</a>
<a class="{{ request()->routeIs('project.database.command') ? 'text-white' : '' }}"
href="{{ route('project.database.command', $parameters) }}">
<button>Execute Command</button>
</a>
<a class="{{ request()->routeIs('project.database.logs') ? 'text-white' : '' }}"
href="{{ route('project.database.logs', $parameters) }}">
<button>Logs</button>
</a>
@if (
$database->getMorphClass() === 'App\Models\StandalonePostgresql' ||
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
<a class="{{ request()->routeIs('project.database.backup.index') ? 'text-white' : '' }}"
href="{{ route('project.database.backup.index', $parameters) }}">
<button>Backups</button>
</a>
@endif
<div class="flex-1"></div>
@if (!str($database->status)->startsWith('exited'))
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
This database will be stopped. <br>Please think again.
</x-modal-confirmation>
@else
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" 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" />
<path d="M7 4v16l13 -8z" />
</svg>
Start
</button>
@endif
@script
<script>
$wire.$on('startEvent', () => {
window.dispatchEvent(new CustomEvent('startdatabase'));
$wire.$call('start');
});
$wire.$on('stopEvent', () => {
$wire.$dispatch('warning', 'Stopping database.');
$wire.$call('stop');
});
</script>
@endscript
</div>
</nav> </nav>

View File

@ -1,6 +1,6 @@
<div> <div>
<h2>Import Backup</h2> <h2>Import Backup</h2>
<div class="mt-2 mb-4 rounded alert"> <div class="mt-2 mb-4 rounded alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none" viewBox="0 0 24 24"> <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" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /> d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
@ -55,6 +55,6 @@ class="progress progress-warning"></progress>
<livewire:activity-monitor header="Database import output" /> <livewire:activity-monitor header="Database import output" />
</div> </div>
@else @else
<div class="text-neutral-500">Database must be running to import a backup.</div> <div>Database must be running to import a backup.</div>
@endif @endif
</div> </div>

View File

@ -1,7 +1,7 @@
<div> <div>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
@forelse($database->scheduledBackups as $backup) @forelse($database->scheduledBackups as $backup)
@if ($type === 'database') @if ($type == 'database')
<a class="flex flex-col box" <a class="flex flex-col box"
href="{{ route('project.database.backup.execution', [...$parameters, 'backup_uuid' => $backup->uuid]) }}"> href="{{ route('project.database.backup.execution', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
<div>Frequency: {{ $backup->frequency }}</div> <div>Frequency: {{ $backup->frequency }}</div>
@ -25,10 +25,10 @@
</div> </div>
@if ($type === 'service-database' && $selectedBackup) @if ($type === 'service-database' && $selectedBackup)
<div class="pt-10"> <div class="pt-10">
<livewire:project.database.backup-edit key="{{ $selectedBackup->id }}" :backup="$selectedBackup" :s3s="$s3s" <livewire:project.database.backup-edit wire:key="{{ $selectedBackup->id }}" :backup="$selectedBackup" :s3s="$s3s"
:status="data_get($database, 'status')" /> :status="data_get($database, 'status')" />
<h3 class="py-4">Executions</h3> <h3 class="py-4">Executions</h3>
<livewire:project.database.backup-executions key="{{ $selectedBackup->id }}" :backup="$selectedBackup" <livewire:project.database.backup-executions wire:keykey="{{ $selectedBackup->id }}" :backup="$selectedBackup"
:executions="$selectedBackup->executions" /> :executions="$selectedBackup->executions" />
</div> </div>
@endif @endif

View File

@ -33,7 +33,7 @@ class="text-warning">@{{ project.VARIABLENAME }}</span><x-helper
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}" <livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
:env="$env" type="project" /> :env="$env" type="project" />
@empty @empty
<div class="text-neutral-500">No environment variables found.</div> <div>No environment variables found.</div>
@endforelse @endforelse
</div> </div>
</div> </div>

View File

@ -62,7 +62,7 @@ class="button">+
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}" <livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
:env="$env" type="environment" /> :env="$env" type="environment" />
@empty @empty
<div class="text-neutral-500">No environment variables found.</div> <div>No environment variables found.</div>
@endforelse @endforelse
</div> </div>
</div> </div>

View File

@ -34,7 +34,7 @@ class="loading loading-xs text-warning loading-spinner"></span>
@endif @endif
@empty @empty
<div class="flex flex-col items-center justify-center gap-2"> <div class="flex flex-col items-center justify-center gap-2">
<div class="text-neutral-500"> <div>
No private keys found. No private keys found.
</div> </div>
<a href="{{ route('security.private-key.index') }}"> <a href="{{ route('security.private-key.index') }}">

View File

@ -28,12 +28,12 @@
</div> </div>
</div> </div>
<h3 class="pt-2">Advanced</h3> <h3 class="pt-2">Advanced</h3>
<div class="w-64"> <div class="w-96">
<x-forms.checkbox instantSave label="Exclude from service status" <x-forms.checkbox instantSave="instantSaveExclude" label="Exclude from service status"
helper="If you do not need to monitor this resource, enable. Useful if this service is optional." helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
id="database.exclude_from_status"></x-forms.checkbox> id="database.exclude_from_status"></x-forms.checkbox>
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings." <x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" /> instantSave="instantSaveLogDrain" id="database.is_log_drain_enabled" label="Drain Logs" />
</div> </div>
</form> </form>
</div> </div>

View File

@ -0,0 +1,29 @@
<div x-data="{ raw: true }">
<div class="pb-4">Volume names are updated upon save. The service UUID will be added as a prefix to all volumes, to
prevent
name collision. <br>To see the actual volume names, check the Deployable Compose file, or go to Storage
menu.</div>
<div x-cloak x-show="raw" class="font-mono">
<x-forms.textarea rows="20" id="service.docker_compose_raw">
</x-forms.textarea>
</div>
<div x-cloak x-show="raw === false" class="font-mono">
<x-forms.textarea rows="20" readonly id="service.docker_compose">
</x-forms.textarea>
</div>
<div class="flex justify-end w-full gap-2 pt-4">
<div class="flex items-end gap-2">
<div x-cloak x-show="raw">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
</div>
<div x-cloak x-show="raw === false">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
Compose</x-forms.button>
</div>
</div>
<x-forms.button class="w-64" wire:click.prevent='saveEditedCompose'>
Save
</x-forms.button>
</div>
</div>

View File

@ -55,9 +55,15 @@
<div x-cloak x-show="activeTab === 'backups'"> <div x-cloak x-show="activeTab === 'backups'">
<div class="flex gap-2 "> <div class="flex gap-2 ">
<h2 class="pb-4">Scheduled Backups</h2> <h2 class="pb-4">Scheduled Backups</h2>
<x-forms.button onclick="createScheduledBackup.showModal()">+ Add</x-forms.button> <x-slide-over>
<x-slot:title>New Scheduled Backup</x-slot:title>
<x-slot:content>
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" :s3s="$s3s" />
</x-slot:content>
<button @click="slideOverOpen=true" class="button">+
Add</button>
</x-slide-over>
</div> </div>
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" :s3s="$s3s" />
<livewire:project.database.scheduled-backups :database="$serviceDatabase" /> <livewire:project.database.scheduled-backups :database="$serviceDatabase" />
</div> </div>
@endisset @endisset

View File

@ -1,12 +0,0 @@
<div>
<x-modal noSubmit modalId="startService">
<x-slot:modalBody>
<livewire:activity-monitor header="Service Startup Logs" />
</x-slot:modalBody>
<x-slot:modalSubmit>
<x-forms.button onclick="startService.close()" type="submit">
Close
</x-forms.button>
</x-slot:modalSubmit>
</x-modal>
</div>

View File

@ -1,6 +1,135 @@
<div> <div>
<livewire:project.service.modal /> <x-slide-over @startservice.window="slideOverOpen = true" closeWithX fullScreen>
<x-slot:title>Service Startup</x-slot:title>
<x-slot:content>
<livewire:activity-monitor header="Logs" showWaiting />
</x-slot:content>
</x-slide-over>
<h1>Configuration</h1> <h1>Configuration</h1>
<x-resources.breadcrumbs :resource="$service" :parameters="$parameters" /> <x-resources.breadcrumbs :resource="$service" :parameters="$parameters" />
<x-services.navbar :service="$service" :parameters="$parameters" /> <div class="navbar-main" x-data>
<a class="{{ request()->routeIs('project.service.configuration') ? 'text-white' : '' }}"
href="{{ route('project.service.configuration', $parameters) }}">
<button>Configuration</button>
</a>
<x-services.links :service="$service" />
<div class="flex-1"></div>
@if (str($service->status())->contains('running'))
<button @click="$wire.dispatch('restartEvent')" class="gap-2 button">
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</g>
</svg>
Pull Latest Images & Restart
</button>
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
This service will be stopped. <br>Please think again.
</x-modal-confirmation>
@elseif (str($service->status())->contains('degraded'))
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</g>
</svg>
Restart Degraded Services
</button>
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
This service will be stopped. <br>Please think again.
</x-modal-confirmation>
@elseif (str($service->status())->contains('exited'))
<button wire:click='stop(true)' class="gap-2 button">
<svg class="w-5 h-5 " viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
<path fill="red"
d="M17.003 20a4.895 4.895 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.699 5.699 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2Zm-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848ZM15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.617 16.617 0 0 1 10 24H8a17.342 17.342 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13.025 13.025 0 0 0 17.596 28Z" />
</svg>
Force Cleanup Containers
</button>
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" 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" />
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
</button>
@else
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
This service will be stopped. <br>Please think again.
</x-modal-confirmation>
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" 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" />
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
</button>
@endif
</div>
@script
<script>
$wire.$on('stopEvent', () => {
$wire.$dispatch('warning', 'Stopping service.');
$wire.$call('stop');
});
$wire.$on('startEvent', () => {
window.dispatchEvent(new CustomEvent('startservice'));
$wire.$call('start');
});
$wire.$on('restartEvent', () => {
$wire.$dispatch('warning', 'Pulling new images.');
$wire.$call('restart');
});
$wire.on('imagePulled', () => {
window.dispatchEvent(new CustomEvent('startservice'));
$wire.$dispatch('warning', 'Restarting service.');
});
</script>
@endscript
</div> </div>

View File

@ -7,7 +7,12 @@
<h2>{{ Str::headline($application->name) }}</h2> <h2>{{ Str::headline($application->name) }}</h2>
@endif @endif
<x-forms.button type="submit">Save</x-forms.button> <x-forms.button type="submit">Save</x-forms.button>
<x-forms.button isError wire:click='delete'>Delete</x-forms.button> <x-modal-confirmation isErrorButton>
<x-slot:button-title>
Delete
</x-slot:button-title>
This will delete this service application. It is not reversible.<br>Please think again.
</x-modal-confirmation>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex gap-2"> <div class="flex gap-2">

View File

@ -1,14 +1,17 @@
<form wire:submit='submit' class="flex flex-col gap-4 pb-2"> <form wire:submit.prevent='submit' class="flex flex-col gap-4 pb-2">
<div class="flex gap-2"> <div>
<div> <div class="flex gap-2">
<h2>Service Stack</h2> <h2>Service Stack</h2>
<div>Configuration</div> <x-forms.button type="submit">Save</x-forms.button>
<x-slide-over closeWithX fullScreen>
<x-slot:title>Docker Compose</x-slot:title>
<x-slot:content>
<livewire:project.service.edit-compose serviceId="{{ $service->id }}" />
</x-slot:content>
<button @click.prevent="slideOverOpen=true" class="button">Edit Compose File</button>
</x-slide-over>
</div> </div>
<x-forms.button type="submit">Save</x-forms.button> <div>Configuration</div>
<x-forms.button class="w-64"
onclick="Livewire.dispatch('openModal', {component: 'modal.edit-compose', arguments: {{ json_encode(['serviceId' => $service->id]) }} })">Edit
Compose
File</x-forms.button>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input id="service.name" required label="Service Name" placeholder="My super wordpress site" /> <x-forms.input id="service.name" required label="Service Name" placeholder="My super wordpress site" />

View File

@ -84,7 +84,7 @@ class="relative flex flex-col text-white cursor-default box w-96">
@endforeach @endforeach
</div> </div>
@else @else
<div class="text-neutral-500">No additional servers available to attach.</div> <div>No additional servers available to attach.</div>
@endif @endif
@endif @endif
</div> </div>

View File

@ -24,7 +24,7 @@
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}" <livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
:env="$env" :type="$resource->type()" /> :env="$env" :type="$resource->type()" />
@empty @empty
<div class="text-neutral-500">No environment variables found.</div> <div>No environment variables found.</div>
@endforelse @endforelse
@if ($resource->type() === 'application' && $resource->environment_variables_preview->count() > 0 && $showPreview) @if ($resource->type() === 'application' && $resource->environment_variables_preview->count() > 0 && $showPreview)
<div> <div>

View File

@ -11,10 +11,10 @@
<div>({{ $pull_request }})</div> <div>({{ $pull_request }})</div>
@endif @endif
@if ($streamLogs) @if ($streamLogs)
<span wire:poll.2000ms='getLogs(true)' class="loading loading-xs text-warning loading-spinner"></span> <x-loading wire:poll.2000ms='getLogs(true)' />
@endif @endif
</div> </div>
<form wire:submit='getLogs(true)' class="flex items-end gap-2 pt-2 "> <form wire:submit='getLogs(true)' class="flex items-end gap-2 ">
<div class="w-96"> <div class="w-96">
<x-forms.input label="Only Show Number of Lines" placeholder="1000" required <x-forms.input label="Only Show Number of Lines" placeholder="1000" required
id="numberOfLines"></x-forms.input> id="numberOfLines"></x-forms.input>

View File

@ -22,7 +22,7 @@
<div>Last run: {{ data_get($task->latest_log, 'status', 'No runs yet') }}</div> <div>Last run: {{ data_get($task->latest_log, 'status', 'No runs yet') }}</div>
</a> </a>
@empty @empty
<div class="text-neutral-500">No scheduled tasks configured.</div> <div>No scheduled tasks configured.</div>
@endforelse @endforelse
</div> </div>
</div> </div>

View File

@ -34,7 +34,7 @@
</script> </script>
@endscript @endscript
<div class="toast z-[9999]" x-cloak x-show="showNotification"> <div class="toast z-[9999]" x-cloak x-show="showNotification">
<div class="flex flex-col text-white border border-red-500 border-dashed rounded alert bg-coolgray-200"> <div class="flex flex-col text-white border border-red-500 border-dashed rounded alert-error bg-coolgray-200">
<span><span class="font-bold text-left text-red-500">WARNING: </span>Coolify could not connect to the new <span><span class="font-bold text-left text-red-500">WARNING: </span>Coolify could not connect to the new
realtime service introduced in beta.154. <br>This will cause unusual problems on the UI if not realtime service introduced in beta.154. <br>This will cause unusual problems on the UI if not
fixed!<br><br>Please check the fixed!<br><br>Please check the

View File

@ -24,7 +24,12 @@
class="flex items-center gap-2 group-hover:text-white p-2 border border-coolgray-200 hover:text-white hover:no-underline min-w-[24rem] cursor-default"> class="flex items-center gap-2 group-hover:text-white p-2 border border-coolgray-200 hover:text-white hover:no-underline min-w-[24rem] cursor-default">
<div>{{ $token->name }}</div> <div>{{ $token->name }}</div>
</div> </div>
<x-forms.button wire:click="revoke('{{ $token->id }}')">Revoke</x-forms.button> <x-modal-confirmation isErrorButton action="revoke({{ data_get($token, 'id') }})">
<x-slot:button-title>
Revoke token
</x-slot:button-title>
This API Token will be deleted and anything using it will fail. <br>Please think again.
</x-modal-confirmation>
</div> </div>
@empty @empty
<div> <div>

View File

@ -1,22 +1,11 @@
<div> <div>
<x-modal yesOrNo modalId="stopProxy" modalTitle="Stop Proxy" action="stop">
<x-slot:modalBody>
<p>This proxy will be stopped. It is not reversible. <br>All resources will be unavailable.
<br>Please think
again.
</p>
</x-slot:modalBody>
</x-modal>
<x-modal yesOrNo modalId="restartProxy" modalTitle="Restart Proxy" action="restart">
<x-slot:modalBody>
<p>This proxy will be stopped and started. It is not reversible. <br>All resources will be unavailable
during the restart.
<br>Please think
again.
</p>
</x-slot:modalBody>
</x-modal>
@if ($server->isFunctional() && $server->proxyType() !== 'NONE') @if ($server->isFunctional() && $server->proxyType() !== 'NONE')
<x-slide-over closeWithX fullScreen @startproxy.window="slideOverOpen = true">
<x-slot:title>Proxy Status</x-slot:title>
<x-slot:content>
<livewire:activity-monitor header="Logs" />
</x-slot:content>
</x-slide-over>
@if (data_get($server, 'proxy.status') === 'running') @if (data_get($server, 'proxy.status') === 'running')
<div class="flex gap-4"> <div class="flex gap-4">
@if ($currentRoute === 'server.proxy' && $traefikDashboardAvailable && $server->proxyType() === 'TRAEFIK_V2') @if ($currentRoute === 'server.proxy' && $traefikDashboardAvailable && $server->proxyType() === 'TRAEFIK_V2')
@ -27,32 +16,39 @@
</a> </a>
</button> </button>
@endif @endif
<x-forms.button isModal noStyle modalId="restartProxy" <x-modal-confirmation @click="$wire.dispatch('restartEvent')">
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400"> <x-slot:button-title>
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"> stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" /> <path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" /> <path d="M20 4v5h-5" />
</g> </g>
</svg> </svg>
Restart Proxy Restart Proxy
</x-forms.button> </x-slot:button-title>
<x-forms.button isModal noStyle modalId="stopProxy" This proxy will be stopped and started. It is not reversible. <br>All resources will be unavailable
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400"> during the restart. <br>Please think again.
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" </x-modal-confirmation>
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" <x-modal-confirmation @click="$wire.dispatch('stopEvent')">
stroke-linejoin="round"> <x-slot:button-title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path> stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path> stroke-linejoin="round">
</svg> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
Stop Proxy <path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</x-forms.button> </path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop Proxy
</x-slot:button-title>
This proxy will be stopped. It is not reversible. <br>All resources will be unavailable.
<br>Please think again.
</x-modal-confirmation>
</div> </div>
@else @else
<button x-on:click="$wire.dispatch('checkProxy')" <button @click="$wire.dispatch('checkProxy')" class="gap-2 button">
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24" <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" 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">
@ -63,10 +59,21 @@ class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400"
</button> </button>
@endif @endif
@endif @endif
<script> @script
Livewire.on('proxyChecked', () => { <script>
startProxy.showModal(); $wire.$on('restartEvent', () => {
window.Livewire.dispatch('startProxy'); $wire.$dispatch('warning', 'Restarting proxy.');
}) $wire.$call('restart');
</script> });
$wire.$on('proxyChecked', () => {
window.dispatchEvent(new CustomEvent('startproxy'))
$wire.$call('startProxy');
});
$wire.$on('stopEvent', () => {
$wire.$dispatch('warning', 'Stopping proxy.');
$wire.$call('stop');
});
</script>
@endscript
</div> </div>

View File

@ -26,7 +26,7 @@
<div class="overflow-hidden"> <div class="overflow-hidden">
<table class="min-w-full divide-y divide-coolgray-400"> <table class="min-w-full divide-y divide-coolgray-400">
<thead> <thead>
<tr class="text-neutral-500"> <tr>
<th class="px-5 py-3 text-xs font-medium text-left uppercase"> <th class="px-5 py-3 text-xs font-medium text-left uppercase">
Project Project
</th> </th>
@ -98,7 +98,7 @@ class=""
<div class="overflow-hidden"> <div class="overflow-hidden">
<table class="min-w-full divide-y divide-coolgray-400"> <table class="min-w-full divide-y divide-coolgray-400">
<thead> <thead>
<tr class="text-neutral-500"> <tr>
<th class="px-5 py-3 text-xs font-medium text-left uppercase"> <th class="px-5 py-3 text-xs font-medium text-left uppercase">
Name Name
</th> </th>
@ -125,7 +125,7 @@ class=""
<td class="px-5 py-4 text-sm whitespace-nowrap"> <td class="px-5 py-4 text-sm whitespace-nowrap">
{{ data_get($resource, 'State') }} {{ data_get($resource, 'State') }}
</td> </td>
<td class="px-5 py-4 text-sm whitespace-nowrap"> <td class="flex gap-2 px-5 py-4 text-sm whitespace-nowrap">
@if (data_get($resource, 'State') === 'running') @if (data_get($resource, 'State') === 'running')
<x-forms.button <x-forms.button
wire:click="restartUnmanaged('{{ data_get($resource, 'ID') }}')" wire:click="restartUnmanaged('{{ data_get($resource, 'ID') }}')"

View File

@ -108,7 +108,7 @@
<div class="overflow-hidden"> <div class="overflow-hidden">
<table class="min-w-full divide-y divide-coolgray-400"> <table class="min-w-full divide-y divide-coolgray-400">
<thead> <thead>
<tr class="text-neutral-500"> <tr>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Project <th class="px-5 py-3 text-xs font-medium text-left uppercase">Project
</th> </th>
<th class="px-5 py-3 text-xs font-medium text-left uppercase"> <th class="px-5 py-3 text-xs font-medium text-left uppercase">

View File

@ -1,11 +1,9 @@
<div> <div>
<form wire:submit='createGitHubApp' class="flex flex-col"> <form wire:submit='createGitHubApp' class="flex flex-col gap-2">
<h2>GitHub App</h2>
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input id="name" label="Name" required /> <x-forms.input id="name" label="Name" required />
<x-forms.input helper="If empty, your GitHub user will be used." id="organization" label="Organization" /> <x-forms.input helper="If empty, your GitHub user will be used." id="organization" label="Organization" />
</div> </div>
<h3 class="py-4">Advanced</h3>
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input id="html_url" label="HTML Url" required /> <x-forms.input id="html_url" label="HTML Url" required />
<x-forms.input id="api_url" label="API Url" required /> <x-forms.input id="api_url" label="API Url" required />
@ -15,7 +13,7 @@
<x-forms.input id="custom_port" type="number" label="Custom Git Port" required /> <x-forms.input id="custom_port" type="number" label="Custom Git Port" required />
</div> </div>
@if (!isCloud()) @if (!isCloud())
<x-forms.checkbox class="pt-2" id="is_system_wide" label="System Wide" /> <x-forms.checkbox id="is_system_wide" label="System Wide" />
@endif @endif
<x-forms.button class="mt-4" type="submit"> <x-forms.button class="mt-4" type="submit">
Continue Continue

View File

@ -14,7 +14,7 @@
class="text-warning">{{ session('currentTeam.name') }}</span></span> class="text-warning">{{ session('currentTeam.name') }}</span></span>
</div> </div>
@if (request()->query->get('cancelled')) @if (request()->query->get('cancelled'))
<div class="mb-6 rounded alert"> <div class="mb-6 rounded alert-error">
<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"

View File

@ -21,7 +21,7 @@ class="text-warning">@{{ team.VARIABLENAME }}</span> <x-helper
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}" <livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
:env="$env" type="team" /> :env="$env" type="team" />
@empty @empty
<div class="text-neutral-500">No environment variables found.</div> <div>No environment variables found.</div>
@endforelse @endforelse
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@
<div class="overflow-hidden"> <div class="overflow-hidden">
<table class="min-w-full divide-y divide-coolgray-400"> <table class="min-w-full divide-y divide-coolgray-400">
<thead> <thead>
<tr class="text-neutral-500"> <tr>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Email <th class="px-5 py-3 text-xs font-medium text-left uppercase">Email
</th> </th>
<th class="px-5 py-3 text-xs font-medium text-left uppercase"> <th class="px-5 py-3 text-xs font-medium text-left uppercase">

View File

@ -41,7 +41,7 @@
@endif @endif
@endif @endif
@else @else
<div class="text-neutral-500">(This is you)</div> <div>(This is you)</div>
@endif @endif
@endif @endif
</td> </td>

View File

@ -9,7 +9,7 @@
<div class="overflow-hidden"> <div class="overflow-hidden">
<table class="min-w-full divide-y divide-coolgray-400"> <table class="min-w-full divide-y divide-coolgray-400">
<thead> <thead>
<tr class="text-neutral-500"> <tr>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Name <th class="px-5 py-3 text-xs font-medium text-left uppercase">Name
</th> </th>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Email</th> <th class="px-5 py-3 text-xs font-medium text-left uppercase">Email</th>

View File

@ -1,15 +1,20 @@
<x-layout> <x-layout>
<div class="flex items-start gap-2"> <div class="flex items-start gap-2">
<h1>Sources</h1> <h1>Sources</h1>
<a class="text-white hover:no-underline" href="{{ route('source.new') }}"> <x-slide-over fullScreen closeWithX>
<x-forms.button >+ Add</x-forms.button> <x-slot:title>New GitHub App</x-slot:title>
</a> <x-slot:content>
<livewire:source.github.create />
</x-slot:content>
<button @click="slideOverOpen=true" class="button">+
Add</button>
</x-slide-over>
</div> </div>
<div class="subtitle ">All Sources.</div> <div class="subtitle ">All Sources.</div>
<div class="grid gap-2 lg:grid-cols-2"> <div class="grid gap-2 lg:grid-cols-2">
@forelse ($sources as $source) @forelse ($sources as $source)
@if ($source->getMorphClass() === 'App\Models\GithubApp') @if ($source->getMorphClass() === 'App\Models\GithubApp')
<a class="flex gap-4 text-center hover:no-underline box group" <a class="flex gap-4 text-center hover:no-underline box group"
href="{{ route('source.github.show', ['github_app_uuid' => data_get($source, 'uuid')]) }}"> href="{{ route('source.github.show', ['github_app_uuid' => data_get($source, 'uuid')]) }}">
<x-git-icon class="text-white w-9 h-9" git="{{ $source->getMorphClass() }}" /> <x-git-icon class="text-white w-9 h-9" git="{{ $source->getMorphClass() }}" />
<div class="text-left group-hover:text-white"> <div class="text-left group-hover:text-white">
@ -21,7 +26,7 @@
</a> </a>
@endif @endif
@if ($source->getMorphClass() === 'App\Models\GitlabApp') @if ($source->getMorphClass() === 'App\Models\GitlabApp')
<a class="flex gap-4 text-center hover:no-underline box group" <a class="flex gap-4 text-center hover:no-underline box group"
href="{{ route('source.gitlab.show', ['gitlab_app_uuid' => data_get($source, 'uuid')]) }}"> href="{{ route('source.gitlab.show', ['gitlab_app_uuid' => data_get($source, 'uuid')]) }}">
<x-git-icon class="text-white w-9 h-9" git="{{ $source->getMorphClass() }}" /> <x-git-icon class="text-white w-9 h-9" git="{{ $source->getMorphClass() }}" />
<div class="text-left group-hover:text-white"> <div class="text-left group-hover:text-white">