mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 16:02:14 +00:00
fix(frontend): improve centered page overflow (#7515)
* center page top when needed * remove debug message
This commit is contained in:
@@ -4,41 +4,60 @@
|
||||
import WindmillIcon from './icons/WindmillIcon.svelte'
|
||||
import LoginPageHeader from './LoginPageHeader.svelte'
|
||||
|
||||
export let subtitle: string | undefined = undefined
|
||||
export let title = 'Windmill'
|
||||
export let disableLogo = false
|
||||
export let large = false
|
||||
interface Props {
|
||||
subtitle?: string | undefined
|
||||
title?: string
|
||||
disableLogo?: boolean
|
||||
large?: boolean
|
||||
centerVertically?: boolean
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
subtitle = undefined,
|
||||
title = 'Windmill',
|
||||
disableLogo = false,
|
||||
large = false,
|
||||
centerVertically = true,
|
||||
children
|
||||
}: Props = $props()
|
||||
|
||||
setLicense()
|
||||
</script>
|
||||
|
||||
<div class="center-center min-h-screen p-4 relative bg-surface-secondary">
|
||||
<div class="flex flex-col gap-2 items-center w-full">
|
||||
<div
|
||||
class="flex justify-center h-screen p-4 relative bg-surface-secondary overflow-auto"
|
||||
class:items-center={centerVertically}
|
||||
style="scrollbar-gutter: stable both-edges;"
|
||||
>
|
||||
<div class="flex flex-col gap-2 items-center w-full pb-8 h-fit">
|
||||
{#if (!disableLogo && !$enterpriseLicense) || !$whitelabelNameStore}
|
||||
<div class="hidden lg:block">
|
||||
<div>
|
||||
<WindmillIcon height="100px" width="100px" spin="slow" />
|
||||
<WindmillIcon size={centerVertically ? 64 : 48} spin="slow" />
|
||||
</div>
|
||||
<h2 class="text-center py-2 text-primary">Windmill</h2>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="pt-8"></div>
|
||||
{/if}
|
||||
|
||||
<div class="mb-4">
|
||||
<h1 class="text-center text-lg text-emphasis font-semibold">
|
||||
{title}
|
||||
</h1>
|
||||
{#if subtitle}
|
||||
<p class="text-xs font-normal text-primary text-center mt-2">
|
||||
{subtitle}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-md bg-surface w-full {large
|
||||
? 'max-w-5xl'
|
||||
: 'max-w-[640px]'} p-4 sm:py-8 sm:px-10 mb-6 md:mb-20 z-10"
|
||||
: 'max-w-[640px]'} p-4 sm:py-8 sm:px-10 z-10"
|
||||
>
|
||||
<div class="mb-10">
|
||||
<h1 class="text-center text-lg text-emphasis font-semibold">
|
||||
{title}
|
||||
</h1>
|
||||
{#if subtitle}
|
||||
<p class="text-xs font-normal text-primary text-center mt-2">
|
||||
{subtitle}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@
|
||||
let domain = $derived($usersWorkspaceStore?.email.split('@')[1])
|
||||
</script>
|
||||
|
||||
<CenteredModal title="{isFork ? 'Forking' : 'New'} Workspace">
|
||||
<CenteredModal title="{isFork ? 'Forking' : 'New'} Workspace" centerVertically={false}>
|
||||
<div class="flex flex-col gap-8">
|
||||
{#if isFork}
|
||||
<div class="flex flex-block gap-2">
|
||||
@@ -593,7 +593,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-wrap flex-row justify-between pt-12 gap-4">
|
||||
<div class="flex flex-wrap flex-row justify-between gap-4 pt-4">
|
||||
<Button
|
||||
disabled={forkCreationLoading}
|
||||
variant="default"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
let saved = false
|
||||
</script>
|
||||
|
||||
<CenteredModal large title="Instance settings">
|
||||
<CenteredModal large title="Instance settings" centerVertically={false}>
|
||||
<InstanceSettings on:saved={() => (saved = true)} />
|
||||
<p class="text-secondary text-sm px-2 py-4">
|
||||
You can change these settings later in the instance settings but finishing setup will leave this
|
||||
|
||||
@@ -173,191 +173,129 @@
|
||||
<SuperadminSettings bind:this={superadminSettings} />
|
||||
{/if}
|
||||
|
||||
<CenteredModal title="Select a workspace" subtitle="Logged in as {$usersWorkspaceStore?.email}">
|
||||
<div class="flex flex-row items-center gap-2 justify-between mb-4">
|
||||
<h2 class="inline-flex gap-2 text-sm font-semibold text-emphasis flex-shrink-0">
|
||||
Workspaces{#if loading}<WindmillIcon spin="fast" />{/if}
|
||||
</h2>
|
||||
|
||||
{#if allWorkspaces.length > 1}
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="relative text-primary flex-1 max-w-48">
|
||||
<TextInput
|
||||
inputProps={{
|
||||
placeholder: 'Search workspaces...'
|
||||
}}
|
||||
size="sm"
|
||||
bind:value={workspaceSearchFilter}
|
||||
class="!pr-8"
|
||||
/>
|
||||
<Search size={14} class="text-secondary absolute right-2 top-0 mt-2" />
|
||||
</div>
|
||||
{#if workspaceHasForks}
|
||||
<Button
|
||||
onClick={() => workspaceExpandCollapseAll?.()}
|
||||
title={workspaceAllExpanded ? 'Collapse all' : 'Expand all'}
|
||||
startIcon={{ icon: workspaceAllExpanded ? ChevronsDownUp : ChevronsUpDown }}
|
||||
size="xs2"
|
||||
variant="default"
|
||||
>
|
||||
{workspaceAllExpanded ? 'Collapse' : 'Expand'}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if $superadmin}
|
||||
<div class="flex justify-end mb-2">
|
||||
<Toggle
|
||||
bind:checked={list_all_as_super_admin}
|
||||
options={{ right: 'List all workspaces as superadmin' }}
|
||||
size="xs"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if workspaces && $usersWorkspaceStore}
|
||||
{#if workspaces.length == 0}
|
||||
<p class="text-xs text-secondary mt-2">
|
||||
You are not a member of any workspace yet. Accept an invitation {#if createWorkspace}or
|
||||
create your own{/if}
|
||||
workspace.
|
||||
</p>
|
||||
{:else}
|
||||
<WorkspaceTreeView
|
||||
workspaces={allWorkspaces}
|
||||
onEnterWorkspace={speakFriendAndEnterWorkspace}
|
||||
onUnarchive={async (_workspaceId) => {
|
||||
if (list_all_as_super_admin) {
|
||||
loadWorkspacesAsAdmin()
|
||||
} else {
|
||||
loadWorkspaces()
|
||||
}
|
||||
}}
|
||||
bind:searchFilter={workspaceSearchFilter}
|
||||
bind:allExpanded={workspaceAllExpanded}
|
||||
bind:hasForks={workspaceHasForks}
|
||||
bind:this={workspaceTreeView}
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
{#each new Array(3) as _, i (i)}
|
||||
<Skeleton layout={[[2], 0.5]} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if createWorkspace}
|
||||
<div class="flex flex-row-reverse pt-4">
|
||||
<Button
|
||||
unifiedSize="sm"
|
||||
btnClasses={noWorkspaces ? 'animate-bounce hover:animate-none' : ''}
|
||||
href="{base}/user/create_workspace{rd ? `?rd=${encodeURIComponent(rd)}` : ''}"
|
||||
variant={noWorkspaces ? 'accent' : 'default'}
|
||||
wrapperClasses="w-full"
|
||||
>+ Create a new workspace
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<CenteredModal
|
||||
title="Select a workspace"
|
||||
subtitle="Logged in as {$usersWorkspaceStore?.email}"
|
||||
centerVertically={false}
|
||||
>
|
||||
{@const nonForkInvites = invites.filter((invite) => invite.parent_workspace_id == undefined)}
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-row items-center gap-2 justify-between mb-4">
|
||||
<h2 class="inline-flex gap-2 text-sm font-semibold text-emphasis flex-shrink-0">
|
||||
Workspaces{#if loading}<WindmillIcon spin="fast" />{/if}
|
||||
</h2>
|
||||
|
||||
<div class="flex flex-row items-center justify-between mt-8">
|
||||
<h2 class="text-sm font-semibold text-emphasis">Invites to join a Workspace</h2>
|
||||
{#if workspaces}
|
||||
<Toggle size="xs" bind:checked={showAllForks} options={{ right: 'Show workspace forks' }} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="mt-4"></div>
|
||||
|
||||
{#if nonForkInvites.length == 0}
|
||||
<p class="text-xs text-secondary"> You don't have new invites at the moment. </p>
|
||||
{/if}
|
||||
|
||||
{#each nonForkInvites as invite}
|
||||
<div
|
||||
class="w-full mx-auto py-1 px-2 rounded-md border border-border-light
|
||||
text-xs mt-1 flex flex-row justify-between items-center"
|
||||
>
|
||||
<div class="grow">
|
||||
<span class="font-mono font-semibold text-emphasis">{invite.workspace_id}</span>
|
||||
{#if invite.is_admin}
|
||||
<span class="text-xs text-primary">as an admin</span>
|
||||
{:else if invite.operator}
|
||||
<span class="text-xs text-primary">as an operator</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex justify-end items-center flex-col sm:flex-row gap-1">
|
||||
<Button
|
||||
variant="accent"
|
||||
size="xs2"
|
||||
href="{base}/user/accept_invite?workspace={encodeURIComponent(invite.workspace_id)}{rd
|
||||
? `&rd=${encodeURIComponent(rd)}`
|
||||
: ''}"
|
||||
>
|
||||
Accept
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="subtle"
|
||||
size="xs2"
|
||||
on:click={async () => {
|
||||
await UserService.declineInvite({
|
||||
requestBody: { workspace_id: invite.workspace_id }
|
||||
})
|
||||
sendUserToast(`Declined invite to ${invite.workspace_id}`)
|
||||
loadInvites()
|
||||
}}
|
||||
destructive
|
||||
>
|
||||
Decline
|
||||
</Button>
|
||||
</div>
|
||||
{#if allWorkspaces.length > 1}
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="relative text-primary flex-1 max-w-48">
|
||||
<TextInput
|
||||
inputProps={{
|
||||
placeholder: 'Search workspaces...'
|
||||
}}
|
||||
size="sm"
|
||||
bind:value={workspaceSearchFilter}
|
||||
class="!pr-8"
|
||||
/>
|
||||
<Search size={14} class="text-secondary absolute right-2 top-0 mt-2" />
|
||||
</div>
|
||||
{#if workspaceHasForks}
|
||||
<Button
|
||||
onClick={() => workspaceExpandCollapseAll?.()}
|
||||
title={workspaceAllExpanded ? 'Collapse all' : 'Expand all'}
|
||||
startIcon={{ icon: workspaceAllExpanded ? ChevronsDownUp : ChevronsUpDown }}
|
||||
size="xs2"
|
||||
variant="default"
|
||||
>
|
||||
{workspaceAllExpanded ? 'Collapse' : 'Expand'}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if showAllForks}
|
||||
{@const allWorkspacesList = workspaces || []}
|
||||
{@const filteredInvites = invites.filter((invite) => invite.parent_workspace_id)}
|
||||
{#if $superadmin}
|
||||
<div class="flex justify-end mb-2">
|
||||
<Toggle
|
||||
bind:checked={list_all_as_super_admin}
|
||||
options={{ right: 'List all workspaces as superadmin' }}
|
||||
size="xs"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if workspaces && $usersWorkspaceStore}
|
||||
{#if workspaces.length == 0}
|
||||
<p class="text-xs text-secondary mt-2">
|
||||
You are not a member of any workspace yet. Accept an invitation {#if createWorkspace}or
|
||||
create your own{/if}
|
||||
workspace.
|
||||
</p>
|
||||
{:else}
|
||||
<WorkspaceTreeView
|
||||
workspaces={allWorkspaces}
|
||||
onEnterWorkspace={speakFriendAndEnterWorkspace}
|
||||
onUnarchive={async (_workspaceId) => {
|
||||
if (list_all_as_super_admin) {
|
||||
loadWorkspacesAsAdmin()
|
||||
} else {
|
||||
loadWorkspaces()
|
||||
}
|
||||
}}
|
||||
bind:searchFilter={workspaceSearchFilter}
|
||||
bind:allExpanded={workspaceAllExpanded}
|
||||
bind:hasForks={workspaceHasForks}
|
||||
bind:this={workspaceTreeView}
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
{#each new Array(3) as _, i (i)}
|
||||
<Skeleton layout={[[2], 0.5]} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if createWorkspace}
|
||||
<div class="flex flex-row-reverse pt-4">
|
||||
<Button
|
||||
unifiedSize="sm"
|
||||
btnClasses={noWorkspaces ? 'animate-bounce hover:animate-none' : ''}
|
||||
href="{base}/user/create_workspace{rd ? `?rd=${encodeURIComponent(rd)}` : ''}"
|
||||
variant={noWorkspaces ? 'accent' : 'default'}
|
||||
wrapperClasses="w-full"
|
||||
>+ Create a new workspace
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-row items-center justify-between mt-8">
|
||||
<h2 class="text-sm font-semibold text-emphasis">Invites to join a Workspace</h2>
|
||||
{#if workspaces}
|
||||
<Toggle size="xs" bind:checked={showAllForks} options={{ right: 'Show workspace forks' }} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="mt-4"></div>
|
||||
{#if filteredInvites.length == 0}
|
||||
<p class="text-xs text-secondary"
|
||||
>There are no invites to join the forks of any workspace you're in.</p
|
||||
>
|
||||
{:else}
|
||||
<span class="mb-2 text-xs font-normal text-secondary">Forks of the workspaces you're in</span>
|
||||
|
||||
{#if nonForkInvites.length == 0}
|
||||
<p class="text-xs text-secondary"> You don't have new invites at the moment. </p>
|
||||
{/if}
|
||||
|
||||
{#each filteredInvites as invite}
|
||||
{@const inviteWorkspace = allWorkspacesList.find((w) => w.id === invite.workspace_id)}
|
||||
{#each nonForkInvites as invite}
|
||||
<div
|
||||
class="w-full mx-auto py-1 px-2 rounded-md border border-border-light
|
||||
text-xs mt-1 flex flex-row justify-between items-center"
|
||||
>
|
||||
<div class="grow">
|
||||
<div class="flex items-center gap-2">
|
||||
{#if inviteWorkspace?.parent_workspace_id}
|
||||
<GitFork size={12} class="text-secondary flex-shrink-0" />
|
||||
{/if}
|
||||
<span class="font-mono font-semibold text-emphasis">{invite.workspace_id}</span>
|
||||
</div>
|
||||
<span class="font-mono font-semibold text-emphasis">{invite.workspace_id}</span>
|
||||
{#if invite.is_admin}
|
||||
<span class="text-xs text-primary">as an admin</span>
|
||||
{:else if invite.operator}
|
||||
<span class="text-xs text-primary">as an operator</span>
|
||||
{/if}
|
||||
{#if invite.parent_workspace_id}
|
||||
<div class="text-secondary text-2xs mt-1">
|
||||
Fork of {invite.parent_workspace_id}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex justify-end items-center flex-col sm:flex-row gap-1">
|
||||
<Button
|
||||
variant="accent"
|
||||
unifiedSize="xs"
|
||||
size="xs2"
|
||||
href="{base}/user/accept_invite?workspace={encodeURIComponent(invite.workspace_id)}{rd
|
||||
? `&rd=${encodeURIComponent(rd)}`
|
||||
: ''}"
|
||||
@@ -367,60 +305,129 @@
|
||||
|
||||
<Button
|
||||
variant="subtle"
|
||||
unifiedSize="xs"
|
||||
destructive
|
||||
onClick={async () => {
|
||||
size="xs2"
|
||||
on:click={async () => {
|
||||
await UserService.declineInvite({
|
||||
requestBody: { workspace_id: invite.workspace_id }
|
||||
})
|
||||
sendUserToast(`Declined invite to ${invite.workspace_id}`)
|
||||
loadInvites()
|
||||
}}
|
||||
destructive
|
||||
>
|
||||
Decline
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<div class="flex justify-between items-center mt-10 flex-wrap gap-2">
|
||||
{#if $superadmin}
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize="md"
|
||||
on:click={superadminSettings.openDrawer}
|
||||
startIcon={{ icon: Settings }}
|
||||
dropdownItems={[
|
||||
{
|
||||
label: 'User settings',
|
||||
onClick: () => userSettings.openDrawer(),
|
||||
icon: User
|
||||
}
|
||||
]}
|
||||
>
|
||||
Instance settings
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize="md"
|
||||
onClick={() => userSettings.openDrawer()}
|
||||
startIcon={{ icon: Settings }}
|
||||
>
|
||||
User settings
|
||||
</Button>
|
||||
{#if showAllForks}
|
||||
{@const allWorkspacesList = workspaces || []}
|
||||
{@const filteredInvites = invites.filter((invite) => invite.parent_workspace_id)}
|
||||
|
||||
<div class="mt-4"></div>
|
||||
{#if filteredInvites.length == 0}
|
||||
<p class="text-xs text-secondary"
|
||||
>There are no invites to join the forks of any workspace you're in.</p
|
||||
>
|
||||
{:else}
|
||||
<span class="mb-2 text-xs font-normal text-secondary"
|
||||
>Forks of the workspaces you're in</span
|
||||
>
|
||||
{/if}
|
||||
|
||||
{#each filteredInvites as invite}
|
||||
{@const inviteWorkspace = allWorkspacesList.find((w) => w.id === invite.workspace_id)}
|
||||
<div
|
||||
class="w-full mx-auto py-1 px-2 rounded-md border border-border-light
|
||||
text-xs mt-1 flex flex-row justify-between items-center"
|
||||
>
|
||||
<div class="grow">
|
||||
<div class="flex items-center gap-2">
|
||||
{#if inviteWorkspace?.parent_workspace_id}
|
||||
<GitFork size={12} class="text-secondary flex-shrink-0" />
|
||||
{/if}
|
||||
<span class="font-mono font-semibold text-emphasis">{invite.workspace_id}</span>
|
||||
</div>
|
||||
{#if invite.is_admin}
|
||||
<span class="text-xs text-primary">as an admin</span>
|
||||
{:else if invite.operator}
|
||||
<span class="text-xs text-primary">as an operator</span>
|
||||
{/if}
|
||||
{#if invite.parent_workspace_id}
|
||||
<div class="text-secondary text-2xs mt-1">
|
||||
Fork of {invite.parent_workspace_id}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex justify-end items-center flex-col sm:flex-row gap-1">
|
||||
<Button
|
||||
variant="accent"
|
||||
unifiedSize="xs"
|
||||
href="{base}/user/accept_invite?workspace={encodeURIComponent(invite.workspace_id)}{rd
|
||||
? `&rd=${encodeURIComponent(rd)}`
|
||||
: ''}"
|
||||
>
|
||||
Accept
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="subtle"
|
||||
unifiedSize="xs"
|
||||
destructive
|
||||
onClick={async () => {
|
||||
await UserService.declineInvite({
|
||||
requestBody: { workspace_id: invite.workspace_id }
|
||||
})
|
||||
sendUserToast(`Declined invite to ${invite.workspace_id}`)
|
||||
loadInvites()
|
||||
}}
|
||||
>
|
||||
Decline
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
variant="accent"
|
||||
unifiedSize="md"
|
||||
on:click={async () => {
|
||||
logout()
|
||||
}}
|
||||
>
|
||||
Log out
|
||||
</Button>
|
||||
<div class="flex justify-between items-center mt-10 flex-wrap gap-2">
|
||||
{#if $superadmin}
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize="md"
|
||||
on:click={superadminSettings.openDrawer}
|
||||
startIcon={{ icon: Settings }}
|
||||
dropdownItems={[
|
||||
{
|
||||
label: 'User settings',
|
||||
onClick: () => userSettings.openDrawer(),
|
||||
icon: User
|
||||
}
|
||||
]}
|
||||
>
|
||||
Instance settings
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize="md"
|
||||
onClick={() => userSettings.openDrawer()}
|
||||
startIcon={{ icon: Settings }}
|
||||
>
|
||||
User settings
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
variant="accent"
|
||||
unifiedSize="md"
|
||||
on:click={async () => {
|
||||
logout()
|
||||
}}
|
||||
>
|
||||
Log out
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CenteredModal>
|
||||
<!-- <div class="center-center min-h-screen p-4">
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
Token will be posted to {host == 'localhost' ? 'your local machine' : host} to port {port}
|
||||
</p>
|
||||
<div class="flex flex-row justify-around pt-4 gap-x-1">
|
||||
<Button variant="default" size="sm" href={base}>Decline</Button>
|
||||
<Button variant="accent" size="sm" on:click={authorizeToken}>Authorize</Button>
|
||||
<Button variant="default" destructive size="lg" href={base}>Decline</Button>
|
||||
<Button variant="accent" size="lg" on:click={authorizeToken}>Authorize</Button>
|
||||
</div>
|
||||
</CenteredModal>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte'
|
||||
import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
|
||||
import { LogIn, AlertTriangle } from 'lucide-svelte'
|
||||
import { LogIn, AlertTriangle, ExternalLink } from 'lucide-svelte'
|
||||
import { mergeSchema } from '$lib/common'
|
||||
import { emptyString } from '$lib/utils'
|
||||
import { Alert } from '$lib/components/common'
|
||||
@@ -158,7 +158,7 @@
|
||||
|
||||
<ScheduleEditor bind:this={scheduleEditor} />
|
||||
|
||||
<CenteredModal title="Approval for resuming of flow" disableLogo>
|
||||
<CenteredModal title="Approval for resuming of flow" disableLogo centerVertically={false}>
|
||||
{#if error}
|
||||
<div class="space-y-6">
|
||||
{#if error.startsWith('Not authorized:')}
|
||||
@@ -182,23 +182,22 @@
|
||||
{:else}
|
||||
<div class="flex flex-row justify-between flex-wrap sm:flex-nowrap gap-x-4">
|
||||
<div class="w-full">
|
||||
<h2 class="mt-4">Approvers</h2>
|
||||
<h2 class="text-sm font-semibold text-emphasis">Approvers</h2>
|
||||
|
||||
<div class="my-4">
|
||||
<div class="mt-2 text-xs font-normal text-primary">
|
||||
{#if currentApprovers.length > 0}
|
||||
<ul>
|
||||
{#each currentApprovers as approver}
|
||||
<li
|
||||
><b
|
||||
>{approver.approver}<Tooltip
|
||||
>Unique id of approval: {approver.resume_id}</Tooltip
|
||||
></b
|
||||
></li
|
||||
>
|
||||
<li>
|
||||
<p>
|
||||
{approver.approver}
|
||||
<Tooltip>Unique id of approval: {approver.resume_id}</Tooltip>
|
||||
</p>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<p class="text-sm"
|
||||
<p class="text-xs text-secondary"
|
||||
>No current approvers for this step (approval steps can require more than one
|
||||
approval)</p
|
||||
>
|
||||
@@ -212,7 +211,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if !completed}
|
||||
<h2 class="mt-4 mb-2">Flow arguments</h2>
|
||||
<h2 class="mt-4 mb-2 text-sm font-semibold text-emphasis">Flow arguments</h2>
|
||||
|
||||
<JobArgs
|
||||
id={job?.id}
|
||||
@@ -221,78 +220,87 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class="mt-8">
|
||||
<div class="mt-8"></div>
|
||||
|
||||
<div class="p-4 rounded-md bg-surface-tertiary shadow-md">
|
||||
{#if approver}
|
||||
<p>Approving as: <b>{approver}</b></p>
|
||||
<p class="text-xs font-normal text-primary mb-2"
|
||||
>Approving as: <b class="font-semibold text-emphasis">{approver}</b></p
|
||||
>
|
||||
{/if}
|
||||
|
||||
{#if completed}
|
||||
<Alert type="info" title="Flow completed"
|
||||
>The flow is not running anymore. You cannot cancel or resume it.
|
||||
</Alert>
|
||||
{:else if alreadyResumed}
|
||||
<Alert type="info" title="Flow already resumed">
|
||||
You have already approved this flow to be resumed
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
{#if description != undefined}
|
||||
<DisplayResult noControls result={description} />
|
||||
{/if}
|
||||
|
||||
{#if schema && Object.keys(schema).length > 0 && !completed}
|
||||
{#if emptyString($enterpriseLicense)}
|
||||
<Alert type="warning" title="Adding a form to the approval page is an EE feature" />
|
||||
{:else}
|
||||
<SchemaForm
|
||||
onlyMaskPassword
|
||||
noVariablePicker
|
||||
bind:isValid={valid}
|
||||
schema={mergeSchema(schema, enum_payload)}
|
||||
bind:args={default_payload}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if !completed}
|
||||
<div class="w-max-md flex flex-row gap-x-4 gap-y-4 justify-between w-full flex-wrap">
|
||||
{#if !job?.raw_flow?.modules?.[approvalStep]?.suspend?.hide_cancel}
|
||||
<Button
|
||||
variant="accent"
|
||||
destructive
|
||||
on:click|once={cancel}
|
||||
size="lg"
|
||||
disabled={completed || alreadyResumed}>Deny</Button
|
||||
>
|
||||
{:else}
|
||||
<div></div>
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
variant="accent"
|
||||
on:click|once={resume}
|
||||
size="lg"
|
||||
disabled={completed || alreadyResumed || !valid}>Approve</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !completed && !alreadyResumed && job?.raw_flow?.modules?.[approvalStep]?.suspend?.user_auth_required && job?.raw_flow?.modules?.[approvalStep]?.suspend?.self_approval_disabled && $userStore && $userStore.email === job.email && ($userStore.is_admin || $userStore.is_super_admin)}
|
||||
<div class="mt-2">
|
||||
<Alert type="warning" title="Warning">
|
||||
As an administrator, by resuming or cancelling this stage of the flow, you bypass the
|
||||
self-approval interdiction.
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if completed}
|
||||
<div class="my-2"
|
||||
><p><b>The flow is not running anymore. You cannot cancel or resume it.</b></p></div
|
||||
>
|
||||
{:else if alreadyResumed}
|
||||
<div class="my-2"><p><b>You have already approved this flow to be resumed</b></p></div>
|
||||
{/if}
|
||||
|
||||
{#if description != undefined}
|
||||
<DisplayResult noControls result={description} />
|
||||
{/if}
|
||||
|
||||
{#if schema && !completed}
|
||||
{#if emptyString($enterpriseLicense)}
|
||||
<Alert type="warning" title="Adding a form to the approval page is an EE feature" />
|
||||
{:else}
|
||||
<SchemaForm
|
||||
onlyMaskPassword
|
||||
noVariablePicker
|
||||
bind:isValid={valid}
|
||||
schema={mergeSchema(schema, enum_payload)}
|
||||
bind:args={default_payload}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if !completed}
|
||||
<div class="w-max-md flex flex-row gap-x-4 gap-y-4 justify-between w-full flex-wrap mt-2">
|
||||
{#if !job?.raw_flow?.modules?.[approvalStep]?.suspend?.hide_cancel}
|
||||
<Button
|
||||
btnClasses="grow"
|
||||
variant="accent"
|
||||
destructive
|
||||
on:click|once={cancel}
|
||||
size="md"
|
||||
disabled={completed || alreadyResumed}>Deny</Button
|
||||
>
|
||||
{:else}
|
||||
<div></div>
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
btnClasses="grow"
|
||||
variant="accent"
|
||||
on:click|once={resume}
|
||||
size="md"
|
||||
disabled={completed || alreadyResumed || !valid}>Approve</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !completed && !alreadyResumed && job?.raw_flow?.modules?.[approvalStep]?.suspend?.user_auth_required && job?.raw_flow?.modules?.[approvalStep]?.suspend?.self_approval_disabled && $userStore && $userStore.email === job.email && ($userStore.is_admin || $userStore.is_super_admin)}
|
||||
<div class="mt-2">
|
||||
<Alert type="warning" title="Warning">
|
||||
As an administrator, by resuming or cancelling this stage of the flow, you bypass the
|
||||
self-approval interdiction.
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="mt-4 flex flex-row flex-wrap justify-between">
|
||||
<a target="_blank" rel="noreferrer" href="{base}/run/{job?.id}?workspace={job?.workspace_id}"
|
||||
>Flow run details (require auth)</a
|
||||
<a
|
||||
class="text-accent text-xs"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="{base}/run/{job?.id}?workspace={job?.workspace_id}"
|
||||
>Open run details (require auth) <ExternalLink size={12} class="inline" /></a
|
||||
>
|
||||
</div>
|
||||
{#if job && job.raw_flow && !completed}
|
||||
<h2 class="mt-10">Flow details</h2>
|
||||
<div class="border border-gray-700">
|
||||
<h2 class="mt-10 text-sm font-semibold text-emphasis mb-2">Flow details</h2>
|
||||
<div class="rounded-md overflow-hidden">
|
||||
<FlowGraphV2
|
||||
workspace={job.workspace_id}
|
||||
triggerNode={false}
|
||||
|
||||
Reference in New Issue
Block a user