feat(ui/alerts): ReviewStep + promotion prefill warnings

Review step dumps a human summary plus raw request JSON, and (when a
setter is supplied) offers an Enabled-on-save Toggle. Promotion prefill
now returns {form, warnings}: clears agent IDs (per-env), flags missing
apps in target env, and flags webhook connections not allowed in target
env. 4 Vitest cases cover copy-name, agent clear, app-missing, and
webhook-not-allowed paths.

The wizard now consumes {form, warnings}; Task 25 renders the warnings
banner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-20 14:04:04 +02:00
parent 816096f4d1
commit 3963ea5591
4 changed files with 186 additions and 16 deletions

View File

@@ -1,15 +1,59 @@
import { initialForm, type FormState } from './form-state';
import type { AlertRuleResponse } from '../../../api/queries/alertRules';
/**
* Prefill the wizard form from a source rule being promoted from another env.
*
* Task 19 scaffolding: reuses the edit-prefill path and renames the rule.
* Task 24 rewrites this to compute scope-adjustment warnings and return
* `{ form, warnings }`.
*/
export function prefillFromPromotion(source: AlertRuleResponse): FormState {
const f = initialForm(source);
f.name = `${source.name ?? 'rule'} (copy)`;
return f;
export interface PrefillWarning {
field: string;
message: string;
}
export interface PrefillOptions {
targetEnvAppSlugs?: string[];
/** IDs of outbound connections allowed in the target env. */
targetEnvAllowedConnectionIds?: string[];
}
/**
* Client-side prefill when promoting a rule from another env. Emits warnings for
* fields that cross env boundaries (agent IDs, apps missing in target env,
* outbound connections not allowed in target env).
*/
export function prefillFromPromotion(
source: AlertRuleResponse,
opts: PrefillOptions = {},
): { form: FormState; warnings: PrefillWarning[] } {
const form = initialForm(source);
form.name = `${source.name ?? 'rule'} (copy)`;
const warnings: PrefillWarning[] = [];
// Agent IDs are per-env, can't transfer.
if (form.agentId) {
warnings.push({
field: 'scope.agentId',
message: `Agent \`${form.agentId}\` is specific to the source env \u2014 cleared for target env.`,
});
form.agentId = '';
if (form.scopeKind === 'agent') form.scopeKind = 'app';
}
// App slug: warn if not present in target env.
if (form.appSlug && opts.targetEnvAppSlugs && !opts.targetEnvAppSlugs.includes(form.appSlug)) {
warnings.push({
field: 'scope.appSlug',
message: `App \`${form.appSlug}\` does not exist in the target env. Update before saving.`,
});
}
// Webhook connections: warn if connection is not allowed in target env.
if (opts.targetEnvAllowedConnectionIds) {
for (const w of form.webhooks) {
if (!opts.targetEnvAllowedConnectionIds.includes(w.outboundConnectionId)) {
warnings.push({
field: `webhooks[${w.outboundConnectionId}]`,
message: `Outbound connection is not allowed in the target env \u2014 remove or pick another before saving.`,
});
}
}
}
return { form, warnings };
}