Skip to content

Commit

Permalink
refactor trigger run modal
Browse files Browse the repository at this point in the history
  • Loading branch information
andnorda committed Jan 31, 2025
1 parent c58c28f commit 0a820e3
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 106 deletions.
112 changes: 6 additions & 106 deletions src/routes/team/[team]/[env]/job/[job]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
import Persistence from '$lib/components/Persistence.svelte';
import Traffic from '$lib/components/Traffic.svelte';
import GraphErrors from '$lib/GraphErrors.svelte';
import { Button, Heading, Modal, TextField } from '@nais/ds-svelte-community';
import { Button, Heading } from '@nais/ds-svelte-community';
import type { PageData } from './$houdini';
import Runs from './Runs.svelte';
import Schedule from './Schedule.svelte';
import Secrets from './Secrets.svelte';
import Status from './Status.svelte';
import TriggerRunModal from './TriggerRunModal.svelte';
interface Props {
data: PageData;
Expand Down Expand Up @@ -50,10 +51,8 @@
let environment = $derived(page.params.env);
let open = $state(false);
let runName = $state('');
let errors: string[] = $state([]);
const submit = () => {
const submit = (runName: string) => {
triggerRun.mutate({
jobName,
environment,
Expand All @@ -62,65 +61,6 @@
jobId: $Job.data!.team.environment.job.id
});
};
const cancel = () => {
runName = '';
errors = [];
open = false;
};
const confirm = () => {
let valid = validateJobName(runName);
if (!valid.isValid) {
open = true;
errors = valid.errors;
return;
} else if (runName !== '') {
open = false;
submit();
} else {
open = true;
errors = ['The run name cannot be empty.'];
}
};
type ValidationResult = {
isValid: boolean;
errors: string[];
};
function close() {
open = false;
}
function validateJobName(input: string): ValidationResult {
const errors: string[] = [];
// Kubernetes resource name regex and length constraint
const k8sNameRegex = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/;
const maxNameLength = 63;
// Validate jobName
if (!input) {
errors.push('Run name is required.');
} else {
if (!k8sNameRegex.test(input)) {
errors.push(
'Run name must be a valid Kubernetes resource name (lowercase alphanumeric and hyphens).'
);
}
if (input.length > maxNameLength) {
errors.push(
`Run name must not exceed ${maxNameLength} characters. Current length: ${input.length}.`
);
}
}
return {
isValid: errors.length === 0,
errors
};
}
</script>

<GraphErrors errors={$Job.errors} />
Expand Down Expand Up @@ -171,41 +111,9 @@
</div>
</div>

<Modal bind:open onclose={close}>
{#snippet header()}
<h3>Trigger run of {jobName}</h3>
{/snippet}
<div class="modal-content">
This will trigger a new run of
<strong>{jobName}</strong> in
<strong>{environment}</strong>.
<br />
Please provide a name for the run.
<br />
<br />
<TextField type="text" bind:value={runName}>
{#snippet label()}
Run name:
{/snippet}
</TextField>

{#if $triggerRun.errors?.length ?? 0 > 0}
<GraphErrors errors={$triggerRun.errors} />
{/if}
{#if errors.length > 0}
<div class="errors">
{#each errors as error}
<span>{error}</span><br />
{/each}
</div>
{/if}
</div>

{#snippet footer()}
<Button variant="primary" type="submit" onclick={confirm}>Confirm</Button>
<Button variant="tertiary" type="reset" onclick={cancel}>Cancel</Button>
{/snippet}
</Modal>
{#if open}
<TriggerRunModal {jobName} {environment} close={() => (open = false)} {submit} />
{/if}
{/if}

<style>
Expand All @@ -231,12 +139,4 @@
border-top: 1px solid var(--a-border-default);
margin: 1.5rem 0.125rem;
}
.modal-content {
width: 500px;
}
.errors {
margin-top: 1rem;
color: var(--a-text-danger);
}
</style>
71 changes: 71 additions & 0 deletions src/routes/team/[team]/[env]/job/[job]/TriggerRunModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script lang="ts">
import { BodyShort, Button, Heading, Modal, TextField } from '@nais/ds-svelte-community';
interface Props {
jobName: string;
environment: string;
submit: (runName: string) => void;
close: () => void;
}
let { jobName, environment, submit, close }: Props = $props();
let error: string = $state('');
function validateRunName(runName: string): { isValid: true } | { isValid: false; error: string } {
const k8sNameRegex = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/;
const maxNameLength = 63;
if (!runName) {
return {
isValid: false,
error: 'Run name is required.'
};
} else if (!k8sNameRegex.test(runName)) {
return {
isValid: false,
error:
'Run name must be a valid Kubernetes resource name (lowercase alphanumeric and hyphens).'
};
} else if (runName.length > maxNameLength) {
return {
isValid: false,
error: `Run name must not exceed ${maxNameLength} characters. Current length: ${runName.length}.`
};
} else {
return {
isValid: true
};
}
}
const onSubmit = (e: SubmitEvent) => {
e.preventDefault();
const runName = (new FormData(e.target as HTMLFormElement).get('runName') ?? '') as string;
const valid = validateRunName(runName);
if (valid.isValid) {
submit(runName);
close();
} else {
error = valid.error;
}
};
</script>

<Modal open onclose={close} header="Trigger run">
<form method="dialog" id="trigger-run-form" onsubmit={onSubmit} style="max-width: 512px">
<BodyShort spacing>
This will trigger a new run of
<strong>{jobName}</strong> in
<strong>{environment}</strong>.
</BodyShort>
<TextField type="text" name="runName" {error} autofocus>
{#snippet label()}
Run name
{/snippet}
</TextField>
</form>
{#snippet footer()}
<Button form="trigger-run-form">Confirm</Button>
<Button variant="secondary" type="button" onclick={close}>Cancel</Button>
{/snippet}
</Modal>

0 comments on commit 0a820e3

Please sign in to comment.