fix: refine create page layout spacing

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