fix: refine create page layout spacing
This commit is contained in:
@@ -345,158 +345,165 @@ export function OperationForm() {
|
||||
creationMutation.mutate(values);
|
||||
})}
|
||||
>
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>1 — Protocol</h2>
|
||||
<p>One tool maps to exactly one upstream method.</p>
|
||||
</div>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="proto-grid">
|
||||
{protocolCards.map((card) => {
|
||||
const isSelected = activeProtocol === card.protocol;
|
||||
const selectedClassName = isSelected
|
||||
? `proto-card selected-${card.protocol === "graphql" ? "gql" : card.protocol}`
|
||||
: "proto-card";
|
||||
<div className="builder-content">
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>1 — Protocol</h2>
|
||||
<p>One tool maps to exactly one upstream method.</p>
|
||||
</div>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="proto-grid">
|
||||
{protocolCards.map((card) => {
|
||||
const isSelected = activeProtocol === card.protocol;
|
||||
const selectedClassName = isSelected
|
||||
? `proto-card selected-${card.protocol === "graphql" ? "gql" : card.protocol}`
|
||||
: "proto-card";
|
||||
|
||||
return (
|
||||
<button
|
||||
className={selectedClassName}
|
||||
key={card.protocol}
|
||||
onClick={() => {
|
||||
form.setValue("protocol", card.protocol, {
|
||||
shouldDirty: true,
|
||||
shouldValidate: true,
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<div className="proto-card-top">
|
||||
<div className={`proto-icon pi-${card.protocol === "graphql" ? "gql" : card.protocol}`}>
|
||||
{card.protocol === "graphql" ? "GQL" : card.protocol.toUpperCase()}
|
||||
return (
|
||||
<button
|
||||
className={selectedClassName}
|
||||
key={card.protocol}
|
||||
onClick={() => {
|
||||
form.setValue("protocol", card.protocol, {
|
||||
shouldDirty: true,
|
||||
shouldValidate: true,
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<div className="proto-card-top">
|
||||
<div className={`proto-icon pi-${card.protocol === "graphql" ? "gql" : card.protocol}`}>
|
||||
{card.protocol === "graphql" ? "GQL" : card.protocol.toUpperCase()}
|
||||
</div>
|
||||
<div className="proto-radio" />
|
||||
</div>
|
||||
<div className="proto-radio" />
|
||||
</div>
|
||||
<div className="proto-card-copy">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<div className="proto-card-copy">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>2 — Tool identity</h2>
|
||||
<p>Name and description visible to the LLM at runtime.</p>
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>2 — Tool identity</h2>
|
||||
<p>Name and description visible to the LLM at runtime.</p>
|
||||
</div>
|
||||
<span className="status-pill status-draft">draft</span>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="form-grid">{commonFields.map(renderField)}</div>
|
||||
</div>
|
||||
<span className="status-pill status-draft">draft</span>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="form-grid">{commonFields.map(renderField)}</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>3 — Upstream target</h2>
|
||||
<p>Protocol-specific target configuration exposed through one MCP tool.</p>
|
||||
</div>
|
||||
<span className={`protocol-pill protocol-${activeProtocol}`}>{activeProtocol}</span>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="form-grid">
|
||||
{protocolFields.map(renderField)}
|
||||
{activeProtocol === "grpc" ? (
|
||||
<label className="field-block field-block-wide">
|
||||
<span>Descriptor set file</span>
|
||||
<small className="field-hint">
|
||||
Upload a compiled descriptor-set file to autofill the base64 payload.
|
||||
</small>
|
||||
<input
|
||||
className="input"
|
||||
type="file"
|
||||
accept=".bin,.pb,.desc"
|
||||
onChange={(event) => {
|
||||
void handleDescriptorFileChange(event.target.files?.[0]);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
) : null}
|
||||
{activeProtocol === "rest" ? renderField({
|
||||
name: "restStaticHeadersText",
|
||||
label: "Static headers",
|
||||
description:
|
||||
"Always sent for this REST target before dynamic request headers are merged.",
|
||||
rows: 8,
|
||||
wide: true,
|
||||
code: true,
|
||||
}) : null}
|
||||
</div>
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>3 — Upstream target</h2>
|
||||
<p>Protocol-specific target configuration exposed through one MCP tool.</p>
|
||||
</div>
|
||||
<span className={`protocol-pill protocol-${activeProtocol}`}>{activeProtocol}</span>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="form-grid">
|
||||
{protocolFields.map(renderField)}
|
||||
{activeProtocol === "grpc" ? (
|
||||
<label className="field-block field-block-wide">
|
||||
<span>Descriptor set file</span>
|
||||
<small className="field-hint">
|
||||
Upload a compiled descriptor-set file to autofill the base64 payload.
|
||||
</small>
|
||||
<input
|
||||
className="input"
|
||||
type="file"
|
||||
accept=".bin,.pb,.desc"
|
||||
onChange={(event) => {
|
||||
void handleDescriptorFileChange(event.target.files?.[0]);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
) : null}
|
||||
{activeProtocol === "rest"
|
||||
? renderField({
|
||||
name: "restStaticHeadersText",
|
||||
label: "Static headers",
|
||||
description:
|
||||
"Always sent for this REST target before dynamic request headers are merged.",
|
||||
rows: 8,
|
||||
wide: true,
|
||||
code: true,
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
|
||||
<div className="info-banner">
|
||||
<div className="info-dot" />
|
||||
<p>
|
||||
Headers and auth stay separate from path and payload mapping. Configure shared
|
||||
transport headers in the execution section below.
|
||||
</p>
|
||||
<div className="info-banner">
|
||||
<div className="info-dot" />
|
||||
<p>
|
||||
Headers and auth stay separate from path and payload mapping. Configure shared
|
||||
transport headers in the execution section below.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>4 — Contract schemas</h2>
|
||||
<p>Protocol-agnostic schemas for MCP input and output payloads.</p>
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>4 — Contract schemas</h2>
|
||||
<p>Protocol-agnostic schemas for MCP input and output payloads.</p>
|
||||
</div>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="form-grid">{schemaFields.map(renderField)}</div>
|
||||
</div>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="form-grid">{schemaFields.map(renderField)}</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>5 — Mapping and execution</h2>
|
||||
<p>Translate MCP input to request fields, then map upstream output back to tool output.</p>
|
||||
</div>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="section-divider">
|
||||
<span className="section-divider-label">Input → Request</span>
|
||||
<div className="section-divider-line" />
|
||||
</div>
|
||||
<div className="form-grid">{mappingFields.slice(0, 1).map(renderField)}</div>
|
||||
<section className="builder-card">
|
||||
<header className="builder-card-header">
|
||||
<div>
|
||||
<h2>5 — Mapping and execution</h2>
|
||||
<p>
|
||||
Translate MCP input to request fields, then map upstream output back to tool
|
||||
output.
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
<div className="builder-card-body">
|
||||
<div className="section-divider">
|
||||
<span className="section-divider-label">Input → Request</span>
|
||||
<div className="section-divider-line" />
|
||||
</div>
|
||||
<div className="form-grid">{mappingFields.slice(0, 1).map(renderField)}</div>
|
||||
|
||||
<div className="section-divider">
|
||||
<span className="section-divider-label">Response → Output</span>
|
||||
<div className="section-divider-line" />
|
||||
</div>
|
||||
<div className="form-grid">{mappingFields.slice(1).map(renderField)}</div>
|
||||
<div className="section-divider">
|
||||
<span className="section-divider-label">Response → Output</span>
|
||||
<div className="section-divider-line" />
|
||||
</div>
|
||||
<div className="form-grid">{mappingFields.slice(1).map(renderField)}</div>
|
||||
|
||||
<div className="section-divider">
|
||||
<span className="section-divider-label">Headers And Runtime</span>
|
||||
<div className="section-divider-line" />
|
||||
<div className="section-divider">
|
||||
<span className="section-divider-label">Headers And Runtime</span>
|
||||
<div className="section-divider-line" />
|
||||
</div>
|
||||
<div className="form-grid">{headerFields.map(renderField)}</div>
|
||||
</div>
|
||||
<div className="form-grid">{headerFields.map(renderField)}</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
{creationMutation.error ? (
|
||||
<div className="feedback-card feedback-error">
|
||||
{creationMutation.error instanceof ApiError
|
||||
? creationMutation.error.message
|
||||
: "Failed to create operation"}
|
||||
</div>
|
||||
) : null}
|
||||
{creationMutation.error ? (
|
||||
<div className="feedback-card feedback-error">
|
||||
{creationMutation.error instanceof ApiError
|
||||
? creationMutation.error.message
|
||||
: "Failed to create operation"}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="sticky-action-bar">
|
||||
<div className="sticky-action-copy">
|
||||
|
||||
@@ -60,6 +60,7 @@ export function AppShell({ children }: AppShellProps) {
|
||||
<p className="sidebar-label">Workspace</p>
|
||||
<nav className="sidebar-nav">
|
||||
<NavLink
|
||||
end
|
||||
to="/operations"
|
||||
className={({ isActive }) =>
|
||||
isActive ? "nav-link nav-link-active" : "nav-link"
|
||||
|
||||
+24
-5
@@ -324,7 +324,8 @@ button {
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
max-width: 1240px;
|
||||
margin: 0 auto 24px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
@@ -405,7 +406,14 @@ button {
|
||||
|
||||
.builder-stack {
|
||||
width: 100%;
|
||||
max-width: 1320px;
|
||||
}
|
||||
|
||||
.builder-content {
|
||||
width: 100%;
|
||||
max-width: 1240px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.page-section,
|
||||
@@ -470,6 +478,11 @@ button {
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
|
||||
.builder-content .form-grid {
|
||||
grid-template-columns: repeat(2, minmax(280px, 420px));
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.proto-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
@@ -859,8 +872,9 @@ button {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 12px 20px;
|
||||
border-radius: var(--radius-xl);
|
||||
width: 100%;
|
||||
padding: 12px 28px;
|
||||
border-radius: 0;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(16px);
|
||||
border: 1px solid var(--border);
|
||||
@@ -1114,7 +1128,7 @@ pre {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.builder-stack {
|
||||
.builder-content {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
@@ -1144,4 +1158,9 @@ pre {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.sticky-action-bar {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user