ui: change tooltips and info boxes

This commit is contained in:
Andras Bacsai
2022-09-01 11:20:22 +02:00
parent e1697848a5
commit f6bb14f7c4
28 changed files with 428 additions and 290 deletions

View File

@@ -14,15 +14,20 @@
"format": "prettier --write --plugin-search-dir=. ." "format": "prettier --write --plugin-search-dir=. ."
}, },
"devDependencies": { "devDependencies": {
"@floating-ui/dom": "1.0.1",
"@playwright/test": "1.25.1", "@playwright/test": "1.25.1",
"@popperjs/core": "2.11.6",
"@sveltejs/kit": "1.0.0-next.405", "@sveltejs/kit": "1.0.0-next.405",
"@types/js-cookie": "3.0.2", "@types/js-cookie": "3.0.2",
"@typescript-eslint/eslint-plugin": "5.35.1", "@typescript-eslint/eslint-plugin": "5.35.1",
"@typescript-eslint/parser": "5.35.1", "@typescript-eslint/parser": "5.35.1",
"autoprefixer": "10.4.8", "autoprefixer": "10.4.8",
"classnames": "2.3.1",
"eslint": "8.22.0", "eslint": "8.22.0",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.5.0",
"eslint-plugin-svelte3": "4.0.0", "eslint-plugin-svelte3": "4.0.0",
"flowbite": "1.5.2",
"flowbite-svelte": "0.26.2",
"postcss": "8.4.16", "postcss": "8.4.16",
"prettier": "2.7.1", "prettier": "2.7.1",
"prettier-plugin-svelte": "2.7.0", "prettier-plugin-svelte": "2.7.0",

View File

@@ -0,0 +1,37 @@
<script lang="ts">
import { onMount } from 'svelte';
import Tooltip from './Tooltip.svelte';
export let isLink = false;
export let explanation = '';
let id: any;
let self: any;
onMount(() => {
id = `info-${self.offsetLeft}-${self.offsetTop}`;
});
</script>
<div {id} class="inline-block mx-2 text-pink-500 cursor-pointer" bind:this={self}>
<svg
fill="none"
height="18"
shape-rendering="geometricPrecision"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
viewBox="0 0 24 24"
width="18"
><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z" /><path
d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3"
/><circle cx="12" cy="17" r=".5" />
</svg>
</div>
{#if id}
{#if isLink}
LINK
{:else}
<Tooltip triggeredBy={`#${id}`}>{@html explanation}</Tooltip>
{/if}
{/if}

View File

@@ -1,6 +1,8 @@
<script lang="ts"> <script lang="ts">
import Explainer from '$lib/components/Explainer.svelte'; import DocLink from './DocLink.svelte';
import Tooltip from './Tooltip.svelte';
export let id: any;
export let setting: any; export let setting: any;
export let title: any; export let title: any;
export let description: any; export let description: any;
@@ -8,22 +10,17 @@
export let disabled = false; export let disabled = false;
export let dataTooltip: any = null; export let dataTooltip: any = null;
export let loading = false; export let loading = false;
let triggeredBy = `#${id}`;
</script> </script>
<div class="flex items-center py-4 pr-8 max-w-xs"> <div class="flex items-center py-4 pr-8">
<div class="flex w-96 flex-col"> <div class="flex w-96 flex-col">
<div class="text-xs font-bold text-stone-100 md:text-base">{title}</div> <div class="text-xs font-bold text-stone-100 md:text-base">
<Explainer text={description} /> {title}<DocLink explanation={description} />
</div> </div>
</div> </div>
<div </div>
class:tooltip-right={dataTooltip} <div class:text-center={isCenter} class="flex justify-center">
class:tooltip-primary={dataTooltip}
class:tooltip={dataTooltip}
class:text-center={isCenter}
data-tip={dataTooltip}
class="flex justify-center"
>
<div <div
on:click on:click
aria-pressed="false" aria-pressed="false"
@@ -32,6 +29,7 @@
class:bg-green-600={!loading && setting} class:bg-green-600={!loading && setting}
class:bg-stone-700={!loading && !setting} class:bg-stone-700={!loading && !setting}
class:bg-yellow-500={loading} class:bg-yellow-500={loading}
{id}
> >
<span class="sr-only">Use setting</span> <span class="sr-only">Use setting</span>
<span <span
@@ -72,3 +70,7 @@
</span> </span>
</div> </div>
</div> </div>
{#if dataTooltip}
<Tooltip {triggeredBy} placement="top">{dataTooltip}</Tooltip>
{/if}

View File

@@ -0,0 +1,8 @@
<script lang="ts">
import { Tooltip } from 'flowbite-svelte';
export let placement = 'bottom';
export let color = 'bg-coollabs text-left';
export let triggeredBy = '#tooltip-default';
</script>
<Tooltip {triggeredBy} {placement} arrow={false} {color} style="custom"><slot /></Tooltip>

View File

@@ -209,7 +209,7 @@
"expose_a_port": "Expose a port", "expose_a_port": "Expose a port",
"enable_preview_deploy_mr_pr_requests": "Enable preview deployments from pull or merge requests.", "enable_preview_deploy_mr_pr_requests": "Enable preview deployments from pull or merge requests.",
"debug_logs": "Debug Logs", "debug_logs": "Debug Logs",
"enable_debug_log_during_build": "Enable debug logs during build phase.<br><span class='text-red-500 font-bold'>Sensitive information</span> could be visible and saved in logs.", "enable_debug_log_during_build": "Enable debug logs during build phase.<br><span class='text-settings font-bold'>Sensitive information</span> could be visible and saved in logs.",
"cant_activate_auto_deploy_without_repo": "Cannot activate automatic deployments until only one application is defined for this repository / branch.", "cant_activate_auto_deploy_without_repo": "Cannot activate automatic deployments until only one application is defined for this repository / branch.",
"no_applications_found": "No applications found", "no_applications_found": "No applications found",
"secret__batch_dot_env": "Paste .env file", "secret__batch_dot_env": "Paste .env file",
@@ -275,7 +275,7 @@
"application_id": "Application ID", "application_id": "Application ID",
"group_name": "Group Name", "group_name": "Group Name",
"oauth_id": "OAuth ID", "oauth_id": "OAuth ID",
"oauth_id_explainer": "The OAuth ID is the unique identifier of the GitLab application. <br>You can find it <span class='font-bold text-orange-600' >in the URL</span> of your GitLab OAuth Application.", "oauth_id_explainer": "The OAuth ID is the unique identifier of the GitLab application. <br>You can find it <span class='font-bold text-settings' >in the URL</span> of your GitLab OAuth Application.",
"register_oauth_gitlab": "Register new OAuth application on GitLab", "register_oauth_gitlab": "Register new OAuth application on GitLab",
"gitlab": { "gitlab": {
"self_hosted": "Instance-wide application (self-hosted)", "self_hosted": "Instance-wide application (self-hosted)",

View File

@@ -88,6 +88,7 @@
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store'; import { appSession } from '$lib/store';
import Toasts from '$lib/components/Toasts.svelte'; import Toasts from '$lib/components/Toasts.svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
if (userId) $appSession.userId = userId; if (userId) $appSession.userId = userId;
if (teamId) $appSession.teamId = teamId; if (teamId) $appSession.teamId = teamId;
@@ -132,12 +133,12 @@
{/if} {/if}
<div class="flex flex-col space-y-2 py-2" class:mt-2={$appSession.whiteLabeled}> <div class="flex flex-col space-y-2 py-2" class:mt-2={$appSession.whiteLabeled}>
<a <a
id="dashboard"
sveltekit:prefetch sveltekit:prefetch
href="/" href="/"
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200 hover:text-white" class="icons bg-coolgray-200 hover:text-white"
class:text-white={$page.url.pathname === '/'} class:text-white={$page.url.pathname === '/'}
class:bg-coolgray-500={$page.url.pathname === '/'} class:bg-coolgray-500={$page.url.pathname === '/'}
data-tip="Dashboard"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -156,12 +157,13 @@
<path d="M16 15c-2.21 1.333 -5.792 1.333 -8 0" /> <path d="M16 15c-2.21 1.333 -5.792 1.333 -8 0" />
</svg> </svg>
</a> </a>
<div class="border-t border-stone-700" />
<div class="border-t border-stone-700" />
<a <a
id="applications"
sveltekit:prefetch sveltekit:prefetch
href="/applications" href="/applications"
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200" class="icons bg-coolgray-200"
class:text-applications={$page.url.pathname.startsWith('/applications') || class:text-applications={$page.url.pathname.startsWith('/applications') ||
$page.url.pathname.startsWith('/new/application')} $page.url.pathname.startsWith('/new/application')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/applications') || class:bg-coolgray-500={$page.url.pathname.startsWith('/applications') ||
@@ -186,10 +188,12 @@
<line x1="17" y1="4" x2="17" y2="10" /> <line x1="17" y1="4" x2="17" y2="10" />
</svg> </svg>
</a> </a>
<a <a
id="sources"
sveltekit:prefetch sveltekit:prefetch
href="/sources" href="/sources"
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200" class="icons bg-coolgray-200"
class:text-sources={$page.url.pathname.startsWith('/sources') || class:text-sources={$page.url.pathname.startsWith('/sources') ||
$page.url.pathname.startsWith('/new/source')} $page.url.pathname.startsWith('/new/source')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/sources') || class:bg-coolgray-500={$page.url.pathname.startsWith('/sources') ||
@@ -216,9 +220,10 @@
</svg> </svg>
</a> </a>
<a <a
id="destinations"
sveltekit:prefetch sveltekit:prefetch
href="/destinations" href="/destinations"
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200" class="icons bg-coolgray-200"
class:text-destinations={$page.url.pathname.startsWith('/destinations') || class:text-destinations={$page.url.pathname.startsWith('/destinations') ||
$page.url.pathname.startsWith('/new/destination')} $page.url.pathname.startsWith('/new/destination')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/destinations') || class:bg-coolgray-500={$page.url.pathname.startsWith('/destinations') ||
@@ -251,9 +256,10 @@
</a> </a>
<div class="border-t border-stone-700" /> <div class="border-t border-stone-700" />
<a <a
id="databases"
sveltekit:prefetch sveltekit:prefetch
href="/databases" href="/databases"
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200" class="icons bg-coolgray-200"
class:text-databases={$page.url.pathname.startsWith('/databases') || class:text-databases={$page.url.pathname.startsWith('/databases') ||
$page.url.pathname.startsWith('/new/database')} $page.url.pathname.startsWith('/new/database')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/databases') || class:bg-coolgray-500={$page.url.pathname.startsWith('/databases') ||
@@ -277,9 +283,10 @@
</svg> </svg>
</a> </a>
<a <a
id="services"
sveltekit:prefetch sveltekit:prefetch
href="/services" href="/services"
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200" class="icons bg-coolgray-200"
class:text-services={$page.url.pathname.startsWith('/services') || class:text-services={$page.url.pathname.startsWith('/services') ||
$page.url.pathname.startsWith('/new/service')} $page.url.pathname.startsWith('/new/service')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/services') || class:bg-coolgray-500={$page.url.pathname.startsWith('/services') ||
@@ -306,9 +313,10 @@
<UpdateAvailable /> <UpdateAvailable />
<div class="flex flex-col space-y-2 py-2"> <div class="flex flex-col space-y-2 py-2">
<a <a
id="iam"
sveltekit:prefetch sveltekit:prefetch
href="/iam" href="/iam"
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200" class="icons bg-coolgray-200"
class:text-iam={$page.url.pathname.startsWith('/iam')} class:text-iam={$page.url.pathname.startsWith('/iam')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/iam')} class:bg-coolgray-500={$page.url.pathname.startsWith('/iam')}
data-tip="IAM" data-tip="IAM"
@@ -330,9 +338,10 @@
</svg> </svg>
</a> </a>
<a <a
id="settings"
sveltekit:prefetch sveltekit:prefetch
href={$appSession.teamId === '0' ? '/settings/global' : '/settings/ssh-keys'} href={$appSession.teamId === '0' ? '/settings/global' : '/settings/ssh-keys'}
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200" class="icons bg-coolgray-200"
class:text-settings={$page.url.pathname.startsWith('/settings')} class:text-settings={$page.url.pathname.startsWith('/settings')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/settings')} class:bg-coolgray-500={$page.url.pathname.startsWith('/settings')}
data-tip="Settings" data-tip="Settings"
@@ -356,7 +365,8 @@
</a> </a>
<div <div
class="icons tooltip tooltip-primary tooltip-right bg-coolgray-200 hover:text-error" id="logout"
class="icons bg-coolgray-200 hover:text-error"
data-tip="Logout" data-tip="Logout"
on:click={logout} on:click={logout}
> >
@@ -400,3 +410,16 @@
<slot /> <slot />
</div> </div>
</main> </main>
<Tooltip triggeredBy="#dashboard" placement="right">Dashboard</Tooltip>
<Tooltip triggeredBy="#applications" placement="right" color="bg-applications">Applications</Tooltip
>
<Tooltip triggeredBy="#sources" placement="right" color="bg-sources">Git Sources</Tooltip>
<Tooltip triggeredBy="#destinations" placement="right" color="bg-destinations">Destinations</Tooltip
>
<Tooltip triggeredBy="#databases" placement="right" color="bg-databases">Databases</Tooltip>
<Tooltip triggeredBy="#services" placement="right" color="bg-services">Services</Tooltip>
<Tooltip triggeredBy="#iam" placement="right" color="bg-iam">IAM</Tooltip>
<Tooltip triggeredBy="#settings" placement="right" color="bg-settings text-black">Settings</Tooltip
>
<Tooltip triggeredBy="#logout" placement="right" color="bg-red-600">Logout</Tooltip>

View File

@@ -62,6 +62,7 @@
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { appSession, disabledButton, status, location, setLocation, addToast } from '$lib/store'; import { appSession, disabledButton, status, location, setLocation, addToast } from '$lib/store';
import { errorNotification, handlerNotFoundLoad } from '$lib/common'; import { errorNotification, handlerNotFoundLoad } from '$lib/common';
import Tooltip from '$lib/components/Tooltip.svelte';
let statusInterval: any; let statusInterval: any;
$disabledButton = $disabledButton =
@@ -115,6 +116,10 @@
$status.application.initialLoading = true; $status.application.initialLoading = true;
$status.application.loading = true; $status.application.loading = true;
await post(`/applications/${id}/restart`, {}); await post(`/applications/${id}/restart`, {});
addToast({
type: 'success',
message: 'Restart successful.'
});
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
} finally { } finally {
@@ -174,9 +179,10 @@
<nav class="nav-side"> <nav class="nav-side">
{#if $location} {#if $location}
<a <a
id="open"
href={$location} href={$location}
target="_blank" target="_blank"
class="icons tooltip-bottom flex items-center bg-transparent text-sm" class="icons flex items-center bg-transparent text-sm"
><svg ><svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@@ -193,14 +199,16 @@
<polyline points="15 4 20 4 20 9" /> <polyline points="15 4 20 4 20 9" />
</svg></a </svg></a
> >
<Tooltip triggeredBy="#open">Open</Tooltip>
<div class="border border-coolgray-500 h-8" /> <div class="border border-coolgray-500 h-8" />
{/if} {/if}
{#if $status.application.isExited} {#if $status.application.isExited}
<a <a
id="applicationerror"
href={!$disabledButton ? `/applications/${id}/logs` : null} href={!$disabledButton ? `/applications/${id}/logs` : null}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center text-error" class="icons bg-transparent text-sm flex items-center text-error"
data-tip="Application exited with an error!"
sveltekit:prefetch sveltekit:prefetch
> >
<svg <svg
@@ -221,10 +229,11 @@
<line x1="12" y1="16" x2="12.01" y2="16" /> <line x1="12" y1="16" x2="12.01" y2="16" />
</svg> </svg>
</a> </a>
<Tooltip triggeredBy="#applicationerror">Application exited with an error!</Tooltip>
{/if} {/if}
{#if $status.application.initialLoading} {#if $status.application.initialLoading}
<button <button
class="icons tooltip-bottom flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out" class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -247,13 +256,11 @@
</button> </button>
{:else if $status.application.isRunning} {:else if $status.application.isRunning}
<button <button
id="stop"
on:click={stopApplication} on:click={stopApplication}
type="submit" type="submit"
disabled={$disabledButton} disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2 text-error" class="icons bg-transparent text-sm flex items-center space-x-2 text-error"
data-tip={$appSession.isAdmin
? 'Stop'
: $t('application.permission_denied_stop_application')}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -270,14 +277,14 @@
<rect x="14" y="5" width="4" height="14" rx="1" /> <rect x="14" y="5" width="4" height="14" rx="1" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#stop">Stop</Tooltip>
<button <button
id="restart"
on:click={restartApplication} on:click={restartApplication}
type="submit" type="submit"
disabled={$disabledButton} disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2 " class="icons bg-transparent text-sm flex items-center space-x-2"
data-tip={$appSession.isAdmin
? 'Restart (useful for changing secrets)'
: $t('application.permission_denied_stop_application')}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -294,14 +301,14 @@
<path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4" /> <path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#restart">Restart (useful to change secrets)</Tooltip>
<form on:submit|preventDefault={() => handleDeploySubmit(true)}> <form on:submit|preventDefault={() => handleDeploySubmit(true)}>
<button <button
id="forceredeploy"
type="submit" type="submit"
disabled={$disabledButton} disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2" class="icons bg-transparent text-sm flex items-center space-x-2"
data-tip={$appSession.isAdmin
? 'Force Rebuild without cache'
: 'You do not have permission to rebuild application.'}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -320,16 +327,15 @@
/> />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#forceredeploy">Force redeploy (without cache)</Tooltip>
</form> </form>
{:else} {:else}
<form on:submit|preventDefault={() => handleDeploySubmit(false)}> <form on:submit|preventDefault={() => handleDeploySubmit(false)}>
<button <button
id="deploy"
type="submit" type="submit"
disabled={$disabledButton} disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2 text-success" class="icons bg-transparent text-sm flex items-center space-x-2 text-success"
data-tip={$appSession.isAdmin
? 'Deploy'
: 'You do not have permission to deploy application.'}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -345,22 +351,20 @@
<path d="M7 4v16l13 -8z" /> <path d="M7 4v16l13 -8z" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#deploy">Deploy</Tooltip>
</form> </form>
{/if} {/if}
<div class="border border-coolgray-500 h-8" /> <div class="border border-coolgray-500 h-8" />
<a <a
id="configurations"
href={!$disabledButton ? `/applications/${id}` : null} href={!$disabledButton ? `/applications/${id}` : null}
sveltekit:prefetch sveltekit:prefetch
class="hover:text-yellow-500 rounded" class="hover:text-yellow-500 rounded"
class:text-yellow-500={$page.url.pathname === `/applications/${id}`} class:text-yellow-500={$page.url.pathname === `/applications/${id}`}
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}`} class:bg-coolgray-500={$page.url.pathname === `/applications/${id}`}
> >
<button <button disabled={$disabledButton} class="icons bg-transparent text-sm">
disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
data-tip="Configurations"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@@ -385,17 +389,14 @@
></a ></a
> >
<a <a
id="secrets"
href={!$disabledButton ? `/applications/${id}/secrets` : null} href={!$disabledButton ? `/applications/${id}/secrets` : null}
sveltekit:prefetch sveltekit:prefetch
class="hover:text-pink-500 rounded" class="hover:text-pink-500 rounded"
class:text-pink-500={$page.url.pathname === `/applications/${id}/secrets`} class:text-pink-500={$page.url.pathname === `/applications/${id}/secrets`}
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/secrets`} class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/secrets`}
> >
<button <button disabled={$disabledButton} class="icons bg-transparent text-sm">
disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
data-tip="Secrets"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6" class="w-6 h-6"
@@ -416,17 +417,14 @@
></a ></a
> >
<a <a
id="persistentstorages"
href={!$disabledButton ? `/applications/${id}/storages` : null} href={!$disabledButton ? `/applications/${id}/storages` : null}
sveltekit:prefetch sveltekit:prefetch
class="hover:text-pink-500 rounded" class="hover:text-pink-500 rounded"
class:text-pink-500={$page.url.pathname === `/applications/${id}/storages`} class:text-pink-500={$page.url.pathname === `/applications/${id}/storages`}
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/storages`} class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/storages`}
> >
<button <button disabled={$disabledButton} class="icons bg-transparent text-sm">
disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
data-tip="Persistent Storages"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6" class="w-6 h-6"
@@ -446,17 +444,14 @@
> >
{#if !application.settings.isBot} {#if !application.settings.isBot}
<a <a
id="previews"
href={!$disabledButton ? `/applications/${id}/previews` : null} href={!$disabledButton ? `/applications/${id}/previews` : null}
sveltekit:prefetch sveltekit:prefetch
class="hover:text-orange-500 rounded" class="hover:text-orange-500 rounded"
class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`} class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`} class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`}
> >
<button <button disabled={$disabledButton} class="icons bg-transparent text-sm">
disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
data-tip="Previews"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6" class="w-6 h-6"
@@ -479,6 +474,7 @@
{/if} {/if}
<div class="border border-coolgray-500 h-8" /> <div class="border border-coolgray-500 h-8" />
<a <a
id="applicationlogs"
href={!$disabledButton && $status.application.isRunning ? `/applications/${id}/logs` : null} href={!$disabledButton && $status.application.isRunning ? `/applications/${id}/logs` : null}
sveltekit:prefetch sveltekit:prefetch
class="hover:text-sky-500 rounded" class="hover:text-sky-500 rounded"
@@ -487,8 +483,7 @@
> >
<button <button
disabled={$disabledButton || !$status.application.isRunning} disabled={$disabledButton || !$status.application.isRunning}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm" class="icons bg-transparent text-sm"
data-tip={$t('application.logs')}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -510,17 +505,14 @@
</button></a </button></a
> >
<a <a
id="buildlogs"
href={!$disabledButton ? `/applications/${id}/logs/build` : null} href={!$disabledButton ? `/applications/${id}/logs/build` : null}
sveltekit:prefetch sveltekit:prefetch
class="hover:text-red-500 rounded" class="hover:text-red-500 rounded"
class:text-red-500={$page.url.pathname === `/applications/${id}/logs/build`} class:text-red-500={$page.url.pathname === `/applications/${id}/logs/build`}
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs/build`} class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs/build`}
> >
<button <button disabled={$disabledButton} class="icons bg-transparent text-sm">
disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
data-tip="Build Logs"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@@ -546,16 +538,22 @@
<div class="border border-coolgray-500 h-8" /> <div class="border border-coolgray-500 h-8" />
<button <button
id="delete"
on:click={() => deleteApplication(application.name)} on:click={() => deleteApplication(application.name)}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin} class:hover:text-red-500={$appSession.isAdmin}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm" class="icons bg-transparent text-sm"
data-tip={$appSession.isAdmin
? $t('application.delete_application')
: $t('application.permission_denied_delete_application')}
> >
<DeleteIcon /> <DeleteIcon />
</button> </button>
</nav> </nav>
<slot /> <slot />
<Tooltip triggeredBy="#configurations">Configurations</Tooltip>
<Tooltip triggeredBy="#secrets">Secrets</Tooltip>
<Tooltip triggeredBy="#persistentstorages">Persistent Storages</Tooltip>
<Tooltip triggeredBy="#previews">Previews</Tooltip>
<Tooltip triggeredBy="#applicationlogs">Application Logs</Tooltip>
<Tooltip triggeredBy="#buildlogs">Build Logs</Tooltip>
<Tooltip triggeredBy="#delete">Delete</Tooltip>

View File

@@ -164,7 +164,6 @@
<div class="space-y-4"> <div class="space-y-4">
<input <input
placeholder="eg: https://github.com/coollabsio/nodejs-example/tree/main" placeholder="eg: https://github.com/coollabsio/nodejs-example/tree/main"
class="text-xs"
bind:value={publicRepositoryLink} bind:value={publicRepositoryLink}
/> />
{#if branchSelectOptions.length > 0} {#if branchSelectOptions.length > 0}
@@ -193,7 +192,5 @@
</form> </form>
</div> </div>
</div> </div>
<Explainer
text="Examples:<br><br>https://github.com/coollabsio/nodejs-example<br>https://github.com/coollabsio/nodejs-example/tree/main<br>https://gitlab.com/aleveha/fastify-example<br>https://gitlab.com/aleveha/fastify-example/-/tree/master<br><br>Only works with Github.com and Gitlab.com."
/>
</div> </div>

View File

@@ -33,6 +33,7 @@
import { appSession } from '$lib/store'; import { appSession } from '$lib/store';
import PublicRepository from './_PublicRepository.svelte'; import PublicRepository from './_PublicRepository.svelte';
import Explainer from '$lib/components/Explainer.svelte'; import Explainer from '$lib/components/Explainer.svelte';
import DocLink from '$lib/components/DocLink.svelte';
const { id } = $page.params; const { id } = $page.params;
const from = $page.url.searchParams.get('from'); const from = $page.url.searchParams.get('from');
@@ -192,7 +193,6 @@ import Explainer from '$lib/components/Explainer.svelte';
</div> </div>
{/if} {/if}
</div> </div>
<div class="title py-4">Public Repository</div> <div class="title py-4">Public Repository <DocLink /></div>
<PublicRepository /> <PublicRepository />
</div> </div>

View File

@@ -39,7 +39,9 @@
import { addToast, appSession, disabledButton, setLocation, status } from '$lib/store'; import { addToast, appSession, disabledButton, setLocation, status } from '$lib/store';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { errorNotification, getDomain, notNodeDeployments, staticDeployments } from '$lib/common'; import { errorNotification, getDomain, notNodeDeployments, staticDeployments } from '$lib/common';
import Setting from './_Setting.svelte'; import Setting from '$lib/components/Setting.svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
import DocLink from '$lib/components/DocLink.svelte';
const { id } = $page.params; const { id } = $page.params;
$: isDisabled = $: isDisabled =
@@ -281,6 +283,7 @@
</div> </div>
{#if application.gitSource?.htmlUrl && application.repository && application.branch} {#if application.gitSource?.htmlUrl && application.repository && application.branch}
<a <a
id="git"
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}" href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
target="_blank" target="_blank"
class="w-10" class="w-10"
@@ -321,6 +324,7 @@
</svg> </svg>
{/if} {/if}
</a> </a>
<Tooltip triggeredBy="#git">Open on Git</Tooltip>
{/if} {/if}
</div> </div>
@@ -426,7 +430,7 @@
> >
{/if} {/if}
</div> </div>
<div class="grid grid-cols-2 items-center pb-8"> <div class="grid grid-cols-2 items-center">
<label for="destination" class="text-base font-bold text-stone-100" <label for="destination" class="text-base font-bold text-stone-100"
>{$t('application.destination')}</label >{$t('application.destination')}</label
> >
@@ -440,10 +444,15 @@
</div> </div>
</div> </div>
{#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'} {#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'}
<div class="grid grid-cols-2 items-center pb-8"> <div class="grid grid-cols-2 items-center">
<label for="baseBuildImage" class="text-base font-bold text-stone-100" <label for="baseBuildImage" class="text-base font-bold text-stone-100"
>{$t('application.base_build_image')}</label >{$t('application.base_build_image')}
> <DocLink
explanation={application.buildPack === 'laravel'
? 'For building frontend assets with webpack.'
: 'Image that will be used during the build process.'}
/>
</label>
<div class="custom-select-wrapper"> <div class="custom-select-wrapper">
<Select <Select
@@ -457,17 +466,13 @@
isClearable={false} isClearable={false}
/> />
</div> </div>
{#if application.buildPack === 'laravel'}
<Explainer text="For building frontend assets with webpack." />
{:else}
<Explainer text={$t('application.base_build_image_explainer')} />
{/if}
</div> </div>
{/if} {/if}
{#if application.buildPack !== 'docker'} {#if application.buildPack !== 'docker'}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="baseImage" class="text-base font-bold text-stone-100" <label for="baseImage" class="text-base font-bold text-stone-100"
>{$t('application.base_image')}</label >{$t('application.base_image')}
<DocLink explanation={'Image that will be used for the deployment.'} /></label
> >
<div class="custom-select-wrapper"> <div class="custom-select-wrapper">
<Select <Select
@@ -481,13 +486,15 @@
isClearable={false} isClearable={false}
/> />
</div> </div>
<Explainer text={$t('application.base_image_explainer')} />
</div> </div>
{/if} {/if}
{#if application.buildPack !== 'docker' && (application.buildPack === 'nextjs' || application.buildPack === 'nuxtjs')} {#if application.buildPack !== 'docker' && (application.buildPack === 'nextjs' || application.buildPack === 'nuxtjs')}
<div class="grid grid-cols-2 items-center pb-8"> <div class="grid grid-cols-2 items-center pb-8">
<label for="deploymentType" class="text-base font-bold text-stone-100" <label for="deploymentType" class="text-base font-bold text-stone-100"
>Deployment Type</label >Deployment Type
<DocLink
explanation={"Defines how to deploy your application. <br><br><span class='text-green-500 font-bold'>Static</span> is for static websites, <span class='text-green-500 font-bold'>node</span> is for server-side applications."}
/></label
> >
<div class="custom-select-wrapper"> <div class="custom-select-wrapper">
<Select <Select
@@ -501,9 +508,6 @@
isClearable={false} isClearable={false}
/> />
</div> </div>
<Explainer
text="Defines how to deploy your application. <br><br><span class='text-green-500 font-bold'>Static</span> is for static websites, <span class='text-green-500 font-bold'>node</span> is for server-side applications."
/>
</div> </div>
{/if} {/if}
</div> </div>
@@ -513,26 +517,36 @@
<div class="grid grid-flow-row gap-2 px-10"> <div class="grid grid-flow-row gap-2 px-10">
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<Setting <Setting
id="isBot"
isCenter={false} isCenter={false}
bind:setting={isBot} bind:setting={isBot}
on:click={() => changeSettings('isBot')} on:click={() => changeSettings('isBot')}
title="Is your application a bot?" title="Is your application a bot?"
description="You can deploy applications without domains. <br>You can also make them to listen on <span class='text-green-500 font-bold'>IP:EXPOSEDPORT</span> as well.<br></Setting><br>Useful to host <span class='text-green-500 font-bold'>Twitch bots, regular jobs, or anything that does not require an incoming connection.</span>" description="You can deploy applications without domains or make them to listen on the <span class='text-settings font-bold'>Exposed Port</span>.<br></Setting><br>Useful to host <span class='text-settings font-bold'>Twitch bots, regular jobs, or anything that does not require an incoming HTTP connection.</span>"
disabled={$status.application.isRunning} disabled={$status.application.isRunning}
/> />
</div> </div>
<div class="grid grid-cols-2 items-center pb-8">
<Setting
id="dualCerts"
dataTooltip={$t('forms.must_be_stopped_to_modify')}
disabled={$status.application.isRunning}
isCenter={false}
bind:setting={dualCerts}
title={$t('application.ssl_www_and_non_www')}
description="It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-settings'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both."
on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
/>
</div>
{#if !isBot} {#if !isBot}
<div class="grid grid-cols-2"> <div class="grid grid-cols-2">
<div class="flex-col"> <div class="flex-col">
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100" <label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
>{$t('application.url_fqdn')}</label >{$t('application.url_fqdn')}
> <DocLink
{#if browser && window.location.hostname === 'demo.coolify.io'} explanation={"If you specify <span class='text-settings font-bold'>https</span>, the application will be accessible only over https.<br>SSL certificate will be generated automatically.<br><br>If you specify <span class='text-settings font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application.<br><br><span class='text-settings font-bold'>You must set your DNS to point to the server IP in advance.</span>"}
<Explainer
text="<span class='text-white font-bold'>You can use the predefined random url name or enter your own domain name.</span>"
/> />
{/if} </label>
<Explainer text={$t('application.https_explainer')} />
</div> </div>
<div> <div>
<input <input
@@ -582,17 +596,6 @@
{/if} {/if}
</div> </div>
</div> </div>
<div class="grid grid-cols-2 items-center pb-8">
<Setting
dataTooltip={$t('forms.must_be_stopped_to_modify')}
disabled={$status.application.isRunning}
isCenter={false}
bind:setting={dualCerts}
title={$t('application.ssl_www_and_non_www')}
description={$t('application.ssl_explainer')}
on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
/>
</div>
{/if} {/if}
{#if application.buildPack === 'python'} {#if application.buildPack === 'python'}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
@@ -658,7 +661,11 @@
</div> </div>
{/if} {/if}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label> <label for="exposePort" class="text-base font-bold text-stone-100"
>Exposed Port <DocLink
explanation={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
/></label
>
<input <input
readonly={!$appSession.isAdmin && !$status.application.isRunning} readonly={!$appSession.isAdmin && !$status.application.isRunning}
disabled={isDisabled} disabled={isDisabled}
@@ -667,9 +674,6 @@
bind:value={application.exposePort} bind:value={application.exposePort}
placeholder="12345" placeholder="12345"
/> />
<Explainer
text={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
/>
</div> </div>
{#if !notNodeDeployments.includes(application.buildPack)} {#if !notNodeDeployments.includes(application.buildPack)}
<div class="grid grid-cols-2 items-center pt-4"> <div class="grid grid-cols-2 items-center pt-4">
@@ -715,7 +719,9 @@
{#if application.buildPack === 'docker'} {#if application.buildPack === 'docker'}
<div class="grid grid-cols-2 items-center pt-4"> <div class="grid grid-cols-2 items-center pt-4">
<label for="dockerFileLocation" class="text-base font-bold text-stone-100" <label for="dockerFileLocation" class="text-base font-bold text-stone-100"
>Dockerfile Location</label >Dockerfile Location <DocLink
explanation={"Should be absolute path, like <span class='text-settings font-bold'>/data/Dockerfile</span> or <span class='text-settings font-bold'>/Dockerfile.</span>"}
/></label
> >
<input <input
disabled={isDisabled} disabled={isDisabled}
@@ -725,9 +731,6 @@
bind:value={application.dockerFileLocation} bind:value={application.dockerFileLocation}
placeholder="default: /Dockerfile" placeholder="default: /Dockerfile"
/> />
<Explainer
text="Should be absolute path, like <span class='text-green-500 font-bold'>/data/Dockerfile</span> or <span class='text-green-500 font-bold'>/Dockerfile.</span>"
/>
</div> </div>
{/if} {/if}
{#if application.buildPack === 'deno'} {#if application.buildPack === 'deno'}
@@ -743,7 +746,11 @@
/> />
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="denoOptions" class="text-base font-bold text-stone-100">Arguments</label> <label for="denoOptions" class="text-base font-bold text-stone-100"
>Arguments <DocLink
explanation={"List of arguments to pass to <span class='text-settings font-bold'>deno run</span> command. Could include permissions, configurations files, etc."}
/></label
>
<input <input
disabled={isDisabled} disabled={isDisabled}
readonly={!$appSession.isAdmin} readonly={!$appSession.isAdmin}
@@ -752,18 +759,17 @@
bind:value={application.denoOptions} bind:value={application.denoOptions}
placeholder="eg: --allow-net --allow-hrtime --config path/to/file.json" placeholder="eg: --allow-net --allow-hrtime --config path/to/file.json"
/> />
<Explainer
text="List of arguments to pass to <span class='text-green-500 font-bold'>deno run</span> command. Could include permissions, configurations files, etc."
/>
</div> </div>
{/if} {/if}
{#if application.buildPack !== 'laravel' && application.buildPack !== 'heroku'} {#if application.buildPack !== 'laravel' && application.buildPack !== 'heroku'}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<div class="flex-col"> <div class="flex-col">
<label for="baseDirectory" class="pt-2 text-base font-bold text-stone-100" <label for="baseDirectory" class="pt-2 text-base font-bold text-stone-100"
>{$t('forms.base_directory')}</label >{$t('forms.base_directory')}
<DocLink
explanation={"Directory to use as the base for all commands.<br>Could be useful with <span class='text-settings font-bold'>monorepos</span>."}
/></label
> >
<Explainer text={$t('application.directory_to_use_explainer')} />
</div> </div>
<input <input
disabled={isDisabled} disabled={isDisabled}
@@ -779,9 +785,11 @@
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<div class="flex-col"> <div class="flex-col">
<label for="publishDirectory" class="pt-2 text-base font-bold text-stone-100" <label for="publishDirectory" class="pt-2 text-base font-bold text-stone-100"
>{$t('forms.publish_directory')}</label >{$t('forms.publish_directory')}
<DocLink
explanation={"Directory containing all the assets for deployment. <br> For example: <span class='text-settings font-bold'>dist</span>,<span class='text-settings font-bold'>_site</span> or <span class='text-settings font-bold'>public</span>."}
/></label
> >
<Explainer text={$t('application.publish_directory_explainer')} />
</div> </div>
<input <input
@@ -804,6 +812,7 @@
{#if !application.settings.isPublicRepository} {#if !application.settings.isPublicRepository}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<Setting <Setting
id="autodeploy"
isCenter={false} isCenter={false}
bind:setting={autodeploy} bind:setting={autodeploy}
on:click={() => changeSettings('autodeploy')} on:click={() => changeSettings('autodeploy')}
@@ -815,6 +824,7 @@
{#if !application.settings.isBot} {#if !application.settings.isBot}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<Setting <Setting
id="previews"
isCenter={false} isCenter={false}
bind:setting={previews} bind:setting={previews}
on:click={() => changeSettings('previews')} on:click={() => changeSettings('previews')}
@@ -825,6 +835,7 @@
{/if} {/if}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<Setting <Setting
id="debug"
isCenter={false} isCenter={false}
bind:setting={debug} bind:setting={debug}
on:click={() => changeSettings('debug')} on:click={() => changeSettings('debug')}

View File

@@ -11,6 +11,7 @@
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import LoadingLogs from '$lib/components/LoadingLogs.svelte'; import LoadingLogs from '$lib/components/LoadingLogs.svelte';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import Tooltip from '$lib/components/Tooltip.svelte';
let logs: any = []; let logs: any = [];
let loading = true; let loading = true;
@@ -108,9 +109,9 @@
{:else} {:else}
<div class="flex justify-end sticky top-0 p-2 mx-1"> <div class="flex justify-end sticky top-0 p-2 mx-1">
<button <button
id="follow"
on:click={followBuild} on:click={followBuild}
class="bg-transparent btn btn-sm btn-link tooltip tooltip-primary tooltip-bottom hover:text-green-500 hover:bg-coolgray-500" class="bg-transparent btn btn-sm btn-linkhover:text-green-500 hover:bg-coolgray-500"
data-tip="Follow logs"
class:text-green-500={followingBuild} class:text-green-500={followingBuild}
> >
<svg <svg
@@ -130,12 +131,13 @@
<line x1="16" y1="12" x2="12" y2="16" /> <line x1="16" y1="12" x2="12" y2="16" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#follow">Follow Logs</Tooltip>
{#if currentStatus === 'running'} {#if currentStatus === 'running'}
<button <button
id="cancel"
on:click={cancelBuild} on:click={cancelBuild}
class:animation-spin={cancelInprogress} class:animation-spin={cancelInprogress}
class="bg-transparent btn btn-sm btn-link hover:text-red-500 hover:bg-coolgray-500 tooltip tooltip-primary tooltip-bottom" class="bg-transparent btn btn-sm btn-link hover:text-red-500 hover:bg-coolgray-500"
data-tip="Cancel build"
> >
{#if cancelInprogress} {#if cancelInprogress}
Cancelling... Cancelling...
@@ -156,6 +158,7 @@
</svg> </svg>
{/if} {/if}
</button> </button>
<Tooltip triggeredBy="#cancel">Cancel build</Tooltip>
{/if} {/if}
</div> </div>
{#if logs.length > 0} {#if logs.length > 0}

View File

@@ -5,6 +5,7 @@
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import LoadingLogs from '$lib/components/LoadingLogs.svelte'; import LoadingLogs from '$lib/components/LoadingLogs.svelte';
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from 'svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
let application: any = {}; let application: any = {};
let logsLoading = false; let logsLoading = false;
@@ -146,9 +147,9 @@
{/if} {/if}
<div class="flex justify-end sticky top-0 p-1 mx-1"> <div class="flex justify-end sticky top-0 p-1 mx-1">
<button <button
id="follow"
on:click={followBuild} on:click={followBuild}
class="bg-transparent btn btn-sm btn-link tooltip tooltip-primary tooltip-bottom" class="bg-transparent btn btn-sm btn-link"
data-tip="Follow logs"
class:text-green-500={followingLogs} class:text-green-500={followingLogs}
> >
<svg <svg
@@ -168,6 +169,7 @@
<line x1="16" y1="12" x2="12" y2="16" /> <line x1="16" y1="12" x2="12" y2="16" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#follow">Follow Logs</Tooltip>
</div> </div>
<div <div
class="font-mono w-full leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200" class="font-mono w-full leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200"

View File

@@ -61,6 +61,7 @@
import { appSession, status, disabledButton } from '$lib/store'; import { appSession, status, disabledButton } from '$lib/store';
import DeleteIcon from '$lib/components/DeleteIcon.svelte'; import DeleteIcon from '$lib/components/DeleteIcon.svelte';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
const { id } = $page.params; const { id } = $page.params;
let statusInterval: any = false; let statusInterval: any = false;
@@ -143,9 +144,9 @@
{#if database.type && database.destinationDockerId && database.version && database.defaultDatabase} {#if database.type && database.destinationDockerId && database.version && database.defaultDatabase}
{#if $status.database.isExited} {#if $status.database.isExited}
<a <a
id="exited"
href={!$disabledButton ? `/databases/${id}/logs` : null} href={!$disabledButton ? `/databases/${id}/logs` : null}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center text-red-500 tooltip-error" class="icons bg-transparent text-sm flex items-center text-red-500 tooltip-error"
data-tip="Service exited with an error!"
sveltekit:prefetch sveltekit:prefetch
> >
<svg <svg
@@ -166,10 +167,11 @@
<line x1="12" y1="16" x2="12.01" y2="16" /> <line x1="12" y1="16" x2="12.01" y2="16" />
</svg> </svg>
</a> </a>
<Tooltip triggeredBy="#exited">{'Service exited with an error!'}</Tooltip>
{/if} {/if}
{#if $status.database.initialLoading} {#if $status.database.initialLoading}
<button <button
class="icons tooltip-bottom flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out" class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -192,13 +194,11 @@
</button> </button>
{:else if $status.database.isRunning} {:else if $status.database.isRunning}
<button <button
id="stop"
on:click={stopDatabase} on:click={stopDatabase}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class="icons bg-transparent tooltip tooltip-bottom text-sm flex items-center space-x-2 text-red-500" class="icons bg-transparent text-sm flex items-center space-x-2 text-red-500"
data-tip={$appSession.isAdmin
? $t('database.stop_database')
: $t('database.permission_denied_stop_database')}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -215,15 +215,14 @@
<rect x="14" y="5" width="4" height="14" rx="1" /> <rect x="14" y="5" width="4" height="14" rx="1" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#stop">{'Stop'}</Tooltip>
{:else} {:else}
<button <button
id="start"
on:click={startDatabase} on:click={startDatabase}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2 text-green-500" class="icons bg-transparent text-sm flex items-center space-x-2 text-green-500"
data-tip={$appSession.isAdmin
? $t('database.start_database')
: $t('database.permission_denied_start_database')}
><svg ><svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6" class="w-6 h-6"
@@ -238,20 +237,19 @@
<path d="M7 4v16l13 -8z" /> <path d="M7 4v16l13 -8z" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#start">{'Start'}</Tooltip>
{/if} {/if}
{/if} {/if}
<div class="border border-stone-700 h-8" /> <div class="border border-stone-700 h-8" />
<a <a
id="configuration"
href="/databases/{id}" href="/databases/{id}"
sveltekit:prefetch sveltekit:prefetch
class="hover:text-yellow-500 rounded" class="hover:text-yellow-500 rounded"
class:text-yellow-500={$page.url.pathname === `/databases/${id}`} class:text-yellow-500={$page.url.pathname === `/databases/${id}`}
class:bg-coolgray-500={$page.url.pathname === `/databases/${id}`} class:bg-coolgray-500={$page.url.pathname === `/databases/${id}`}
> >
<button <button class="icons bg-transparent m text-sm disabled:text-red-500">
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm disabled:text-red-500"
data-tip={$t('application.configurations')}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@@ -275,19 +273,17 @@
</svg></button </svg></button
></a ></a
> >
<Tooltip triggeredBy="#configuration">{'Configuration'}</Tooltip>
<div class="border border-stone-700 h-8" /> <div class="border border-stone-700 h-8" />
<a <a
id="databaselogs"
href={$status.database.isRunning ? `/databases/${id}/logs` : null} href={$status.database.isRunning ? `/databases/${id}/logs` : null}
sveltekit:prefetch sveltekit:prefetch
class="hover:text-pink-500 rounded" class="hover:text-pink-500 rounded"
class:text-pink-500={$page.url.pathname === `/databases/${id}/logs`} class:text-pink-500={$page.url.pathname === `/databases/${id}/logs`}
class:bg-coolgray-500={$page.url.pathname === `/databases/${id}/logs`} class:bg-coolgray-500={$page.url.pathname === `/databases/${id}/logs`}
> >
<button <button disabled={!$status.database.isRunning} class="icons bg-transparent text-sm">
disabled={!$status.database.isRunning}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
data-tip={$t('database.logs')}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@@ -307,16 +303,16 @@
</svg></button </svg></button
></a ></a
> >
<Tooltip triggeredBy="#databaselogs">{'Logs'}</Tooltip>
<button <button
id="delete"
on:click={deleteDatabase} on:click={deleteDatabase}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin} class:hover:text-red-500={$appSession.isAdmin}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm" class="icons bg-transparent text-sm"><DeleteIcon /></button
data-tip={$appSession.isAdmin
? $t('database.delete_database')
: $t('database.permission_denied_delete_database')}><DeleteIcon /></button
> >
<Tooltip triggeredBy="#delete">{'Delete'}</Tooltip>
</nav> </nav>
{/if} {/if}
<slot /> <slot />

View File

@@ -6,6 +6,7 @@
import { get } from '$lib/api'; import { get } from '$lib/api';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import Tooltip from '$lib/components/Tooltip.svelte';
const { id } = $page.params; const { id } = $page.params;
@@ -129,9 +130,9 @@
{/if} {/if}
<div class="flex justify-end sticky top-0 p-1 mx-1"> <div class="flex justify-end sticky top-0 p-1 mx-1">
<button <button
id="follow"
on:click={followBuild} on:click={followBuild}
class="bg-transparent btn btn-sm tooltip tooltip-primary tooltip-bottom" class="bg-transparent btn btn-sm"
data-tip="Follow logs"
class:text-green-500={followingLogs} class:text-green-500={followingLogs}
> >
<svg <svg
@@ -151,6 +152,7 @@
<line x1="16" y1="12" x2="12" y2="16" /> <line x1="16" y1="12" x2="12" y2="16" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#follow">Follow Logs</Tooltip>
</div> </div>
<div <div
class="font-mono w-full leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200" class="font-mono w-full leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200"

View File

@@ -196,14 +196,15 @@
/> />
</div> </div>
{#if $appSession.teamId === '0'} {#if $appSession.teamId === '0'}
<div class="grid lg:grid-cols-2 items-center"> <div class="grid lg:grid-cols-2 items-center px-10">
<Setting <Setting
id="changeProxySetting"
loading={loading.proxy} loading={loading.proxy}
disabled={cannotDisable} disabled={cannotDisable}
bind:setting={destination.isCoolifyProxyUsed} bind:setting={destination.isCoolifyProxyUsed}
on:click={changeProxySetting} on:click={changeProxySetting}
title={$t('destination.use_coolify_proxy')} title={$t('destination.use_coolify_proxy')}
description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration. Databases will have their own proxy. <br><br>${ description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration.${
cannotDisable cannotDisable
? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>' ? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>'
: '' : ''

View File

@@ -33,11 +33,7 @@
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4"> <form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
<div class="flex items-center space-x-2 pb-5"> <div class="flex items-center space-x-2 pb-5">
<div class="title font-bold">{$t('forms.configuration')}</div> <div class="title font-bold">{$t('forms.configuration')}</div>
<button <button type="submit" class="btn btn-sm bg-destinations" class:loading disabled={loading}
type="submit"
class="btn btn-sm bg-destinations"
class:loading={loading}
disabled={loading}
>{loading >{loading
? payload.isCoolifyProxyUsed ? payload.isCoolifyProxyUsed
? $t('destination.new.saving_and_configuring_proxy') ? $t('destination.new.saving_and_configuring_proxy')
@@ -69,12 +65,13 @@
/> />
</div> </div>
{#if $appSession.teamId === '0'} {#if $appSession.teamId === '0'}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center px-10">
<Setting <Setting
id="changeProxySetting"
bind:setting={payload.isCoolifyProxyUsed} bind:setting={payload.isCoolifyProxyUsed}
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)} on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
title={$t('destination.use_coolify_proxy')} title={$t('destination.use_coolify_proxy')}
description={$t('destination.new.install_proxy')} description={'This will install a proxy on the destination to allow you to access your applications and services without any manual configuration.'}
/> />
</div> </div>
{/if} {/if}

View File

@@ -33,18 +33,14 @@
customClass="max-w-[32rem]" customClass="max-w-[32rem]"
text="Remote Docker Engines are using <span class='text-white font-bold'>SSH</span> to communicate with the remote docker engine. text="Remote Docker Engines are using <span class='text-white font-bold'>SSH</span> to communicate with the remote docker engine.
You need to setup an <span class='text-white font-bold'>SSH key</span> in advance on the server and install Docker. You need to setup an <span class='text-white font-bold'>SSH key</span> in advance on the server and install Docker.
<br>See <a class='text-white' href='https://docs.coollabs.io/coolify/destinations#remote-docker-engine'>docs</a> for more details." <br>See <a class='text-white' href='https://docs.coollabs.io/coolify/destinations#remote-docker-engine' target='blank'>docs</a> for more details."
/> />
</div> </div>
<div class="flex justify-center px-6 pb-8"> <div class="flex justify-center px-6 pb-8">
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4"> <form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
<div class="flex items-center space-x-2 pb-5"> <div class="flex items-center space-x-2 pb-5">
<div class="title font-bold">{$t('forms.configuration')}</div> <div class="title font-bold">{$t('forms.configuration')}</div>
<button <button type="submit" class="btn btn-sm bg-destinations" class:loading disabled={loading}
type="submit"
class="btn btn-sm bg-destinations"
class:loading={loading}
disabled={loading}
>{loading >{loading
? payload.isCoolifyProxyUsed ? payload.isCoolifyProxyUsed
? $t('destination.new.saving_and_configuring_proxy') ? $t('destination.new.saving_and_configuring_proxy')
@@ -97,12 +93,13 @@
bind:value={payload.network} bind:value={payload.network}
/> />
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center px-10">
<Setting <Setting
id="isCoolifyProxyUsed"
bind:setting={payload.isCoolifyProxyUsed} bind:setting={payload.isCoolifyProxyUsed}
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)} on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
title={$t('destination.use_coolify_proxy')} title={$t('destination.use_coolify_proxy')}
description={$t('destination.new.install_proxy')} description={'This will install a proxy on the destination to allow you to access your applications and services without any manual configuration.'}
/> />
</div> </div>
</form> </form>

View File

@@ -259,14 +259,15 @@
/></a /></a
> >
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center px-10">
<Setting <Setting
id="changeProxySetting"
disabled={cannotDisable} disabled={cannotDisable}
loading={loading.proxy} loading={loading.proxy}
bind:setting={destination.isCoolifyProxyUsed} bind:setting={destination.isCoolifyProxyUsed}
on:click={changeProxySetting} on:click={changeProxySetting}
title={$t('destination.use_coolify_proxy')} title={$t('destination.use_coolify_proxy')}
description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration. Databases will have their own proxy. <br><br>${ description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration.${
cannotDisable cannotDisable
? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>' ? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>'
: '' : ''

View File

@@ -40,7 +40,7 @@
} }
}; };
} catch (error) { } catch (error) {
console.log(error) console.log(error);
return handlerNotFoundLoad(error, url); return handlerNotFoundLoad(error, url);
} }
}; };
@@ -56,12 +56,16 @@
import { errorNotification, handlerNotFoundLoad } from '$lib/common'; import { errorNotification, handlerNotFoundLoad } from '$lib/common';
import { appSession } from '$lib/store'; import { appSession } from '$lib/store';
import DeleteIcon from '$lib/components/DeleteIcon.svelte'; import DeleteIcon from '$lib/components/DeleteIcon.svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
const { id } = $page.params; const { id } = $page.params;
const isDestinationDeletable = destination?.application.length === 0 && destination?.database.length === 0 && destination?.service.length === 0 const isDestinationDeletable =
destination?.application.length === 0 &&
destination?.database.length === 0 &&
destination?.service.length === 0;
async function deleteDestination(destination: any) { async function deleteDestination(destination: any) {
if (!isDestinationDeletable) return if (!isDestinationDeletable) return;
const sure = confirm($t('application.confirm_to_delete', { name: destination.name })); const sure = confirm($t('application.confirm_to_delete', { name: destination.name }));
if (sure) { if (sure) {
try { try {
@@ -74,12 +78,12 @@
} }
function deletable() { function deletable() {
if (!isDestinationDeletable) { if (!isDestinationDeletable) {
return "Please delete all resources before deleting this." return 'Please delete all resources before deleting this.';
} }
if ($appSession.isAdmin) { if ($appSession.isAdmin) {
return $t('destination.delete_destination') return $t('destination.delete_destination');
} else { } else {
return $t('destination.permission_denied_delete_destination') return $t('destination.permission_denied_delete_destination');
} }
} }
</script> </script>
@@ -87,14 +91,15 @@
{#if id !== 'new'} {#if id !== 'new'}
<nav class="nav-side"> <nav class="nav-side">
<button <button
id="delete"
on:click={() => deleteDestination(destination)} on:click={() => deleteDestination(destination)}
type="submit" type="submit"
disabled={!$appSession.isAdmin && isDestinationDeletable} disabled={!$appSession.isAdmin && isDestinationDeletable}
class:hover:text-red-500={$appSession.isAdmin && isDestinationDeletable} class:hover:text-red-500={$appSession.isAdmin && isDestinationDeletable}
class="icons tooltip tooltip-primary tooltip-left bg-transparent text-sm" class="icons bg-transparent text-sm"
class:text-stone-600={!isDestinationDeletable} class:text-stone-600={!isDestinationDeletable}><DeleteIcon /></button
data-tip={deletable()}><DeleteIcon /></button
> >
</nav> </nav>
<Tooltip triggeredBy="#delete">{deletable()}</Tooltip>
{/if} {/if}
<slot /> <slot />

View File

@@ -35,6 +35,7 @@
import DeleteIcon from '$lib/components/DeleteIcon.svelte'; import DeleteIcon from '$lib/components/DeleteIcon.svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Tooltip from '$lib/components/Tooltip.svelte';
const { id } = $page.params; const { id } = $page.params;
async function deleteTeam() { async function deleteTeam() {
@@ -69,15 +70,14 @@
<nav class="nav-side"> <nav class="nav-side">
{#if team.id !== '0'} {#if team.id !== '0'}
<button <button
id="delete"
on:click={deleteTeam} on:click={deleteTeam}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin} class:hover:text-red-500={$appSession.isAdmin}
class="icons tooltip tooltip-primary tooltip-left bg-transparent text-sm" class="icons bg-transparent text-sm"><DeleteIcon /></button
data-tip={$appSession.isAdmin
? 'Delete'
: $t('destination.permission_denied_delete_destination')}><DeleteIcon /></button
> >
<Tooltip triggeredBy="#delete">Delete</Tooltip>
{/if} {/if}
</nav> </nav>
{/if} {/if}

View File

@@ -62,6 +62,7 @@
import { errorNotification, handlerNotFoundLoad } from '$lib/common'; import { errorNotification, handlerNotFoundLoad } from '$lib/common';
import { appSession, disabledButton, status, location, setLocation } from '$lib/store'; import { appSession, disabledButton, status, location, setLocation } from '$lib/store';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
const { id } = $page.params; const { id } = $page.params;
export let service: any; export let service: any;
@@ -108,7 +109,7 @@
} }
async function startService() { async function startService() {
$status.service.initialLoading = true; $status.service.initialLoading = true;
$status.service.loading = true $status.service.loading = true;
try { try {
await post(`/services/${service.id}/${service.type}/start`, {}); await post(`/services/${service.id}/${service.type}/start`, {});
} catch (error) { } catch (error) {
@@ -152,9 +153,10 @@
{#if service.type && service.destinationDockerId && service.version} {#if service.type && service.destinationDockerId && service.version}
{#if $location} {#if $location}
<a <a
id="open"
href={$location} href={$location}
target="_blank" target="_blank"
class="icons tooltip-bottom flex items-center bg-transparent text-sm" class="icons flex items-center bg-transparent text-sm"
><svg ><svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@@ -171,13 +173,14 @@
<polyline points="15 4 20 4 20 9" /> <polyline points="15 4 20 4 20 9" />
</svg></a </svg></a
> >
<Tooltip triggeredBy="#open">Open</Tooltip>
<div class="border border-stone-700 h-8" /> <div class="border border-stone-700 h-8" />
{/if} {/if}
{#if $status.service.isExited} {#if $status.service.isExited}
<a <a
id="error"
href={!$disabledButton ? `/services/${id}/logs` : null} href={!$disabledButton ? `/services/${id}/logs` : null}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center text-red-500 tooltip-error" class="icons bg-transparent text-sm flex items-center text-red-500 tooltip-error"
data-tip="Service exited with an error!"
sveltekit:prefetch sveltekit:prefetch
> >
<svg <svg
@@ -198,10 +201,11 @@
<line x1="12" y1="16" x2="12.01" y2="16" /> <line x1="12" y1="16" x2="12.01" y2="16" />
</svg> </svg>
</a> </a>
<Tooltip triggeredBy="#error">Service exited with an error!</Tooltip>
{/if} {/if}
{#if $status.service.initialLoading} {#if $status.service.initialLoading}
<button <button
class="icons tooltip-bottom flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out" class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -224,13 +228,11 @@
</button> </button>
{:else if $status.service.isRunning} {:else if $status.service.isRunning}
<button <button
id="stop"
on:click={stopService} on:click={stopService}
type="submit" type="submit"
disabled={$disabledButton} disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2 text-red-500" class="icons bg-transparent text-sm flex items-center space-x-2 text-red-500"
data-tip={$appSession.isAdmin
? $t('service.stop_service')
: $t('service.permission_denied_stop_service')}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -247,15 +249,14 @@
<rect x="14" y="5" width="4" height="14" rx="1" /> <rect x="14" y="5" width="4" height="14" rx="1" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#stop">Stop</Tooltip>
{:else} {:else}
<button <button
id="start"
on:click={startService} on:click={startService}
type="submit" type="submit"
disabled={$disabledButton} disabled={$disabledButton}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm flex items-center space-x-2 text-green-500" class="icons bg-transparent text-sm flex items-center space-x-2 text-green-500"
data-tip={$appSession.isAdmin
? $t('service.start_service')
: $t('service.permission_denied_start_service')}
><svg ><svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6" class="w-6 h-6"
@@ -270,21 +271,20 @@
<path d="M7 4v16l13 -8z" /> <path d="M7 4v16l13 -8z" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#start">Start</Tooltip>
{/if} {/if}
<div class="border border-stone-700 h-8" /> <div class="border border-stone-700 h-8" />
{/if} {/if}
{#if service.type && service.destinationDockerId && service.version} {#if service.type && service.destinationDockerId && service.version}
<a <a
id="configuration"
href="/services/{id}" href="/services/{id}"
sveltekit:prefetch sveltekit:prefetch
class="hover:text-yellow-500 rounded" class="hover:text-yellow-500 rounded"
class:text-yellow-500={$page.url.pathname === `/services/${id}`} class:text-yellow-500={$page.url.pathname === `/services/${id}`}
class:bg-coolgray-500={$page.url.pathname === `/services/${id}`} class:bg-coolgray-500={$page.url.pathname === `/services/${id}`}
> >
<button <button class="icons bg-transparent text-sm disabled:text-red-500">
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm disabled:text-red-500"
data-tip={$t('application.configurations')}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@@ -308,17 +308,16 @@
</svg></button </svg></button
></a ></a
> >
<Tooltip triggeredBy="#configuration">Configuration</Tooltip>
<a <a
id="secrets"
href="/services/{id}/secrets" href="/services/{id}/secrets"
sveltekit:prefetch sveltekit:prefetch
class="hover:text-pink-500 rounded" class="hover:text-pink-500 rounded"
class:text-pink-500={$page.url.pathname === `/services/${id}/secrets`} class:text-pink-500={$page.url.pathname === `/services/${id}/secrets`}
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/secrets`} class:bg-coolgray-500={$page.url.pathname === `/services/${id}/secrets`}
> >
<button <button class="icons bg-transparent text-sm disabled:text-red-500">
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm disabled:text-red-500"
data-tip={$t('application.secret')}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6" class="w-6 h-6"
@@ -338,17 +337,16 @@
</svg></button </svg></button
></a ></a
> >
<Tooltip triggeredBy="#secrets">Secrets</Tooltip>
<a <a
id="persistentstorage"
href="/services/{id}/storages" href="/services/{id}/storages"
sveltekit:prefetch sveltekit:prefetch
class="hover:text-pink-500 rounded" class="hover:text-pink-500 rounded"
class:text-pink-500={$page.url.pathname === `/services/${id}/storages`} class:text-pink-500={$page.url.pathname === `/services/${id}/storages`}
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/storages`} class:bg-coolgray-500={$page.url.pathname === `/services/${id}/storages`}
> >
<button <button class="icons bg-transparent text-sm disabled:text-red-500">
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm disabled:text-red-500"
data-tip="Persistent Storage"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6" class="w-6 h-6"
@@ -366,19 +364,17 @@
</svg> </svg>
</button></a </button></a
> >
<Tooltip triggeredBy="#persistentstorage">Persistent Storage</Tooltip>
<div class="border border-stone-700 h-8" /> <div class="border border-stone-700 h-8" />
<a <a
id="logs"
href={!$disabledButton && $status.service.isRunning ? `/services/${id}/logs` : null} href={!$disabledButton && $status.service.isRunning ? `/services/${id}/logs` : null}
sveltekit:prefetch sveltekit:prefetch
class="hover:text-pink-500 rounded" class="hover:text-pink-500 rounded"
class:text-pink-500={$page.url.pathname === `/services/${id}/logs`} class:text-pink-500={$page.url.pathname === `/services/${id}/logs`}
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/logs`} class:bg-coolgray-500={$page.url.pathname === `/services/${id}/logs`}
> >
<button <button disabled={!$status.service.isRunning} class="icons bg-transparent text-sm">
disabled={!$status.service.isRunning}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm"
data-tip={$t('service.logs')}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@@ -398,16 +394,16 @@
</svg></button </svg></button
></a ></a
> >
<Tooltip triggeredBy="#logs">Logs</Tooltip>
{/if} {/if}
<button <button
id="delete"
on:click={deleteService} on:click={deleteService}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin} class:hover:text-red-500={$appSession.isAdmin}
class="icons bg-transparent tooltip tooltip-primary tooltip-bottom text-sm" class="icons bg-transparent text-sm"><DeleteIcon /></button
data-tip={$appSession.isAdmin
? $t('service.delete_service')
: $t('service.permission_denied_delete_service')}><DeleteIcon /></button
> >
<Tooltip triggeredBy="#delete">Delete</Tooltip>
</nav> </nav>
<slot /> <slot />

View File

@@ -5,6 +5,7 @@
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
let service: any = {}; let service: any = {};
let logsLoading = false; let logsLoading = false;
@@ -126,9 +127,9 @@
{/if} {/if}
<div class="flex justify-end sticky top-0 p-1 mx-1"> <div class="flex justify-end sticky top-0 p-1 mx-1">
<button <button
id="follow"
on:click={followBuild} on:click={followBuild}
class="bg-transparent btn btn-sm tooltip tooltip-primary tooltip-bottom" class="bg-transparent btn btn-sm"
data-tip="Follow logs"
class:text-green-500={followingLogs} class:text-green-500={followingLogs}
> >
<svg <svg
@@ -148,6 +149,7 @@
<line x1="16" y1="12" x2="12" y2="16" /> <line x1="16" y1="12" x2="12" y2="16" />
</svg> </svg>
</button> </button>
<Tooltip triggeredBy="#follow">Follow Logs</Tooltip>
</div> </div>
<div <div
class="font-mono w-full leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200" class="font-mono w-full leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200"

View File

@@ -26,6 +26,7 @@
import { addToast, appSession, features } from '$lib/store'; import { addToast, appSession, features } from '$lib/store';
import { errorNotification, getDomain } from '$lib/common'; import { errorNotification, getDomain } from '$lib/common';
import Menu from './_Menu.svelte'; import Menu from './_Menu.svelte';
import DocLink from '$lib/components/DocLink.svelte';
let isRegistrationEnabled = settings.isRegistrationEnabled; let isRegistrationEnabled = settings.isRegistrationEnabled;
let dualCerts = settings.dualCerts; let dualCerts = settings.dualCerts;
@@ -195,8 +196,8 @@
<div class="flex-col"> <div class="flex-col">
<div class="pt-2 text-base font-bold text-stone-100"> <div class="pt-2 text-base font-bold text-stone-100">
{$t('application.url_fqdn')} {$t('application.url_fqdn')}
<DocLink explanation={$t('setting.ssl_explainer')} />
</div> </div>
<Explainer text={$t('setting.ssl_explainer')} />
</div> </div>
<div class="justify-start text-left"> <div class="justify-start text-left">
<input <input
@@ -250,8 +251,8 @@
<div class="flex-col"> <div class="flex-col">
<div class="pt-2 text-base font-bold text-stone-100"> <div class="pt-2 text-base font-bold text-stone-100">
{$t('forms.public_port_range')} {$t('forms.public_port_range')}
<DocLink explanation={$t('forms.public_port_range_explainer')} />
</div> </div>
<Explainer text={$t('forms.public_port_range_explainer')} />
</div> </div>
<div class="mx-auto flex-row items-center justify-center space-y-2"> <div class="mx-auto flex-row items-center justify-center space-y-2">
<input <input
@@ -273,6 +274,7 @@
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<Setting <Setting
id="isDNSCheckEnabled"
bind:setting={isDNSCheckEnabled} bind:setting={isDNSCheckEnabled}
title={$t('setting.is_dns_check_enabled')} title={$t('setting.is_dns_check_enabled')}
description={$t('setting.is_dns_check_enabled_explainer')} description={$t('setting.is_dns_check_enabled_explainer')}
@@ -280,18 +282,19 @@
/> />
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<div class="flex-col"> <div class="text-base font-bold text-stone-100">
<div class="pt-2 text-base font-bold text-stone-100"> Custom DNS servers <DocLink
Custom DNS servers 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> </div>
<Explainer text="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> <div class="flex-row items-center justify-center">
<div class="mx-auto flex-row items-center justify-center space-y-2">
<input placeholder="1.1.1.1,8.8.8.8" bind:value={DNSServers} /> <input placeholder="1.1.1.1,8.8.8.8" bind:value={DNSServers} />
</div> </div>
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<Setting <Setting
id="dualCerts"
dataTooltip={$t('setting.must_remove_domain_before_changing')} dataTooltip={$t('setting.must_remove_domain_before_changing')}
disabled={isFqdnSet} disabled={isFqdnSet}
bind:setting={dualCerts} bind:setting={dualCerts}

View File

@@ -8,6 +8,7 @@
import { dashify, errorNotification, getDomain } from '$lib/common'; import { dashify, errorNotification, getDomain } from '$lib/common';
import { addToast, appSession } from '$lib/store'; import { addToast, appSession } from '$lib/store';
import { dev } from '$app/env'; import { dev } from '$app/env';
import DocLink from '$lib/components/DocLink.svelte';
const { id } = $page.params; const { id } = $page.params;
@@ -115,7 +116,9 @@
<input name="apiUrl" id="apiUrl" required bind:value={source.apiUrl} /> <input name="apiUrl" id="apiUrl" required bind:value={source.apiUrl} />
</div> </div>
<div class="grid lg:grid-cols-2 items-center"> <div class="grid lg:grid-cols-2 items-center">
<label for="customPort" class="text-base font-bold text-stone-100">Custom SSH Port</label> <label for="customPort" class="text-base font-bold text-stone-100">Custom SSH Port <DocLink
explanation={"If you use a self-hosted version of Git, you can provide custom port for all the Git related actions."}
/></label>
<input <input
name="customPort" name="customPort"
id="customPort" id="customPort"
@@ -124,18 +127,15 @@
required required
value={source.customPort} value={source.customPort}
/> />
<Explainer
text="If you use a self-hosted version of Git, you can provide custom port for all the Git related actions."
/>
</div> </div>
<div class="grid lg:grid-cols-2"> <div class="grid lg:grid-cols-2">
<div class="flex flex-col"> <div class="flex flex-col">
<label for="organization" class="pt-2 text-base font-bold text-stone-100" <label for="organization" class="pt-2 text-base font-bold text-stone-100"
>Organization</label >Organization
<DocLink
explanation={"Fill it if you would like to use an organization's as your Git Source. Otherwise your user will be used."}
/></label
> >
<Explainer
text="Fill it if you would like to use an organization's as your Git Source. Otherwise your user will be used."
/>
</div> </div>
<input <input
name="organization" name="organization"

View File

@@ -11,6 +11,7 @@
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import { addToast, appSession } from '$lib/store'; import { addToast, appSession } from '$lib/store';
import DocLink from '$lib/components/DocLink.svelte';
const { id } = $page.params; const { id } = $page.params;
let url = settings.fqdn ? settings.fqdn : window.location.origin; let url = settings.fqdn ? settings.fqdn : window.location.origin;
@@ -148,10 +149,8 @@
<div class="flex space-x-1 pb-7"> <div class="flex space-x-1 pb-7">
<div class="title">General</div> <div class="title">General</div>
{#if $appSession.isAdmin} {#if $appSession.isAdmin}
<button <button type="submit" class="btn btn-sm bg-sources" disabled={loading}
type="submit" >{loading ? $t('forms.saving') : $t('forms.save')}</button
class="btn btn-sm bg-sources"
disabled={loading}>{loading ? $t('forms.saving') : $t('forms.save')}</button
> >
{#if source.gitlabAppId} {#if source.gitlabAppId}
<button class="btn btn-sm" on:click|preventDefault={changeSettings} <button class="btn btn-sm" on:click|preventDefault={changeSettings}
@@ -166,16 +165,12 @@
</div> </div>
<div class="grid grid-flow-row gap-2 px-10"> <div class="grid grid-flow-row gap-2 px-10">
{#if !source.gitlabAppId} {#if !source.gitlabAppId}
<Explainer <a
customClass="w-full" href="https://docs.coollabs.io/coolify/sources#how-to-integrate-with-gitlab"
text="<span class='font-bold text-base text-white'>Scopes required:</span> class="font-bold "
<br>- <span class='text-sources font-bold'>api</span> (Access the authenticated user's API) target="_blank"
<br>- <span class='text-sources font-bold'>read_repository</span> (Allows read-only access to the repository) rel="noopener noreferrer">Documentation and detailed instructions.</a
<br>- <span class='text-sources font-bold'>email</span> (Allows read-only access to the user's primary email address using OpenID Connect) >
<br>
<br>For extra security, you can set <span class='text-sources font-bold'>Expire Access Tokens</span>
<br><br>Webhook URL: <span class='text-sources font-bold'>{url}/webhooks/gitlab</span>"
/>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="type" class="text-base font-bold text-stone-100">Application Type</label> <label for="type" class="text-base font-bold text-stone-100">Application Type</label>
<select name="type" id="type" class="w-96" bind:value={applicationType}> <select name="type" id="type" class="w-96" bind:value={applicationType}>
@@ -245,7 +240,11 @@
</div> </div>
{#if selfHosted} {#if selfHosted}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="customPort" class="text-base font-bold text-stone-100">Custom SSH Port</label> <label for="customPort" class="text-base font-bold text-stone-100"
>Custom SSH Port <DocLink
explanation={'If you use a self-hosted version of Git, you can provide custom port for all the Git related actions.'}
/></label
>
<input <input
name="customPort" name="customPort"
id="customPort" id="customPort"
@@ -254,19 +253,16 @@
required required
bind:value={source.customPort} bind:value={source.customPort}
/> />
<Explainer
text="If you use a self-hosted version of Git, you can provide custom port for all the Git related actions."
/>
</div> </div>
{/if} {/if}
<div class="grid grid-cols-2 items-start"> <div class="grid grid-cols-2 items-start">
<div class="flex-col"> <div class="flex-col">
<label for="oauthId" class="pt-2 text-base font-bold text-stone-100" <label for="oauthId" class="pt-2 text-base font-bold text-stone-100"
>{$t('source.oauth_id')}</label >{$t('source.oauth_id')}
>
{#if !source.gitlabAppId} {#if !source.gitlabAppId}
<Explainer text={$t('source.oauth_id_explainer')} /> <DocLink explanation={$t('source.oauth_id_explainer')} />
{/if} {/if}</label
>
</div> </div>
<input <input
disabled={source.gitlabAppId} disabled={source.gitlabAppId}

View File

@@ -37,6 +37,7 @@
import { appSession } from '$lib/store'; import { appSession } from '$lib/store';
import DeleteIcon from '$lib/components/DeleteIcon.svelte'; import DeleteIcon from '$lib/components/DeleteIcon.svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import Tooltip from '$lib/components/Tooltip.svelte';
const { id } = $page.params; const { id } = $page.params;
async function deleteSource(name: string) { async function deleteSource(name: string) {
@@ -55,15 +56,14 @@
{#if id !== 'new'} {#if id !== 'new'}
<nav class="nav-side"> <nav class="nav-side">
<button <button
id="delete"
on:click={() => deleteSource(source.name)} on:click={() => deleteSource(source.name)}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin} class:hover:text-red-500={$appSession.isAdmin}
class="icons tooltip tooltip-primary tooltip-bottom bg-transparent text-sm" class="icons bg-transparent text-sm"><DeleteIcon /></button
data-tip={$appSession.isAdmin
? $t('source.delete_git_source')
: $t('source.permission_denied')}><DeleteIcon /></button
> >
</nav> </nav>
<Tooltip triggeredBy="#delete">Delete</Tooltip>
{/if} {/if}
<slot /> <slot />

View File

@@ -1,7 +1,7 @@
const defaultTheme = require('tailwindcss/defaultTheme'); const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = { module.exports = {
content: ['./**/*.html', './src/**/*.{js,jsx,ts,tsx,svelte}'], content: ['./**/*.html', './src/**/*.{js,jsx,ts,tsx,svelte}', "./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",],
important: true, important: true,
daisyui: { daisyui: {
themes: [ themes: [
@@ -62,5 +62,6 @@ module.exports = {
scrollbar: ['dark'], scrollbar: ['dark'],
extend: {} extend: {}
}, },
darkMode: 'class',
plugins: [require('tailwindcss-scrollbar'), require('daisyui'), require("@tailwindcss/typography")] plugins: [require('tailwindcss-scrollbar'), require('daisyui'), require("@tailwindcss/typography")]
}; };

55
pnpm-lock.yaml generated
View File

@@ -131,7 +131,9 @@ importers:
apps/ui: apps/ui:
specifiers: specifiers:
'@floating-ui/dom': ^1.0.1
'@playwright/test': 1.25.1 '@playwright/test': 1.25.1
'@popperjs/core': ^2.11.6
'@sveltejs/adapter-static': 1.0.0-next.39 '@sveltejs/adapter-static': 1.0.0-next.39
'@sveltejs/kit': 1.0.0-next.405 '@sveltejs/kit': 1.0.0-next.405
'@tailwindcss/typography': ^0.5.4 '@tailwindcss/typography': ^0.5.4
@@ -139,11 +141,14 @@ importers:
'@typescript-eslint/eslint-plugin': 5.35.1 '@typescript-eslint/eslint-plugin': 5.35.1
'@typescript-eslint/parser': 5.35.1 '@typescript-eslint/parser': 5.35.1
autoprefixer: 10.4.8 autoprefixer: 10.4.8
classnames: ^2.3.1
cuid: 2.1.8 cuid: 2.1.8
daisyui: 2.24.0 daisyui: 2.24.0
eslint: 8.22.0 eslint: 8.22.0
eslint-config-prettier: 8.5.0 eslint-config-prettier: 8.5.0
eslint-plugin-svelte3: 4.0.0 eslint-plugin-svelte3: 4.0.0
flowbite: ^1.5.2
flowbite-svelte: ^0.26.2
js-cookie: 3.0.1 js-cookie: 3.0.1
p-limit: 4.0.0 p-limit: 4.0.0
postcss: 8.4.16 postcss: 8.4.16
@@ -169,15 +174,20 @@ importers:
svelte-select: 4.4.7 svelte-select: 4.4.7
sveltekit-i18n: 2.2.2_svelte@3.49.0 sveltekit-i18n: 2.2.2_svelte@3.49.0
devDependencies: devDependencies:
'@floating-ui/dom': 1.0.1
'@playwright/test': 1.25.1 '@playwright/test': 1.25.1
'@popperjs/core': 2.11.6
'@sveltejs/kit': 1.0.0-next.405_svelte@3.49.0+vite@3.0.5 '@sveltejs/kit': 1.0.0-next.405_svelte@3.49.0+vite@3.0.5
'@types/js-cookie': 3.0.2 '@types/js-cookie': 3.0.2
'@typescript-eslint/eslint-plugin': 5.35.1_ktjxjibzrfqejavile4bhmzhjq '@typescript-eslint/eslint-plugin': 5.35.1_ktjxjibzrfqejavile4bhmzhjq
'@typescript-eslint/parser': 5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq '@typescript-eslint/parser': 5.35.1_4rv7y5c6xz3vfxwhbrcxxi73bq
autoprefixer: 10.4.8_postcss@8.4.16 autoprefixer: 10.4.8_postcss@8.4.16
classnames: 2.3.1
eslint: 8.22.0 eslint: 8.22.0
eslint-config-prettier: 8.5.0_eslint@8.22.0 eslint-config-prettier: 8.5.0_eslint@8.22.0
eslint-plugin-svelte3: 4.0.0_laaqauvsmoyypsiqkozwyi2fn4 eslint-plugin-svelte3: 4.0.0_laaqauvsmoyypsiqkozwyi2fn4
flowbite: 1.5.2
flowbite-svelte: 0.26.2
postcss: 8.4.16 postcss: 8.4.16
prettier: 2.7.1 prettier: 2.7.1
prettier-plugin-svelte: 2.7.0_o3ioganyptcsrh6x4hnxvjkpqi prettier-plugin-svelte: 2.7.0_o3ioganyptcsrh6x4hnxvjkpqi
@@ -369,6 +379,16 @@ packages:
- supports-color - supports-color
dev: false dev: false
/@floating-ui/core/1.0.1:
resolution: {integrity: sha512-bO37brCPfteXQfFY0DyNDGB3+IMe4j150KFQcgJ5aBP295p9nBGeHEs/p0czrRbtlHq4Px/yoPXO/+dOCcF4uA==}
dev: true
/@floating-ui/dom/1.0.1:
resolution: {integrity: sha512-wBDiLUKWU8QNPNOTAFHiIAkBv1KlHauG2AhqjSeh2H+wR8PX+AArXfz8NkRexH5PgMJMmSOS70YS89AbWYh5dA==}
dependencies:
'@floating-ui/core': 1.0.1
dev: true
/@humanwhocodes/config-array/0.10.4: /@humanwhocodes/config-array/0.10.4:
resolution: {integrity: sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==} resolution: {integrity: sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==}
engines: {node: '>=10.10.0'} engines: {node: '>=10.10.0'}
@@ -458,6 +478,10 @@ packages:
playwright-core: 1.25.1 playwright-core: 1.25.1
dev: true dev: true
/@popperjs/core/2.11.6:
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
dev: true
/@prisma/client/3.15.2_prisma@3.15.2: /@prisma/client/3.15.2_prisma@3.15.2:
resolution: {integrity: sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==} resolution: {integrity: sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==}
engines: {node: '>=12.6'} engines: {node: '>=12.6'}
@@ -2000,6 +2024,10 @@ packages:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
dev: false dev: false
/classnames/2.3.1:
resolution: {integrity: sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==}
dev: true
/clean-stack/4.2.0: /clean-stack/4.2.0:
resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@@ -3461,6 +3489,24 @@ packages:
resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==} resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==}
dev: true dev: true
/flowbite-svelte/0.26.2:
resolution: {integrity: sha512-CpZ33fyMuzrYvECKN2rD5zNa/ZY0c4cDMcY6wE/hvajFybbCqwMYzd0AkA88zTNm5fknEN0/E23TVmD4Qkhzzw==}
engines: {node: '>=16.0.0', npm: '>=7.0.0'}
dependencies:
'@floating-ui/dom': 1.0.1
'@popperjs/core': 2.11.6
classnames: 2.3.1
flowbite: 1.5.2
svelte-heros: 2.3.5
dev: true
/flowbite/1.5.2:
resolution: {integrity: sha512-oSKhPkg0bYb4dZG4ypSh++dPrFM3IOSER6HOwJHYRFPW5u9PXL4AQFMQh7Kytgws2xrDQyl/zqefZmymeWt2IA==}
dependencies:
'@popperjs/core': 2.11.6
mini-svg-data-uri: 1.4.4
dev: true
/follow-redirects/1.15.0: /follow-redirects/1.15.0:
resolution: {integrity: sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==} resolution: {integrity: sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
@@ -4482,6 +4528,11 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true dev: true
/mini-svg-data-uri/1.4.4:
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
hasBin: true
dev: true
/minimalistic-assert/1.0.1: /minimalistic-assert/1.0.1:
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
dev: false dev: false
@@ -5856,6 +5907,10 @@ packages:
- sugarss - sugarss
dev: true dev: true
/svelte-heros/2.3.5:
resolution: {integrity: sha512-08PdccaeRPP1pVa90AGieTwGzrNtXpC1Fry+i95OTvcR3xbGRU/hxK4rnaFYvGgk1Pxj9YT6GKGTEX8uXE9XJQ==}
dev: true
/svelte-hmr/0.14.12_svelte@3.49.0: /svelte-hmr/0.14.12_svelte@3.49.0:
resolution: {integrity: sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==} resolution: {integrity: sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==}
engines: {node: ^12.20 || ^14.13.1 || >= 16} engines: {node: ^12.20 || ^14.13.1 || >= 16}