ui: fixes

This commit is contained in:
Andras Bacsai
2022-09-19 14:05:25 +02:00
parent 1e1566082f
commit c17064f853
9 changed files with 178 additions and 151 deletions

View File

@@ -4,7 +4,7 @@
export let type = 'info'; export let type = 'info';
function success() { function success() {
if (type === 'success') { if (type === 'success') {
return 'bg-gradient-to-r from-coollabs to-pink-500'; return 'bg-gradient-to-r from-purple-500 via-pink-500 to-red-500';
} }
} }
</script> </script>
@@ -15,7 +15,7 @@
on:focus={() => dispatch('pause')} on:focus={() => dispatch('pause')}
on:mouseout={() => dispatch('resume')} on:mouseout={() => dispatch('resume')}
on:blur={() => dispatch('resume')} on:blur={() => dispatch('resume')}
class={`alert shadow-lg text-white hover:scale-105 transition-all duration-100 cursor-pointer rounded ${success()}`} class={`flex flex-row alert shadow-lg text-white hover:scale-105 transition-all duration-100 cursor-pointer rounded ${success()}`}
class:alert-error={type === 'error'} class:alert-error={type === 'error'}
class:alert-info={type === 'info'} class:alert-info={type === 'info'}
> >

View File

@@ -1,12 +1,11 @@
<script lang="ts"> <script lang="ts">
import { dev } from '$app/env'; import { dev } from '$app/env';
import { get, post } from '$lib/api'; import { get, post } from '$lib/api';
import { addToast, appSession, features } from '$lib/store'; import { addToast, appSession, features, updateLoading, isUpdateAvailable } from '$lib/store';
import { asyncSleep, errorNotification } from '$lib/common'; import { asyncSleep, errorNotification } from '$lib/common';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Tooltip from './Tooltip.svelte'; import Tooltip from './Tooltip.svelte';
let isUpdateAvailable = false;
let updateStatus: any = { let updateStatus: any = {
found: false, found: false,
loading: false, loading: false,
@@ -58,37 +57,41 @@
if ($appSession.userId) { if ($appSession.userId) {
const overrideVersion = $features.latestVersion; const overrideVersion = $features.latestVersion;
if ($appSession.teamId === '0') { if ($appSession.teamId === '0') {
if ($updateLoading === true) return;
try { try {
$updateLoading = true;
const data = await get(`/update`); const data = await get(`/update`);
if (overrideVersion || data?.isUpdateAvailable) { if (overrideVersion || data?.isUpdateAvailable) {
latestVersion = overrideVersion || data.latestVersion; latestVersion = overrideVersion || data.latestVersion;
if (overrideVersion) { if (overrideVersion) {
isUpdateAvailable = true; $isUpdateAvailable = true;
} else { } else {
isUpdateAvailable = data.isUpdateAvailable; $isUpdateAvailable = data.isUpdateAvailable;
} }
} }
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
} finally {
$updateLoading = false;
} }
} }
} }
}); });
</script> </script>
<div class="py-2"> <div class="py-0 lg:py-2">
{#if $appSession.teamId === '0'} {#if $appSession.teamId === '0'}
{#if isUpdateAvailable} {#if $isUpdateAvailable}
<button <button
id="update" id="update"
disabled={updateStatus.success === false} disabled={updateStatus.success === false}
on:click={update} on:click={update}
class="icons bg-gradient-to-r from-coollabs to-pink-500 text-white duration-75 hover:scale-105" class="icons bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white duration-75 hover:scale-105 w-full"
> >
{#if updateStatus.loading} {#if updateStatus.loading}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="lds-heart h-9 w-8" class="lds-heart h-8 w-8"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="1.5" stroke-width="1.5"
stroke="currentColor" stroke="currentColor"
@@ -102,24 +105,27 @@
/> />
</svg> </svg>
{:else if updateStatus.success === null} {:else if updateStatus.success === null}
<svg <div class="flex items-center justify-center space-x-2">
xmlns="http://www.w3.org/2000/svg" <svg
class="h-9 w-8" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" class="h-8 w-8"
stroke-width="1.5" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="1.5"
fill="none" stroke="currentColor"
stroke-linecap="round" fill="none"
stroke-linejoin="round" stroke-linecap="round"
> stroke-linejoin="round"
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> >
<circle cx="12" cy="12" r="9" /> <path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="12" y1="8" x2="8" y2="12" /> <circle cx="12" cy="12" r="9" />
<line x1="12" y1="8" x2="12" y2="16" /> <line x1="12" y1="8" x2="8" y2="12" />
<line x1="16" y1="12" x2="12" y2="8" /> <line x1="12" y1="8" x2="12" y2="16" />
</svg> <line x1="16" y1="12" x2="12" y2="8" />
</svg>
<span class="flex lg:hidden">Update available</span>
</div>
{:else if updateStatus.success} {:else if updateStatus.success}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="h-9 w-8" <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="h-8 w-8"
><path ><path
fill="#DD2E44" fill="#DD2E44"
d="M11.626 7.488c-.112.112-.197.247-.268.395l-.008-.008L.134 33.141l.011.011c-.208.403.14 1.223.853 1.937.713.713 1.533 1.061 1.936.853l.01.01L28.21 24.735l-.008-.009c.147-.07.282-.155.395-.269 1.562-1.562-.971-6.627-5.656-11.313-4.687-4.686-9.752-7.218-11.315-5.656z" d="M11.626 7.488c-.112.112-.197.247-.268.395l-.008-.008L.134 33.141l.011.011c-.208.403.14 1.223.853 1.937.713.713 1.533 1.061 1.936.853l.01.01L28.21 24.735l-.008-.009c.147-.07.282-.155.395-.269 1.562-1.562-.971-6.627-5.656-11.313-4.687-4.686-9.752-7.218-11.315-5.656z"
@@ -184,7 +190,9 @@
> >
{/if} {/if}
</button> </button>
<Tooltip triggeredBy="#update" placement="right" color="bg-gradient-to-r from-purple-500 via-pink-500 to-red-500">New Version Available!</Tooltip> <Tooltip triggeredBy="#update" placement="right" color="bg-coolgray-200 text-white"
>New Version Available!</Tooltip
>
{/if} {/if}
{/if} {/if}
</div> </div>

View File

@@ -26,7 +26,8 @@ interface AddToast {
message: string, message: string,
timeout?: number | undefined timeout?: number | undefined
} }
export const updateLoading: Writable<boolean> = writable(false);
export const isUpdateAvailable: Writable<boolean> = writable(false);
export const search: any = writable('') export const search: any = writable('')
export const loginEmail: Writable<string | undefined> = writable() export const loginEmail: Writable<string | undefined> = writable()
export const appSession: Writable<AppSession> = writable({ export const appSession: Writable<AppSession> = writable({

View File

@@ -195,8 +195,9 @@
<Tooltip triggeredBy="#dashboard" placement="right">Dashboard</Tooltip> <Tooltip triggeredBy="#dashboard" placement="right">Dashboard</Tooltip>
<Tooltip triggeredBy="#servers" placement="right">Servers</Tooltip> <Tooltip triggeredBy="#servers" placement="right">Servers</Tooltip>
<div class="flex-1" /> <div class="flex-1" />
<div class="lg:block hidden">
<UpdateAvailable /> <UpdateAvailable />
</div>
<div class="flex flex-col space-y-2 py-2"> <div class="flex flex-col space-y-2 py-2">
<a <a
id="iam" id="iam"
@@ -301,7 +302,6 @@
{#if !$appSession.whiteLabeled} {#if !$appSession.whiteLabeled}
<h3 class="mb-0 text-white">Coolify</h3> <h3 class="mb-0 text-white">Coolify</h3>
{/if} {/if}
<UpdateAvailable />
</div> </div>
</div> </div>
<main> <main>
@@ -416,6 +416,10 @@
Settings Settings
</a> </a>
</li> </li>
<li class="flex-1 bg-transparent" />
<div class="block lg:hidden">
<UpdateAvailable />
</div>
<li> <li>
<div class="no-underline icons hover:bg-error" on:click={logout}> <div class="no-underline icons hover:bg-error" on:click={logout}>
<svg <svg
@@ -437,12 +441,11 @@
<div class="-ml-1">Logout</div> <div class="-ml-1">Logout</div>
</div> </div>
</li> </li>
<li class="flex-1 bg-transparent" /> <li class="w-full">
<li class="w-full justify-center">
<a <a
class="text-xs hover:bg-coolgray-200 no-underline hover:text-white" class="text-xs hover:bg-coolgray-200 no-underline hover:text-white text-right"
href={`https://github.com/coollabsio/coolify/releases/tag/v${$appSession.version}`} href={`https://github.com/coollabsio/coolify/releases/tag/v${$appSession.version}`}
target="_blank">Changelog: v{$appSession.version}</a target="_blank">v{$appSession.version}</a
> >
</li> </li>
</ul> </ul>

View File

@@ -260,7 +260,7 @@
{/if} {/if}
</div> </div>
<div class="lg:block hidden flex-1" /> <div class="lg:block hidden flex-1" />
<div class="flex flex-row flex-wrap space-x-3 justify-center lg:justify-start lg:py-0"> <div class="flex flex-row flex-wrap space-x-3 justify-center lg:justify-start lg:py-0 px-12 lg:px-0">
{#if $location} {#if $location}
<a id="open" href={$location} target="_blank" class="icons bg-transparent" <a id="open" href={$location} target="_blank" class="icons bg-transparent"
><svg ><svg

View File

@@ -48,6 +48,7 @@
import Tooltip from '$lib/components/Tooltip.svelte'; import Tooltip from '$lib/components/Tooltip.svelte';
import Explainer from '$lib/components/Explainer.svelte'; import Explainer from '$lib/components/Explainer.svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { fade } from 'svelte/transition';
const { id } = $page.params; const { id } = $page.params;
@@ -295,7 +296,7 @@
</script> </script>
{#if $status.application.isRunning} {#if $status.application.isRunning}
<div class="mx-auto max-w-6xl px-6 lg:my-0 my-4 lg:pt-0 pt-4 rounded"> <div class="mx-auto max-w-6xl px-6 lg:my-0 my-4 lg:pt-0 pt-4 rounded" in:fade>
<div class="text-center"> <div class="text-center">
<div class="stat w-64"> <div class="stat w-64">
<div class="stat-title">Used Memory / Memory Limit</div> <div class="stat-title">Used Memory / Memory Limit</div>

View File

@@ -32,7 +32,7 @@
<div class="header"> <div class="header">
<h1 class="text-2xl font-bold">Servers</h1> <h1 class="text-2xl font-bold">Servers</h1>
</div> </div>
<div class="container lg:mx-auto lg:p-0 px-8 p-5 lg:pt-20"> <div class="container lg:mx-auto lg:p-0 px-8 p-5">
{#if servers.length > 0} {#if servers.length > 0}
<div class="grid grid-col gap-8 auto-cols-max grid-cols-1 p-4"> <div class="grid grid-col gap-8 auto-cols-max grid-cols-1 p-4">
{#each servers as server} {#each servers as server}

View File

@@ -200,13 +200,10 @@
<div class="mx-auto w-full"> <div class="mx-auto w-full">
<div class="flex lg:flex-row flex-col"> <div class="flex lg:flex-row flex-col">
<Menu /> <Menu />
<div class="mt-5">
<form on:submit|preventDefault={handleSubmit}> <form on:submit|preventDefault={handleSubmit}>
<div <div class="flex flex-col lg:flex-row flex-wrap items-center space-x-3 justify-center lg:justify-start lg:py-0 px-4">
class="flex flex-col space-y-2 p-6 lg:p-0 lg:flex-row lg:space-y0 lg:space-x-4 w-full lg:justify-between lg:items-center mb-5"
>
<div class="title font-bold">{$t('index.global_settings')}</div> <div class="title font-bold">{$t('index.global_settings')}</div>
<div class="flex lg:flex-row lg:space-x-4 flex-col space-y-2 lg:space-y-0"> <div class="flex lg:flex-row lg:space-x-4 flex-col space-y-2 lg:space-y-0 py-4">
<button <button
class="btn btn-sm bg-settings text-black" class="btn btn-sm bg-settings text-black"
type="submit" type="submit"
@@ -235,125 +232,142 @@
> >
</div> </div>
</div> </div>
<div class="grid gap-4 grid-cols-2 auto-rows-max lg:px-10 p-6"> <div class="grid grid-flow-row gap-2 lg:px-10 px-2 pr-5">
<div class="pt-2 text-base font-bold text-stone-100"> <div class="grid grid-cols-2 items-center">
{$t('application.url_fqdn')} <div>
<Explainer explanation={$t('setting.ssl_explainer')} /> {$t('application.url_fqdn')}
</div> <Explainer explanation={$t('setting.ssl_explainer')} />
<input </div>
class="w-full" <input
bind:value={fqdn} class="w-full"
readonly={!$appSession.isAdmin || isFqdnSet} bind:value={fqdn}
disabled={!$appSession.isAdmin || isFqdnSet} readonly={!$appSession.isAdmin || isFqdnSet}
on:input={resetView} disabled={!$appSession.isAdmin || isFqdnSet}
name="fqdn" on:input={resetView}
id="fqdn" name="fqdn"
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$" id="fqdn"
placeholder="{$t('forms.eg')}: https://coolify.io" pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
/> placeholder="{$t('forms.eg')}: https://coolify.io"
/>
{#if forceSave} {#if forceSave}
<div class="flex-col space-y-2 pt-4 text-center"> <div class="flex-col space-y-2 pt-4 text-center">
{#if isNonWWWDomainOK} {#if isNonWWWDomainOK}
<button
class="btn btn-sm bg-success"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="btn btn-sm bg-error"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
>
{/if}
{#if dualCerts}
{#if isWWWDomainOK}
<button <button
class="btn btn-sm bg-success" class="btn btn-sm bg-success"
on:click|preventDefault={() => on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)} >DNS settings for {nonWWWDomain} is OK, click to recheck.</button
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
> >
{:else} {:else}
<button <button
class="btn btn-sm bg-error" class="btn btn-sm bg-error"
on:click|preventDefault={() => on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)} >DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
> >
{/if} {/if}
{/if} {#if dualCerts}
{#if isWWWDomainOK}
<button
class="btn btn-sm bg-success"
on:click|preventDefault={() =>
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="btn btn-sm bg-error"
on:click|preventDefault={() =>
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
>
{/if}
{/if}
</div>
{/if}
</div>
<div class="grid grid-cols-2 items-center">
<div>
{$t('forms.public_port_range')}
<Explainer explanation={$t('forms.public_port_range_explainer')} />
</div>
<div class="flex flex-row items-center space-x-2">
<input
class=" w-full px-2"
type="number"
bind:value={minPort}
min="1024"
max={maxPort}
/>
<p>-</p>
<input
class="w-full px-2"
type="number"
bind:value={maxPort}
min={minPort}
max="65543"
/>
</div> </div>
{/if}
<div class="pt-2 text-base font-bold text-stone-100">
{$t('forms.public_port_range')}
<Explainer explanation={$t('forms.public_port_range_explainer')} />
</div> </div>
<div class="flex flex-row items-center space-x-2"> <div class="grid grid-cols-2 items-center">
<input
class="h-8 w-full px-2"
type="number"
bind:value={minPort}
min="1024"
max={maxPort}
/>
<p>-</p>
<input
class="h-8 w-full px-2"
type="number"
bind:value={maxPort}
min={minPort}
max="65543"
/>
</div>
<Setting
id="isDNSCheckEnabled"
bind:setting={isDNSCheckEnabled}
title={$t('setting.is_dns_check_enabled')}
description={$t('setting.is_dns_check_enabled_explainer')}
on:click={() => changeSettings('isDNSCheckEnabled')}
/>
<div class="text-base font-bold text-stone-100">
Custom DNS servers <Explainer
explanation="You can specify a custom DNS server to verify your domains all over Coolify.<br><br>By default, the OS defined DNS servers are used."
/>
</div>
<input class="w-full" placeholder="1.1.1.1,8.8.8.8" bind:value={DNSServers} />
<Setting
id="dualCerts"
dataTooltip={$t('setting.must_remove_domain_before_changing')}
disabled={isFqdnSet}
bind:setting={dualCerts}
title={$t('application.ssl_www_and_non_www')}
description={$t('setting.generate_www_non_www_ssl')}
on:click={() => !isFqdnSet && changeSettings('dualCerts')}
/>
<Setting
id="isRegistrationEnabled"
bind:setting={isRegistrationEnabled}
title={$t('setting.registration_allowed')}
description={$t('setting.registration_allowed_explainer')}
on:click={() => changeSettings('isRegistrationEnabled')}
/>
<Setting
id="isAPIDebuggingEnabled"
bind:setting={isAPIDebuggingEnabled}
title="API Debugging"
description="Enable API debugging. This will log all API requests and responses.<br><br>You need to restart the Coolify for this to take effect."
on:click={() => changeSettings('isAPIDebuggingEnabled')}
/>
{#if browser && $features.beta}
<Setting <Setting
id="isAutoUpdateEnabled" id="isDNSCheckEnabled"
bind:setting={isAutoUpdateEnabled} bind:setting={isDNSCheckEnabled}
title={$t('setting.auto_update_enabled')} title={$t('setting.is_dns_check_enabled')}
description={$t('setting.auto_update_enabled_explainer')} description={$t('setting.is_dns_check_enabled_explainer')}
on:click={() => changeSettings('isAutoUpdateEnabled')} on:click={() => changeSettings('isDNSCheckEnabled')}
/> />
</div>
<div class="grid grid-cols-2 items-center">
<div>
Custom DNS servers <Explainer
explanation="You can specify a custom DNS server to verify your domains all over Coolify.<br><br>By default, the OS defined DNS servers are used."
/>
</div>
<input class="w-full" placeholder="1.1.1.1,8.8.8.8" bind:value={DNSServers} />
</div>
<div class="grid grid-cols-2 items-center">
<Setting
id="dualCerts"
dataTooltip={$t('setting.must_remove_domain_before_changing')}
disabled={isFqdnSet}
bind:setting={dualCerts}
title={$t('application.ssl_www_and_non_www')}
description={$t('setting.generate_www_non_www_ssl')}
on:click={() => !isFqdnSet && changeSettings('dualCerts')}
/>
</div>
<div class="grid grid-cols-2 items-center">
<Setting
id="isRegistrationEnabled"
bind:setting={isRegistrationEnabled}
title={$t('setting.registration_allowed')}
description={$t('setting.registration_allowed_explainer')}
on:click={() => changeSettings('isRegistrationEnabled')}
/>
</div>
<div class="grid grid-cols-2 items-center">
<Setting
id="isAPIDebuggingEnabled"
bind:setting={isAPIDebuggingEnabled}
title="API Debugging"
description="Enable API debugging. This will log all API requests and responses.<br><br>You need to restart the Coolify for this to take effect."
on:click={() => changeSettings('isAPIDebuggingEnabled')}
/>
</div>
{#if browser && $features.beta}
<div class="grid grid-cols-2 items-center">
<Setting
id="isAutoUpdateEnabled"
bind:setting={isAutoUpdateEnabled}
title={$t('setting.auto_update_enabled')}
description={$t('setting.auto_update_enabled_explainer')}
on:click={() => changeSettings('isAutoUpdateEnabled')}
/>
</div>
{/if} {/if}
</div> </div>
</form> </form>
</div>
</div> </div>
</div> </div>

View File

@@ -65,9 +65,9 @@
<Menu /> <Menu />
<div class="flex flex-col mt-5"> <div class="flex flex-col mt-5">
<div <div
class="flex flex-col space-y-2 p-6 lg:p-0 lg:flex-row lg:space-y0 lg:space-x-4 w-full lg:justify-between lg:items-center mb-5" class="flex flex-col lg:flex-row flex-wrap items-center space-x-3 justify-center lg:justify-start lg:py-0 px-4 pb-4 lg:pb-4"
style="min-width: 83vw" style="min-width: 83vw"
> >
<div class="title font-bold">SSH Keys</div> <div class="title font-bold">SSH Keys</div>
<button <button
on:click={() => (isModalActive = true)} on:click={() => (isModalActive = true)}