222 lines
5.0 KiB
Svelte
222 lines
5.0 KiB
Svelte
<script>
|
|
import {
|
|
Select,
|
|
InlineAlert,
|
|
Input,
|
|
Label,
|
|
Layout,
|
|
notifications,
|
|
} from "@budibase/bbui"
|
|
import { onMount, createEventDispatcher } from "svelte"
|
|
import { flags } from "stores/builder"
|
|
import { featureFlags } from "stores/portal"
|
|
import { API } from "api"
|
|
import MagicWand from "../../../../assets/MagicWand.svelte"
|
|
|
|
import { helpers, REBOOT_CRON } from "@budibase/shared-core"
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
export let cronExpression
|
|
|
|
let error
|
|
let nextExecutions
|
|
|
|
// AI prompt
|
|
let aiCronPrompt = ""
|
|
let loadingAICronExpression = false
|
|
|
|
$: aiEnabled = $featureFlags.AI_CUSTOM_CONFIGS || $featureFlags.BUDIBASE_AI
|
|
$: {
|
|
if (cronExpression) {
|
|
try {
|
|
nextExecutions = helpers.cron
|
|
.getNextExecutionDates(cronExpression)
|
|
.join("\n")
|
|
} catch (err) {
|
|
nextExecutions = null
|
|
}
|
|
}
|
|
}
|
|
|
|
const onChange = e => {
|
|
if (e.detail !== REBOOT_CRON) {
|
|
error = helpers.cron.validate(e.detail).err
|
|
}
|
|
if (e.detail === cronExpression || error) {
|
|
return
|
|
}
|
|
|
|
cronExpression = e.detail
|
|
dispatch("change", e.detail)
|
|
}
|
|
|
|
const updatePreset = e => {
|
|
aiCronPrompt = ""
|
|
onChange(e)
|
|
}
|
|
|
|
const updateCronExpression = e => {
|
|
aiCronPrompt = ""
|
|
cronExpression = null
|
|
nextExecutions = null
|
|
onChange(e)
|
|
}
|
|
|
|
let touched = false
|
|
|
|
const CRON_EXPRESSIONS = [
|
|
{
|
|
label: "Every Minute",
|
|
value: "* * * * *",
|
|
},
|
|
{
|
|
label: "Every Hour",
|
|
value: "0 * * * *",
|
|
},
|
|
{
|
|
label: "Every Morning at 8AM",
|
|
value: "0 8 * * *",
|
|
},
|
|
{
|
|
label: "Every Night at Midnight",
|
|
value: "0 0 * * *",
|
|
},
|
|
]
|
|
|
|
onMount(() => {
|
|
if (!$flags.cloud) {
|
|
CRON_EXPRESSIONS.push({
|
|
label: "Every Budibase Reboot",
|
|
value: REBOOT_CRON,
|
|
})
|
|
}
|
|
})
|
|
|
|
async function generateAICronExpression() {
|
|
loadingAICronExpression = true
|
|
try {
|
|
const response = await API.generateCronExpression({
|
|
prompt: aiCronPrompt,
|
|
})
|
|
cronExpression = response.message
|
|
dispatch("change", response.message)
|
|
} catch (err) {
|
|
notifications.error(err.message)
|
|
} finally {
|
|
loadingAICronExpression = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
<Layout noPadding gap="S">
|
|
<Select
|
|
on:change={updatePreset}
|
|
value={cronExpression || "Custom"}
|
|
secondary
|
|
extraThin
|
|
label="Use a Preset (Optional)"
|
|
options={CRON_EXPRESSIONS}
|
|
/>
|
|
{#if aiEnabled}
|
|
<div class="cron-ai-generator">
|
|
<Input
|
|
bind:value={aiCronPrompt}
|
|
label="Generate Cron Expression with AI"
|
|
size="S"
|
|
placeholder="Run every hour between 1pm to 4pm everyday of the week"
|
|
/>
|
|
{#if aiCronPrompt}
|
|
<div
|
|
class="icon"
|
|
class:pulsing-text={loadingAICronExpression}
|
|
on:click={generateAICronExpression}
|
|
>
|
|
<MagicWand height="17" width="17" />
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
<Input
|
|
label="Cron Expression"
|
|
{error}
|
|
on:change={updateCronExpression}
|
|
value={cronExpression}
|
|
on:blur={() => (touched = true)}
|
|
updateOnChange={false}
|
|
/>
|
|
{#if touched && !cronExpression}
|
|
<Label><div class="error">Please specify a CRON expression</div></Label>
|
|
{/if}
|
|
{#if nextExecutions}
|
|
<InlineAlert
|
|
type="info"
|
|
header="Next Executions"
|
|
message={nextExecutions}
|
|
/>
|
|
{/if}
|
|
</Layout>
|
|
|
|
<style>
|
|
.cron-ai-generator {
|
|
flex: 1;
|
|
position: relative;
|
|
}
|
|
.icon {
|
|
right: 1px;
|
|
bottom: 1px;
|
|
position: absolute;
|
|
justify-content: center;
|
|
align-items: center;
|
|
display: flex;
|
|
flex-direction: row;
|
|
box-sizing: border-box;
|
|
border-left: 1px solid var(--spectrum-alias-border-color);
|
|
border-top-right-radius: var(--spectrum-alias-border-radius-regular);
|
|
border-bottom-right-radius: var(--spectrum-alias-border-radius-regular);
|
|
width: 31px;
|
|
color: var(--spectrum-alias-text-color);
|
|
background-color: var(--spectrum-global-color-gray-75);
|
|
transition: background-color
|
|
var(--spectrum-global-animation-duration-100, 130ms),
|
|
box-shadow var(--spectrum-global-animation-duration-100, 130ms),
|
|
border-color var(--spectrum-global-animation-duration-100, 130ms);
|
|
height: calc(var(--spectrum-alias-item-height-m) - 2px);
|
|
}
|
|
|
|
.icon:hover {
|
|
cursor: pointer;
|
|
color: var(--spectrum-alias-text-color-hover);
|
|
background-color: var(--spectrum-global-color-gray-50);
|
|
border-color: var(--spectrum-alias-border-color-hover);
|
|
}
|
|
|
|
.error {
|
|
padding-top: var(--spacing-xs);
|
|
color: var(--spectrum-global-color-red-500);
|
|
}
|
|
|
|
.pulsing-text {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
animation: pulse 1.5s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% {
|
|
opacity: 0.3;
|
|
transform: scale(1);
|
|
}
|
|
50% {
|
|
opacity: 1;
|
|
transform: scale(1.05);
|
|
}
|
|
100% {
|
|
opacity: 0.3;
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
</style>
|