mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 16:00:38 +00:00
484fdfa2bc
* Improve workers page * Update group config drawer * improve dirty workergroup config * Make layout reactive * fix section animation * prevent opening dropdown while clicking New group config * migrate workers page to svelte 5 * Open drawer upon adding a worker group * nit critical alert table * improve queue metrics drawer * improve agent worker drawer * harmonize copy icon * improve agent worker doc * improve layout * Improve autoscaling event list * Improve tags managment * Remove default tags * fix npm check * Add info for agent workers * improve agent worker jwt token creation * Improve token display * nit * improve tag display * create EE component * nit * harmonize tag overflow * handle permission better * improve env var presets * handle permission for config * nit alerts * nit * Improve custom tag creation in tag select * optimistic tag addition * nit * nit * fix typo * improve workers table * Group config tags * show mismatch * fix typo * optimistic update when adding tag * do not allow to create tag when picking a tag to watch in alerts
292 lines
7.9 KiB
Svelte
292 lines
7.9 KiB
Svelte
<script lang="ts">
|
|
import { base } from '$lib/base'
|
|
import Dropdown from '$lib/components/DropdownV2.svelte'
|
|
import type MoveDrawer from '$lib/components/MoveDrawer.svelte'
|
|
import SharedBadge from '$lib/components/SharedBadge.svelte'
|
|
import type ShareModal from '$lib/components/ShareModal.svelte'
|
|
import { AppService, DraftService, type ListableApp } from '$lib/gen'
|
|
import { userStore, workspaceStore } from '$lib/stores'
|
|
import { createEventDispatcher } from 'svelte'
|
|
import Button from '../button/Button.svelte'
|
|
import Row from './Row.svelte'
|
|
import DraftBadge from '$lib/components/DraftBadge.svelte'
|
|
import Badge from '../badge/Badge.svelte'
|
|
import {
|
|
ExternalLink,
|
|
Eye,
|
|
File,
|
|
FileJson,
|
|
FolderOpen,
|
|
GitFork,
|
|
ChevronUpSquare,
|
|
History,
|
|
Pen,
|
|
Share,
|
|
Trash,
|
|
Copy
|
|
} from 'lucide-svelte'
|
|
import { goto as gotoUrl } from '$app/navigation'
|
|
import { page } from '$app/stores'
|
|
import type DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
|
|
import { DELETE, copyToClipboard } from '$lib/utils'
|
|
import AppDeploymentHistory from '$lib/components/apps/editor/AppDeploymentHistory.svelte'
|
|
import { isDeployable } from '$lib/utils_deployable'
|
|
import { getDeployUiSettings } from '$lib/components/home/deploy_ui'
|
|
|
|
interface Props {
|
|
app: ListableApp & { has_draft?: boolean; draft_only?: boolean; canWrite: boolean }
|
|
marked: string | undefined
|
|
starred: boolean
|
|
shareModal: ShareModal
|
|
moveDrawer: MoveDrawer
|
|
deploymentDrawer: DeployWorkspaceDrawer
|
|
deleteConfirmedCallback: (() => void) | undefined
|
|
depth?: number
|
|
menuOpen?: boolean
|
|
}
|
|
|
|
let {
|
|
app,
|
|
marked,
|
|
starred,
|
|
shareModal,
|
|
moveDrawer,
|
|
deploymentDrawer,
|
|
deleteConfirmedCallback = $bindable(),
|
|
depth = 0,
|
|
menuOpen = $bindable(false)
|
|
}: Props = $props()
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
let appExport: { open: (path: string) => void } | undefined = $state(undefined)
|
|
let appDeploymentHistory: AppDeploymentHistory | undefined = $state(undefined)
|
|
|
|
async function loadAppJson() {
|
|
appExport?.open(app.path)
|
|
}
|
|
</script>
|
|
|
|
{#if menuOpen}
|
|
{#await import('$lib/components/apps/editor/AppJsonEditor.svelte') then Module}
|
|
<Module.default on:change bind:this={appExport} />
|
|
{/await}
|
|
<AppDeploymentHistory bind:this={appDeploymentHistory} appPath={app.path} />
|
|
{/if}
|
|
|
|
<Row
|
|
href="{base}/apps{app.raw_app ? '_raw' : ''}/get/{app.path}"
|
|
kind="app"
|
|
{marked}
|
|
path={app.path}
|
|
summary={app.summary}
|
|
workspaceId={app.workspace_id ?? $workspaceStore ?? ''}
|
|
{starred}
|
|
on:change
|
|
canFavorite={!app.draft_only}
|
|
{depth}
|
|
>
|
|
{#snippet badges()}
|
|
{#if app.execution_mode == 'anonymous'}
|
|
<Badge small icon={{ icon: Eye }}>Public</Badge>
|
|
{/if}
|
|
{#if app.raw_app}
|
|
<Badge small icon={{ icon: FileJson }}>Raw</Badge>
|
|
{/if}
|
|
<SharedBadge canWrite={app.canWrite} extraPerms={app.extra_perms} />
|
|
<DraftBadge has_draft={app.has_draft} draft_only={app.draft_only} />
|
|
<div class="w-8 center-center"></div>
|
|
{/snippet}
|
|
{#snippet actions()}
|
|
<span class="hidden md:inline-flex gap-x-1">
|
|
{#if !$userStore?.operator}
|
|
{#if app.canWrite}
|
|
<div>
|
|
<Button
|
|
aiId={`edit-app-button-${app.summary?.length > 0 ? app.summary : app.path}`}
|
|
aiDescription={`Edits the app ${app.summary?.length > 0 ? app.summary : app.path}`}
|
|
variant="subtle"
|
|
wrapperClasses="w-20"
|
|
startIcon={{ icon: Pen }}
|
|
href="{base}/apps{app.raw_app ? '_raw' : ''}/edit/{app.path}?nodraft=true"
|
|
>
|
|
Edit
|
|
</Button>
|
|
</div>
|
|
{:else}
|
|
<div>
|
|
<Button
|
|
aiId={`fork-app-button-${app.summary?.length > 0 ? app.summary : app.path}`}
|
|
aiDescription={`Fork the app ${app.summary?.length > 0 ? app.summary : app.path}`}
|
|
variant="subtle"
|
|
wrapperClasses="w-20"
|
|
unifiedSize="md"
|
|
startIcon={{ icon: GitFork }}
|
|
href="{base}/apps{app.raw_app ? '_raw' : ''}/add?template={app.path}"
|
|
>
|
|
Fork
|
|
</Button>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
</span>
|
|
<Dropdown
|
|
aiId={`app-row-dropdown-${app.summary?.length > 0 ? app.summary : app.path}`}
|
|
aiDescription={`Open dropdown for app ${app.summary?.length > 0 ? app.summary : app.path} options`}
|
|
items={async () => {
|
|
let { draft_only, canWrite, summary, execution_mode, path, has_draft } = app
|
|
|
|
if (draft_only) {
|
|
return [
|
|
{
|
|
displayName: 'Delete',
|
|
icon: Trash,
|
|
action: async (event) => {
|
|
// TODO
|
|
// @ts-ignore
|
|
if (event?.shiftKey) {
|
|
await AppService.deleteApp({ workspace: $workspaceStore ?? '', path })
|
|
dispatch('change')
|
|
} else {
|
|
deleteConfirmedCallback = async () => {
|
|
await AppService.deleteApp({ workspace: $workspaceStore ?? '', path })
|
|
dispatch('change')
|
|
}
|
|
}
|
|
},
|
|
type: 'delete',
|
|
disabled: !canWrite,
|
|
hide: $userStore?.operator
|
|
},
|
|
{
|
|
displayName: $userStore?.operator ? 'View JSON' : 'View/Edit JSON',
|
|
icon: File,
|
|
action: () => {
|
|
loadAppJson()
|
|
}
|
|
}
|
|
]
|
|
}
|
|
return [
|
|
{
|
|
displayName: 'Duplicate/Fork',
|
|
icon: GitFork,
|
|
href: `${base}/apps${app.raw_app ? '_raw' : ''}/add?template=${path}`,
|
|
hide: $userStore?.operator
|
|
},
|
|
{
|
|
displayName: 'Move/Rename',
|
|
icon: FolderOpen,
|
|
action: () => {
|
|
moveDrawer.openDrawer(path, summary, 'app')
|
|
},
|
|
disabled: !canWrite,
|
|
hide: $userStore?.operator
|
|
},
|
|
...(isDeployable('app', path, await getDeployUiSettings())
|
|
? [
|
|
{
|
|
displayName: 'Deploy to staging/prod',
|
|
icon: ChevronUpSquare,
|
|
action: () => {
|
|
deploymentDrawer.openDrawer(path, 'app')
|
|
},
|
|
hide: $userStore?.operator
|
|
}
|
|
]
|
|
: []),
|
|
{
|
|
displayName: $userStore?.operator ? 'View JSON' : 'View/Edit JSON',
|
|
icon: FileJson,
|
|
action: () => {
|
|
loadAppJson()
|
|
}
|
|
},
|
|
{
|
|
displayName: 'Deployments',
|
|
icon: History,
|
|
action: () => appDeploymentHistory?.open(),
|
|
hide: $userStore?.operator
|
|
},
|
|
{
|
|
displayName: canWrite ? 'Share' : 'See Permissions',
|
|
icon: Share,
|
|
action: () => {
|
|
shareModal.openDrawer && shareModal.openDrawer(path, 'app')
|
|
},
|
|
hide: $userStore?.operator
|
|
},
|
|
{
|
|
displayName: 'Copy path',
|
|
icon: Copy,
|
|
action: () => {
|
|
copyToClipboard(path)
|
|
}
|
|
},
|
|
...(execution_mode == 'anonymous'
|
|
? [
|
|
{
|
|
displayName: 'Go to public page',
|
|
icon: ExternalLink,
|
|
action: async () => {
|
|
let secretUrl = await AppService.getPublicSecretOfApp({
|
|
workspace: $workspaceStore ?? '',
|
|
path
|
|
})
|
|
let url =
|
|
$page.url.protocol +
|
|
'//' +
|
|
`${$page.url.hostname}/public/${$workspaceStore}/${secretUrl}`
|
|
gotoUrl(url)
|
|
}
|
|
}
|
|
]
|
|
: []),
|
|
...(has_draft
|
|
? [
|
|
{
|
|
displayName: 'Delete Draft',
|
|
icon: Trash,
|
|
action: async () => {
|
|
await DraftService.deleteDraft({
|
|
workspace: $workspaceStore ?? '',
|
|
path,
|
|
kind: 'app'
|
|
})
|
|
dispatch('change')
|
|
},
|
|
type: DELETE,
|
|
disabled: !canWrite,
|
|
hide: $userStore?.operator
|
|
}
|
|
]
|
|
: []),
|
|
{
|
|
displayName: 'Delete',
|
|
icon: Trash,
|
|
action: async (event) => {
|
|
// TODO
|
|
// @ts-ignore
|
|
if (event?.shiftKey) {
|
|
await AppService.deleteApp({ workspace: $workspaceStore ?? '', path })
|
|
dispatch('change')
|
|
} else {
|
|
deleteConfirmedCallback = async () => {
|
|
await AppService.deleteApp({ workspace: $workspaceStore ?? '', path })
|
|
dispatch('change')
|
|
}
|
|
}
|
|
},
|
|
type: 'delete',
|
|
disabled: !canWrite,
|
|
hide: $userStore?.operator
|
|
}
|
|
]
|
|
}}
|
|
on:open={() => {
|
|
menuOpen = true
|
|
}}
|
|
/>
|
|
{/snippet}
|
|
</Row>
|