feat(scenarios): add step title, scenario credentials, and environment context in executor
- add nullable title column to scenario steps; exposed in create/edit forms and step table - add scenario-credential join (many-to-many with CredentialEntity) with CRUD endpoints - expose environment URLs in code executor helpers via helpers.env and helpers.getEnvUrl() - resolve and cache environment per run from the login step's environmentName in scheduler - add section spacing and step table title column to ScenarioDetailPage
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
font-family: var(--font-family);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.textarea {
|
||||
font-family: var(--font-family-mono, monospace);
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text);
|
||||
background: var(--color-bg);
|
||||
border: var(--border-width) solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
width: 100%;
|
||||
outline: none;
|
||||
resize: vertical;
|
||||
transition: border-color var(--transition), box-shadow var(--transition);
|
||||
}
|
||||
|
||||
.textarea::placeholder {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.textarea:focus {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.textarea:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
background: var(--color-bg-subtle);
|
||||
}
|
||||
|
||||
.hasError {
|
||||
border-color: var(--color-error-fg);
|
||||
}
|
||||
.hasError:focus {
|
||||
border-color: var(--color-error-fg);
|
||||
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2);
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-error-fg);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import React, { useId } from 'react';
|
||||
import styles from './Textarea.module.css';
|
||||
|
||||
export interface TextareaProps
|
||||
extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'id'> {
|
||||
label?: string;
|
||||
hint?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export function Textarea({ label, hint, error, className, ...props }: TextareaProps) {
|
||||
const id = useId();
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
{label && (
|
||||
<label htmlFor={id} className={styles.label}>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<textarea
|
||||
id={id}
|
||||
className={[styles.textarea, error ? styles.hasError : '', className]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
aria-describedby={error ? `${id}-error` : hint ? `${id}-hint` : undefined}
|
||||
aria-invalid={error ? true : undefined}
|
||||
{...props}
|
||||
/>
|
||||
{error ? (
|
||||
<span id={`${id}-error`} className={styles.error} role="alert">
|
||||
{error}
|
||||
</span>
|
||||
) : hint ? (
|
||||
<span id={`${id}-hint`} className={styles.hint}>
|
||||
{hint}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user