Files
crank/apps/ui/html/wizard/step5.html
T
github-ops 209b3e1485
CI / Rust Checks (push) Successful in 5m32s
CI / UI Checks (push) Successful in 4s
CI / Deployment Manifests (push) Successful in 3s
CI / Frontend E2E (push) Successful in 3m30s
CI / Deploy (push) Successful in 1m33s
Polish approval and import UI
2026-06-24 21:51:13 +00:00

350 lines
23 KiB
HTML

<div id="step-panel-5" class="step-pane">
<div class="step-panel-header">
<div class="step-panel-eyebrow">
<div class="step-panel-eyebrow-line"></div>
<span data-step-counter>Step 5 of 5</span>
</div>
<h1 class="step-panel-title" data-i18n="wizard.step5.title">Связи полей и запуск</h1>
<p class="step-panel-subtitle" data-i18n="wizard.step5.subtitle">Свяжите параметры MCP инструмента с полями API-запроса, а ответ API с результатом инструмента. Авторизация настраивается на шаге 2.</p>
</div>
<div class="section-divider" style="margin-bottom: 16px;">
<span class="section-divider-label" data-i18n="wizard.step5.input_request">Вход инструмента → API-запрос</span>
<div class="section-divider-line"></div>
</div>
<div class="config-card mapping-builder-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div>
<div class="config-card-title">Конструктор API-запроса</div>
<div class="config-card-subtitle">Укажите, куда отправлять поля инструмента: в путь, query-параметры, заголовки или JSON-тело.</div>
</div>
<button id="wizard-add-request-mapping-row" class="btn-ghost-sm" type="button">Добавить поле</button>
</div>
<div class="config-card-body" style="gap: 14px;">
<div class="mapping-table" id="wizard-request-mapping-rows" data-testid="wizard-request-mapping-rows"></div>
<div class="mapping-builder-actions">
<button id="wizard-auto-request-mapping" class="btn-ghost-sm" type="button">Собрать из входного JSON</button>
<button id="wizard-sync-request-mapping" class="btn-ghost-sm" type="button">Обновить YAML</button>
</div>
<details class="advanced-mapping-details">
<summary>Дополнительно: YAML маппинга запроса</summary>
<div class="code-block">
<div class="code-toolbar">
<div class="code-dots"><span></span><span></span><span></span></div>
<span class="code-toolbar-label">yaml / request-fields</span>
</div>
<textarea id="tool-input-mapping" class="form-textarea code-textarea" rows="10">first_name: "$.input.first_name"
last_name: "$.input.last_name"
email: "$.input.email"
company: "$.input.company"
phone: "$.input.phone"
source: "$.input.source"</textarea>
</div>
</details>
</div>
</div>
<div class="section-divider" style="margin-bottom: 16px;">
<span class="section-divider-label" data-i18n="wizard.step5.output_response">Ответ API → результат инструмента</span>
<div class="section-divider-line"></div>
</div>
<div class="config-card mapping-builder-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div>
<div class="config-card-title">Конструктор ответа инструмента</div>
<div class="config-card-subtitle">Выберите поля из ответа API, которые нужно вернуть MCP клиенту.</div>
</div>
<button id="wizard-add-response-mapping-row" class="btn-ghost-sm" type="button">Добавить поле</button>
</div>
<div class="config-card-body" style="gap: 14px;">
<div class="mapping-response-layout">
<div>
<div class="form-label">Поля ответа API</div>
<div id="wizard-response-json-tree" class="json-tree-picker" data-testid="wizard-response-json-tree"></div>
</div>
<div>
<div class="form-label">Поля результата инструмента</div>
<div class="mapping-table" id="wizard-response-mapping-rows" data-testid="wizard-response-mapping-rows"></div>
</div>
</div>
<div class="mapping-builder-actions">
<button id="wizard-auto-response-mapping" class="btn-ghost-sm" type="button">Собрать из ответа JSON</button>
<button id="wizard-sync-response-mapping" class="btn-ghost-sm" type="button">Обновить YAML</button>
</div>
<details class="advanced-mapping-details">
<summary>Дополнительно: YAML маппинга ответа</summary>
<div class="code-block">
<div class="code-toolbar">
<div class="code-dots"><span></span><span></span><span></span></div>
<span class="code-toolbar-label">yaml / response-fields</span>
</div>
<textarea id="tool-output-mapping" class="form-textarea code-textarea" rows="10">lead_id: "$.response.data.id"
status: "$.response.data.status"
created_at: "$.response.data.created_at"
owner_id: "$.response.data.owner.id"</textarea>
</div>
</details>
</div>
</div>
<div class="section-divider" style="margin-bottom: 16px;">
<span class="section-divider-label" data-i18n="wizard.step5.execution">Параметры выполнения</span>
<div class="section-divider-line"></div>
</div>
<div class="config-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div class="config-card-header-icon">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<circle cx="8" cy="8" r="6"/>
<path d="M8 5v3l2 2"/>
</svg>
</div>
<div>
<div class="config-card-title" data-i18n="wizard.step5.exec_title">Выполнение запроса</div>
<div class="config-card-subtitle" data-i18n="wizard.step5.exec_subtitle">Время ожидания, повторные попытки и профиль авторизации</div>
</div>
<span class="config-card-optional" data-i18n="wizard.optional">Optional</span>
</div>
<div class="config-card-body" style="gap: 0; padding: 0;">
<div class="code-block" style="border-radius: 0; border: none;">
<div class="code-toolbar">
<div class="code-dots"><span></span><span></span><span></span></div>
<span class="code-toolbar-label">yaml / execution</span>
</div>
<textarea id="tool-exec-config" class="form-textarea code-textarea" rows="10">timeout_ms: 10000
retry:
max_attempts: 3
backoff_ms: 500
auth:
profile: acme_bearer
tls:
verify: true</textarea>
</div>
</div>
</div>
<div class="section-divider" style="margin-bottom: 16px;">
<span class="section-divider-label" data-i18n="wizard.step5.live_title">Проверка и публикация</span>
<div class="section-divider-line"></div>
</div>
<div id="wizard-live-status" class="info-callout" hidden style="margin-bottom: 20px;">
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<circle cx="8" cy="8" r="7"/>
<path d="M8 11V8M8 5v-.5"/>
</svg>
<div class="info-callout-body">
<div class="info-callout-title" id="wizard-live-status-title" data-i18n="wizard.step5.draft_actions">Результат действия</div>
<div class="info-callout-text" id="wizard-live-status-text"></div>
</div>
</div>
<div id="wizard-mapping-warnings" class="mapping-warnings" hidden data-testid="wizard-mapping-warnings"></div>
<div class="config-card" id="agent-facing-preview-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div class="config-card-header-icon">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 4h10M3 8h10M3 12h6"/>
</svg>
</div>
<div>
<div class="config-card-title" data-i18n="wizard.step5.agent_preview_title">Как инструмент увидит MCP клиент</div>
<div class="config-card-subtitle" data-i18n="wizard.step5.agent_preview_subtitle">Проверьте имя, описание, входную схему и примеры до публикации.</div>
</div>
<button id="wizard-copy-agent-preview" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.copy_agent_preview">Копировать схему</button>
</div>
<div class="config-card-body" style="gap: 16px;">
<div class="agent-preview-summary">
<div>
<div class="agent-preview-label" data-i18n="wizard.step5.preview_tool_name">Имя инструмента</div>
<div class="agent-preview-value input-mono" id="agent-preview-tool-name"></div>
</div>
<div>
<div class="agent-preview-label" data-i18n="wizard.step5.preview_tool_title">Короткое описание</div>
<div class="agent-preview-value" id="agent-preview-tool-title"></div>
</div>
</div>
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.preview_tool_description">Описание для модели</label>
<div class="agent-preview-description" id="agent-preview-tool-description"></div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.preview_input_schema">Input schema</label>
<textarea id="agent-preview-input-schema" class="form-textarea code-textarea" rows="10" spellcheck="false" readonly></textarea>
</div>
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.preview_tool_call">Пример вызова tools/call</label>
<textarea id="agent-preview-tool-call" class="form-textarea code-textarea" rows="10" spellcheck="false" readonly></textarea>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.preview_success_response">Пример успешного ответа</label>
<textarea id="agent-preview-success-response" class="form-textarea code-textarea" rows="8" spellcheck="false" readonly></textarea>
</div>
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.preview_error_response">Пример ошибки</label>
<textarea id="agent-preview-error-response" class="form-textarea code-textarea" rows="8" spellcheck="false" readonly></textarea>
</div>
</div>
</div>
</div>
<div class="config-card" id="wizard-quality-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div class="config-card-header-icon">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 2l5 2v4c0 3-2 5-5 6-3-1-5-3-5-6V4l5-2z"/>
<path d="M6 8l1.5 1.5L10.5 6"/>
</svg>
</div>
<div>
<div class="config-card-title" data-i18n="wizard.quality.title">Проверка качества инструмента</div>
<div class="config-card-subtitle" data-i18n="wizard.quality.subtitle">Проверьте имя, описание, схемы и результат, который увидит агент.</div>
</div>
<button id="wizard-run-quality" class="btn-ghost-sm" type="button" data-i18n="wizard.quality.action">Проверить качество</button>
</div>
<div class="config-card-body" style="gap: 12px;">
<div id="wizard-quality-empty" class="form-hint" data-i18n="wizard.quality.empty">Проверка еще не запускалась.</div>
<div id="wizard-quality-findings" class="quality-findings" hidden></div>
</div>
</div>
<div class="config-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div class="config-card-header-icon">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="12" height="10" rx="1.5"/>
<path d="M5 7h6M5 10h6"/>
</svg>
</div>
<div>
<div class="config-card-title" data-i18n="wizard.step5.samples_title">Примеры и автоматическая настройка</div>
<div class="config-card-subtitle" data-i18n="wizard.step5.samples_subtitle">Сохраните пример входных и выходных данных, затем Crank сможет пересобрать схемы и связи полей на их основе.</div>
</div>
</div>
<div class="config-card-body" style="gap: 16px;">
<div class="form-row">
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.input_sample">Пример входных данных</label>
<textarea id="wizard-input-sample" class="form-textarea code-textarea" rows="9" spellcheck="false">{
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com"
}</textarea>
<input id="wizard-input-sample-file" type="file" accept=".json,application/json" hidden>
</div>
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.output_sample">Пример ответа</label>
<textarea id="wizard-output-sample" class="form-textarea code-textarea" rows="9" spellcheck="false">{
"id": "lead_123",
"status": "created"
}</textarea>
<input id="wizard-output-sample-file" type="file" accept=".json,application/json" hidden>
</div>
</div>
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
<button id="wizard-load-input-sample-file" class="btn-ghost-sm" type="button">Загрузить JSON запроса</button>
<button id="wizard-load-output-sample-file" class="btn-ghost-sm" type="button">Загрузить JSON ответа</button>
<button id="wizard-format-samples" class="btn-ghost-sm" type="button">Проверить и форматировать JSON</button>
<button id="wizard-upload-input-sample" class="btn-primary-sm" type="button" data-i18n="wizard.step5.save_input_sample">Сохранить входной пример</button>
<button id="wizard-upload-output-sample" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.save_output_sample">Сохранить выходной пример</button>
<button id="wizard-generate-draft" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.generate_draft">Пересобрать по примерам</button>
</div>
</div>
</div>
<div class="config-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div class="config-card-header-icon">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M2 8h12M10 4l4 4-4 4"/>
</svg>
</div>
<div>
<div class="config-card-title" data-i18n="wizard.step5.test_title">Тестирование операции</div>
<div class="config-card-subtitle" data-i18n="wizard.step5.test_subtitle">Выполните инструмент на реальном API перед публикацией.</div>
</div>
</div>
<div class="config-card-body" style="gap: 16px;">
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.test_input_payload">Входные данные для теста</label>
<textarea id="wizard-test-input" class="form-textarea code-textarea" rows="8" spellcheck="false">{
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com"
}</textarea>
</div>
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
<button id="wizard-run-test" class="btn-primary-sm" type="button" data-i18n="wizard.step5.run_test">Запустить тест</button>
<button id="wizard-copy-test-response" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.use_response_output">Использовать ответ как выходной пример</button>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.request_preview">Предпросмотр API-запроса</label>
<textarea id="wizard-test-request-preview" class="form-textarea code-textarea" rows="8" spellcheck="false" readonly></textarea>
</div>
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.response_preview">Предпросмотр ответа API</label>
<textarea id="wizard-test-response-preview" class="form-textarea code-textarea" rows="8" spellcheck="false" readonly></textarea>
</div>
</div>
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.errors">Ошибки</label>
<textarea id="wizard-test-errors" class="form-textarea code-textarea" rows="4" spellcheck="false" readonly></textarea>
</div>
</div>
</div>
<div class="config-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div class="config-card-header-icon">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 2v12M3 7l5-5 5 5"/>
</svg>
</div>
<div>
<div class="config-card-title" data-i18n="wizard.step5.yaml_title">Импорт и экспорт YAML</div>
<div class="config-card-subtitle" data-i18n="wizard.step5.yaml_subtitle">Экспортируйте операцию в файл или импортируйте подготовленную YAML-конфигурацию.</div>
</div>
</div>
<div class="config-card-body" style="gap: 16px;">
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
<button id="wizard-export-yaml" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.export_yaml">Экспорт YAML</button>
<button id="wizard-import-yaml-file-trigger" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.load_yaml_file">Загрузить YAML-файл</button>
<button id="wizard-import-yaml" class="btn-primary-sm" type="button" data-i18n="wizard.step5.import_yaml">Импорт YAML</button>
<input id="wizard-import-yaml-file" type="file" accept=".yaml,.yml,text/yaml" style="display:none">
</div>
<div class="form-group">
<label class="form-label" data-i18n="wizard.step5.yaml_document">YAML-конфигурация</label>
<textarea id="wizard-import-yaml-text" class="form-textarea code-textarea" rows="10" spellcheck="false" data-i18n-ph="wizard.step5.yaml_placeholder" placeholder="Вставьте YAML сюда, чтобы создать или обновить операцию."></textarea>
</div>
</div>
</div>
<div class="config-card" style="margin-bottom: 20px;">
<div class="config-card-header">
<div class="config-card-header-icon">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 8l3 3 7-7"/>
</svg>
</div>
<div>
<div class="config-card-title" data-i18n="wizard.step5.publish_title">Публикация</div>
<div class="config-card-subtitle" data-i18n="wizard.step5.publish_subtitle">Опубликуйте операцию, чтобы ее можно было добавить агентам.</div>
</div>
</div>
<div class="config-card-body" style="gap: 12px;">
<div class="form-hint" data-i18n="wizard.step5.publish_hint">Перед публикацией Crank сохраняет текущие настройки из мастера.</div>
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
<button id="wizard-publish-operation" class="btn-primary-sm" type="button" data-i18n="wizard.step5.publish_action">Опубликовать операцию</button>
</div>
</div>
</div>
</div><!-- /step-pane-5 -->