mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 16:02:19 +00:00
feat: add schedule page to script settings
This commit is contained in:
@@ -88,7 +88,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function createSchedule(path: string) {
|
||||
const { cron, timezone, args, enabled } = $scheduleStore
|
||||
const { cron, timezone, args, enabled, summary } = $scheduleStore
|
||||
|
||||
try {
|
||||
await ScheduleService.createSchedule({
|
||||
@@ -100,7 +100,8 @@
|
||||
script_path: path,
|
||||
is_flow: true,
|
||||
args,
|
||||
enabled
|
||||
enabled,
|
||||
summary
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -215,7 +216,7 @@
|
||||
// console.log('flow', computeUnlockedSteps(flow)) // del
|
||||
// loadingSave = false // del
|
||||
// return
|
||||
const { cron, timezone, args, enabled } = $scheduleStore
|
||||
const { cron, timezone, args, enabled, summary } = $scheduleStore
|
||||
if (newFlow) {
|
||||
try {
|
||||
localStorage.removeItem('flow')
|
||||
@@ -256,14 +257,20 @@
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath
|
||||
})
|
||||
if (JSON.stringify(schedule.args) != JSON.stringify(args) || schedule.schedule != cron) {
|
||||
if (
|
||||
JSON.stringify(schedule.args) != JSON.stringify(args) ||
|
||||
schedule.schedule != cron ||
|
||||
schedule.timezone != timezone ||
|
||||
schedule.summary != summary
|
||||
) {
|
||||
await ScheduleService.updateSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
requestBody: {
|
||||
schedule: formatCron(cron),
|
||||
timezone,
|
||||
args
|
||||
args,
|
||||
summary
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -338,6 +345,7 @@
|
||||
}
|
||||
|
||||
const scheduleStore = writable<Schedule>({
|
||||
summary: undefined,
|
||||
args: {},
|
||||
cron: '',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
@@ -373,14 +381,15 @@
|
||||
})
|
||||
|
||||
async function loadSchedule() {
|
||||
loadFlowSchedule(initialPath, $workspaceStore)
|
||||
loadFlowSchedule(initialPath, $workspaceStore!)
|
||||
.then((schedule: Schedule) => {
|
||||
scheduleStore.set(schedule)
|
||||
})
|
||||
.catch(() => {
|
||||
scheduleStore.set({
|
||||
summary: undefined,
|
||||
cron: '0 */5 * * *',
|
||||
timezone: 'UTC',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
args: {},
|
||||
enabled: false
|
||||
})
|
||||
|
||||
@@ -72,9 +72,10 @@
|
||||
drawer?.openDrawer()
|
||||
}
|
||||
|
||||
export async function openNew(is_flow: boolean, initial_script_path?: string) {
|
||||
export async function openNew(nis_flow: boolean, initial_script_path?: string) {
|
||||
args = {}
|
||||
runnable = undefined
|
||||
is_flow = nis_flow
|
||||
let defaultErrorHandlerMaybe = undefined
|
||||
let defaultRecoveryHandlerMaybe = undefined
|
||||
if ($workspaceStore) {
|
||||
@@ -87,7 +88,7 @@
|
||||
}
|
||||
|
||||
edit = false
|
||||
itemKind = is_flow ? 'flow' : 'script'
|
||||
itemKind = nis_flow ? 'flow' : 'script'
|
||||
initialScriptPath = initial_script_path ?? ''
|
||||
summary = ''
|
||||
no_flow_overlap = false
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { DraftService, NewScript, Script, ScriptService, type NewScriptWithDraft } from '$lib/gen'
|
||||
import {
|
||||
DraftService,
|
||||
NewScript,
|
||||
Script,
|
||||
ScriptService,
|
||||
type NewScriptWithDraft,
|
||||
ScheduleService
|
||||
} from '$lib/gen'
|
||||
import { goto } from '$app/navigation'
|
||||
import { page } from '$app/stores'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
@@ -10,6 +17,7 @@
|
||||
emptySchema,
|
||||
emptyString,
|
||||
encodeState,
|
||||
formatCron,
|
||||
orderedJsonStringify
|
||||
} from '$lib/utils'
|
||||
import Path from './Path.svelte'
|
||||
@@ -24,6 +32,7 @@
|
||||
import ErrorHandlerToggleButton from '$lib/components/details/ErrorHandlerToggleButton.svelte'
|
||||
import {
|
||||
Bug,
|
||||
Calendar,
|
||||
CheckCircle,
|
||||
Code,
|
||||
DiffIcon,
|
||||
@@ -50,6 +59,9 @@
|
||||
import type Editor from './Editor.svelte'
|
||||
import WorkerTagPicker from './WorkerTagPicker.svelte'
|
||||
import MetadataGen from './copilot/MetadataGen.svelte'
|
||||
import ScriptSchedules from './ScriptSchedules.svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { type ScriptSchedule, loadScriptSchedule } from '$lib/scripts'
|
||||
|
||||
export let script: NewScript
|
||||
export let initialPath: string = ''
|
||||
@@ -69,6 +81,26 @@
|
||||
let editor: Editor | undefined = undefined
|
||||
let scriptEditor: ScriptEditor | undefined = undefined
|
||||
|
||||
let scheduleStore = writable<ScriptSchedule>({
|
||||
summary: '',
|
||||
cron: '0 */5 * * *',
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
args: {},
|
||||
enabled: false
|
||||
})
|
||||
async function loadSchedule() {
|
||||
const scheduleRes = await loadScriptSchedule(initialPath, $workspaceStore!)
|
||||
if (scheduleRes) {
|
||||
scheduleStore.set(scheduleRes)
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if (initialPath != '') {
|
||||
loadSchedule()
|
||||
}
|
||||
}
|
||||
|
||||
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql']
|
||||
|
||||
export function setCode(code: string): void {
|
||||
@@ -161,6 +193,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function createSchedule(path: string) {
|
||||
const { cron, timezone, args, enabled, summary } = $scheduleStore
|
||||
|
||||
try {
|
||||
await ScheduleService.createSchedule({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path: path,
|
||||
schedule: formatCron(cron),
|
||||
timezone,
|
||||
script_path: path,
|
||||
is_flow: false,
|
||||
args,
|
||||
enabled,
|
||||
summary
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
sendUserToast(`The primary schedule could not be created: ${err}`, true)
|
||||
}
|
||||
}
|
||||
|
||||
async function editScript(stay: boolean): Promise<void> {
|
||||
loadingSave = true
|
||||
try {
|
||||
@@ -204,6 +258,49 @@
|
||||
concurrency_key: emptyString(script.concurrency_key) ? undefined : script.concurrency_key
|
||||
}
|
||||
})
|
||||
|
||||
const { enabled, timezone, args, cron, summary } = $scheduleStore
|
||||
const scheduleExists =
|
||||
initialPath &&
|
||||
initialPath != '' &&
|
||||
(await ScheduleService.existsSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath
|
||||
}))
|
||||
|
||||
if (scheduleExists) {
|
||||
const schedule = await ScheduleService.getSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath
|
||||
})
|
||||
if (
|
||||
JSON.stringify(schedule.args) != JSON.stringify(args) ||
|
||||
schedule.schedule != cron ||
|
||||
schedule.timezone != timezone ||
|
||||
schedule.summary != summary
|
||||
) {
|
||||
await ScheduleService.updateSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
requestBody: {
|
||||
schedule: formatCron(cron),
|
||||
timezone,
|
||||
args,
|
||||
summary
|
||||
}
|
||||
})
|
||||
}
|
||||
if (enabled != schedule.enabled) {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
requestBody: { enabled }
|
||||
})
|
||||
}
|
||||
} else if (enabled) {
|
||||
await createSchedule(initialPath)
|
||||
}
|
||||
|
||||
savedScript = cloneDeep(script) as NewScriptWithDraft
|
||||
history.replaceState(history.state, '', `/scripts/edit/${script.path}`)
|
||||
if (stay) {
|
||||
@@ -365,7 +462,7 @@
|
||||
let path: Path | undefined = undefined
|
||||
let dirtyPath = false
|
||||
|
||||
let selectedTab: 'metadata' | 'runtime' | 'ui' = 'metadata'
|
||||
let selectedTab: 'metadata' | 'runtime' | 'ui' | 'schedule' = 'metadata'
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
@@ -387,6 +484,7 @@
|
||||
cannot be inferred from the type directly.
|
||||
</Tooltip>
|
||||
</Tab>
|
||||
<Tab value="schedule" active={$scheduleStore.enabled}>Schedule</Tab>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-4">
|
||||
<TabContent value="metadata">
|
||||
@@ -858,6 +956,9 @@
|
||||
<div class="mt-4" />
|
||||
<ScriptSchema bind:schema={script.schema} />
|
||||
</TabContent>
|
||||
<TabContent value="schedule">
|
||||
<ScriptSchedules {initialPath} schema={script.schema} schedule={scheduleStore} />
|
||||
</TabContent>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tabs>
|
||||
@@ -888,6 +989,21 @@
|
||||
</div>
|
||||
|
||||
<div class="gap-4 flex">
|
||||
{#if $scheduleStore.enabled}
|
||||
<Button
|
||||
btnClasses="hidden lg:inline-flex"
|
||||
startIcon={{ icon: Calendar }}
|
||||
variant="contained"
|
||||
color="light"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
metadataOpen = true
|
||||
selectedTab = 'schedule'
|
||||
}}
|
||||
>
|
||||
{$scheduleStore.cron ?? ''}
|
||||
</Button>
|
||||
{/if}
|
||||
<div class="flex justify-start w-full border rounded-md overflow-hidden">
|
||||
<div>
|
||||
<button
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<script lang="ts">
|
||||
import CronInput from '$lib/components/CronInput.svelte'
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { emptyString } from '$lib/utils'
|
||||
import { Alert, Button, Skeleton } from '$lib/components/common'
|
||||
import ScheduleEditor from '$lib/components/ScheduleEditor.svelte'
|
||||
import { ScheduleService, type Schedule } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { Calendar } from 'lucide-svelte'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type { ScriptSchedule } from '$lib/scripts'
|
||||
|
||||
export let initialPath: string
|
||||
export let schema: Record<string, any> | undefined
|
||||
export let schedule: Writable<ScriptSchedule>
|
||||
// const { schedule, flowStore, pathStore, initialPath } =
|
||||
// getContext<FlowEditorContext>('FlowEditorContext')
|
||||
let schedules: Schedule[] | undefined = undefined
|
||||
|
||||
async function loadSchedules() {
|
||||
try {
|
||||
schedules = (
|
||||
await ScheduleService.listSchedules({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
isFlow: false
|
||||
})
|
||||
).filter((s) => s.path != initialPath)
|
||||
} catch (e) {
|
||||
console.error('impossible to load schedules')
|
||||
}
|
||||
}
|
||||
|
||||
let scheduleEditor: ScheduleEditor
|
||||
|
||||
$: initialPath && loadSchedules()
|
||||
</script>
|
||||
|
||||
<div class="w-full py-2">
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
autofocus
|
||||
type="text"
|
||||
placeholder="Schedule summary"
|
||||
class="text-sm w-full font-semibold mb-4"
|
||||
bind:value={$schedule.summary}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CronInput bind:schedule={$schedule.cron} bind:timezone={$schedule.timezone} />
|
||||
<div class="mt-10" />
|
||||
<SchemaForm {schema} bind:args={$schedule.args} />
|
||||
{#if emptyString($schedule.cron)}
|
||||
<p class="text-xs text-tertiary mt-10">Define a schedule frequency first</p>
|
||||
{/if}
|
||||
<div class="mt-10" />
|
||||
<Toggle
|
||||
disabled={emptyString($schedule.cron)}
|
||||
bind:checked={$schedule.enabled}
|
||||
options={{
|
||||
right: 'Schedule enabled'
|
||||
}}
|
||||
/>
|
||||
<Alert bgClass="my-4" type="warning" title="Changes only applied upon deploy">
|
||||
Changes to the primary schedule are only applied upon deploy. Other schedules' changes are applied
|
||||
immediately.
|
||||
</Alert>
|
||||
|
||||
{#if initialPath != ''}
|
||||
<ScheduleEditor
|
||||
on:update={() => {
|
||||
loadSchedules()
|
||||
}}
|
||||
bind:this={scheduleEditor}
|
||||
/>
|
||||
<h2 class="pt-7">Other schedules</h2>
|
||||
<div class="py-4 flex">
|
||||
<Button
|
||||
on:click={() => scheduleEditor?.openNew(false, initialPath)}
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{ icon: Calendar }}
|
||||
>
|
||||
New Schedule
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if schedules}
|
||||
{#if schedules.length == 0}
|
||||
<div class="text-xs text-secondary px-2"> No other schedules </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y px-2 pt-2 max-w-lg">
|
||||
{#each schedules as schedule (schedule.path)}
|
||||
<div class="grid grid-cols-6 text-2xs items-center py-2"
|
||||
><div class="col-span-3 truncate">{schedule.path}</div><div>{schedule.schedule}</div>
|
||||
<div>{schedule.enabled ? 'on' : 'off'}</div>
|
||||
<button on:click={() => scheduleEditor?.openEdit(schedule.path, false)}>Edit</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<Skeleton layout={[[8]]} />
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -5,10 +5,45 @@
|
||||
import { emptyString } from '$lib/utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import { Alert, Button, Skeleton } from '$lib/components/common'
|
||||
import ScheduleEditor from '$lib/components/ScheduleEditor.svelte'
|
||||
import { ScheduleService, type Schedule } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { Calendar } from 'lucide-svelte'
|
||||
|
||||
const { schedule, flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const { schedule, flowStore, initialPath } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
let schedules: Schedule[] | undefined = undefined
|
||||
|
||||
async function loadSchedules() {
|
||||
try {
|
||||
schedules = (
|
||||
await ScheduleService.listSchedules({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: initialPath,
|
||||
isFlow: true
|
||||
})
|
||||
).filter((s) => s.path != initialPath)
|
||||
} catch (e) {
|
||||
console.error('impossible to load schedules')
|
||||
}
|
||||
}
|
||||
|
||||
let scheduleEditor: ScheduleEditor
|
||||
|
||||
$: initialPath && loadSchedules()
|
||||
</script>
|
||||
|
||||
<div class="w-full py-2">
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
autofocus
|
||||
type="text"
|
||||
placeholder="Schedule summary"
|
||||
class="text-sm w-full font-semibold mb-4"
|
||||
bind:value={$schedule.summary}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CronInput bind:schedule={$schedule.cron} bind:timezone={$schedule.timezone} />
|
||||
<div class="mt-10" />
|
||||
<SchemaForm schema={$flowStore.schema} bind:args={$schedule.args} />
|
||||
@@ -23,3 +58,46 @@
|
||||
right: 'Schedule enabled'
|
||||
}}
|
||||
/>
|
||||
<Alert bgClass="my-4" type="warning" title="Changes only applied upon deploy">
|
||||
Changes to the primary schedule are only applied upon deploy. Other schedules' changes are applied
|
||||
immediately.
|
||||
</Alert>
|
||||
|
||||
{#if initialPath != ''}
|
||||
<ScheduleEditor
|
||||
on:update={() => {
|
||||
loadSchedules()
|
||||
}}
|
||||
bind:this={scheduleEditor}
|
||||
/>
|
||||
<h2 class="pt-7">Other schedules</h2>
|
||||
<div class="py-4 flex">
|
||||
<Button
|
||||
on:click={() => scheduleEditor?.openNew(true, initialPath)}
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{ icon: Calendar }}
|
||||
>
|
||||
New Schedule
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if schedules}
|
||||
{#if schedules.length == 0}
|
||||
<div class="text-xs text-secondary px-2"> No other schedules </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y px-2 pt-2 max-w-lg">
|
||||
{#each schedules as schedule (schedule.path)}
|
||||
<div class="grid grid-cols-6 text-2xs items-center py-2"
|
||||
><div class="col-span-3 truncate">{schedule.path}</div><div>{schedule.schedule}</div>
|
||||
<div>{schedule.enabled ? 'on' : 'off'}</div>
|
||||
<button on:click={() => scheduleEditor?.openEdit(schedule.path, true)}>Edit</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<Skeleton layout={[[8]]} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ScheduleService } from '$lib/gen'
|
||||
|
||||
export type Schedule = {
|
||||
summary: string | undefined
|
||||
args: Record<string, any>
|
||||
cron: string
|
||||
timezone: string
|
||||
@@ -8,7 +9,7 @@ export type Schedule = {
|
||||
}
|
||||
|
||||
// Load the schedule of a flow given its path and the workspace
|
||||
export async function loadFlowSchedule(path: string, workspace: string = ''): Promise<Schedule> {
|
||||
export async function loadFlowSchedule(path: string, workspace: string): Promise<Schedule> {
|
||||
const existsSchedule = await ScheduleService.existsSchedule({
|
||||
workspace,
|
||||
path
|
||||
@@ -24,6 +25,7 @@ export async function loadFlowSchedule(path: string, workspace: string = ''): Pr
|
||||
})
|
||||
|
||||
return {
|
||||
summary: schedule.summary,
|
||||
enabled: schedule.enabled,
|
||||
cron: schedule.schedule,
|
||||
timezone: schedule.timezone,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { get } from 'svelte/store'
|
||||
import type { Schema, SupportedLanguage } from './common'
|
||||
import { FlowService, Script, ScriptService } from './gen'
|
||||
import { FlowService, Script, ScriptService, ScheduleService } from './gen'
|
||||
import { workspaceStore } from './stores'
|
||||
|
||||
export function scriptLangToEditorLang(lang: Script.language) {
|
||||
@@ -35,6 +35,42 @@ export function scriptLangToEditorLang(lang: Script.language) {
|
||||
}
|
||||
}
|
||||
|
||||
export type ScriptSchedule = {
|
||||
summary: string | undefined
|
||||
args: Record<string, any>
|
||||
cron: string
|
||||
timezone: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
// Load the schedule of a flow given its path and the workspace
|
||||
export async function loadScriptSchedule(
|
||||
path: string,
|
||||
workspace: string
|
||||
): Promise<ScriptSchedule | undefined> {
|
||||
const existsSchedule = await ScheduleService.existsSchedule({
|
||||
workspace,
|
||||
path
|
||||
})
|
||||
|
||||
if (!existsSchedule) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const schedule = await ScheduleService.getSchedule({
|
||||
workspace,
|
||||
path
|
||||
})
|
||||
|
||||
return {
|
||||
summary: schedule.summary,
|
||||
enabled: schedule.enabled,
|
||||
cron: schedule.schedule,
|
||||
timezone: schedule.timezone,
|
||||
args: schedule.args ?? {}
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadSchemaFlow(path: string): Promise<Schema> {
|
||||
const flow = await FlowService.getFlowByPath({
|
||||
workspace: get(workspaceStore)!,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import DetailPageLayout from '$lib/components/details/DetailPageLayout.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
import { Alert, Badge as HeaderBadge, Skeleton } from '$lib/components/common'
|
||||
import { Alert, Button, Badge as HeaderBadge, Skeleton } from '$lib/components/common'
|
||||
import MoveDrawer from '$lib/components/MoveDrawer.svelte'
|
||||
import RunForm from '$lib/components/RunForm.svelte'
|
||||
import ShareModal from '$lib/components/ShareModal.svelte'
|
||||
@@ -27,7 +27,8 @@
|
||||
History,
|
||||
Columns,
|
||||
Pen,
|
||||
Eye
|
||||
Eye,
|
||||
Calendar
|
||||
} from 'lucide-svelte'
|
||||
|
||||
import DetailPageHeader from '$lib/components/details/DetailPageHeader.svelte'
|
||||
@@ -42,6 +43,7 @@
|
||||
import TimeAgo from '$lib/components/TimeAgo.svelte'
|
||||
import ClipboardPanel from '$lib/components/details/ClipboardPanel.svelte'
|
||||
import FlowGraphViewerStep from '$lib/components/FlowGraphViewerStep.svelte'
|
||||
import { loadFlowSchedule, type Schedule } from '$lib/components/flows/scheduleUtils'
|
||||
|
||||
let flow: Flow | undefined
|
||||
let can_write = false
|
||||
@@ -76,9 +78,13 @@
|
||||
goto('/')
|
||||
}
|
||||
|
||||
let schedule: Schedule | undefined = undefined
|
||||
async function loadFlow(): Promise<void> {
|
||||
flow = await FlowService.getFlowByPath({ workspace: $workspaceStore!, path })
|
||||
can_write = canWrite(flow.path, flow.extra_perms!, $userStore)
|
||||
try {
|
||||
schedule = await loadFlowSchedule(path, $workspaceStore!)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
$: urlAsync = `${$page.url.origin}/api/w/${$workspaceStore}/jobs/run/f/${flow?.path}`
|
||||
@@ -307,6 +313,21 @@
|
||||
</HeaderBadge>
|
||||
</div>
|
||||
{/if}
|
||||
{#if schedule?.enabled}
|
||||
<Button
|
||||
btnClasses="inline-flex"
|
||||
startIcon={{ icon: Calendar }}
|
||||
variant="contained"
|
||||
color="light"
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
detailSelected = 'details'
|
||||
triggerSelected = 'schedule'
|
||||
}}
|
||||
>
|
||||
{schedule.cron ?? ''}
|
||||
</Button>
|
||||
{/if}
|
||||
</DetailPageHeader>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="form">
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
Badge,
|
||||
Alert,
|
||||
DrawerContent,
|
||||
Drawer
|
||||
Drawer,
|
||||
Button
|
||||
} from '$lib/components/common'
|
||||
import Skeleton from '$lib/components/common/skeleton/Skeleton.svelte'
|
||||
import RunForm from '$lib/components/RunForm.svelte'
|
||||
@@ -41,6 +42,7 @@
|
||||
Activity,
|
||||
Archive,
|
||||
ArchiveRestore,
|
||||
Calendar,
|
||||
Eye,
|
||||
FolderOpen,
|
||||
GitFork,
|
||||
@@ -64,6 +66,7 @@
|
||||
import TimeAgo from '$lib/components/TimeAgo.svelte'
|
||||
import ClipboardPanel from '$lib/components/details/ClipboardPanel.svelte'
|
||||
import PersistentScriptDrawer from '$lib/components/PersistentScriptDrawer.svelte'
|
||||
import { loadScriptSchedule, type ScriptSchedule } from '$lib/scripts'
|
||||
|
||||
let script: Script | undefined
|
||||
let topHash: string | undefined
|
||||
@@ -139,6 +142,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
let schedule: ScriptSchedule | undefined = undefined
|
||||
|
||||
async function loadScript(hash: string): Promise<void> {
|
||||
try {
|
||||
@@ -147,6 +151,7 @@
|
||||
script = await ScriptService.getScriptByPath({ workspace: $workspaceStore!, path: hash })
|
||||
hash = script.hash
|
||||
}
|
||||
schedule = await loadScriptSchedule(script.path, $workspaceStore!)
|
||||
can_write =
|
||||
script.workspace_id == $workspaceStore &&
|
||||
canWrite(script.path, script.extra_perms!, $userStore)
|
||||
@@ -488,6 +493,21 @@
|
||||
</Badge>
|
||||
</div>
|
||||
{/if}
|
||||
{#if schedule?.enabled}
|
||||
<Button
|
||||
btnClasses="inline-flex"
|
||||
startIcon={{ icon: Calendar }}
|
||||
variant="contained"
|
||||
color="light"
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
detailSelected = 'details'
|
||||
triggerSelected = 'schedule'
|
||||
}}
|
||||
>
|
||||
{schedule.cron ?? ''}
|
||||
</Button>
|
||||
{/if}
|
||||
</DetailPageHeader>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="form">
|
||||
|
||||
Reference in New Issue
Block a user