Showing the changes to plugins/openapi (2 of 17 files). Show all
Unified · Split
| @@ -15,7 +15,6 @@ import { · 14 unmodified lines | |||
| 15 | import { | 15 | import { |
| 16 | Alert, | 16 | Alert, |
| 17 | Button, | 17 | Button, |
| 18 | Card, | ||
| 19 | DocumentPanel, | 18 | DocumentPanel, |
| 20 | IconPencil, | 19 | IconPencil, |
| 21 | Input, | 20 | Input, |
| @@ -149,7 +148,7 @@ const Section = (props: { · 127 unmodified lines | |||
| 149 | children: ReactNode; | 148 | children: ReactNode; |
| 150 | }) => ( | 149 | }) => ( |
| 151 | <section> | 150 | <section> |
| 152 | <h2 className="mb-3 text-sm font-semibold text-foreground">{props.title}</h2> | 151 | <h2 className="mb-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">{props.title}</h2> |
| 153 | {props.children} | 152 | {props.children} |
| 154 | </section> | 153 | </section> |
| 155 | ); | 154 | ); |
| @@ -387,7 +386,7 @@ function OpenApiSourceForm(props: { · 231 unmodified lines | |||
| 387 | }; | 386 | }; |
| 388 | 387 | ||
| 389 | return ( | 388 | return ( |
| 390 | <Card className="space-y-6 p-6"> | 389 | <div className="space-y-6 rounded-lg border border-border bg-card p-6 text-sm ring-1 ring-foreground/[0.04]"> |
| 391 | <Section title="Connection"> | 390 | <Section title="Connection"> |
| 392 | <div className="grid gap-4"> | 391 | <div className="grid gap-4"> |
| 393 | <div className="grid gap-2"> | 392 | <div className="grid gap-2"> |
| @@ -477,59 +476,47 @@ function OpenApiSourceForm(props: { · 83 unmodified lines | |||
| 477 | </div> | 476 | </div> |
| 478 | </Section> | 477 | </Section> |
| 479 | 478 | ||
| 480 | <Section title="Preview"> | 479 | {(preview || previewMutation.status === "pending") && ( |
| 481 | <div className="flex items-center gap-3"> | 480 | <Section title="Preview"> |
| 482 | <Button | 481 | {previewMutation.status === "pending" ? ( |
| 483 | variant="outline" | 482 | <div className="text-xs text-muted-foreground">Loading preview...</div> |
| 484 | type="button" | 483 | ) : preview && ( |
| 485 | onClick={() => { | 484 | <div className="rounded-lg border border-border bg-muted/30 px-4 py-3 text-sm"> |
| 486 | void runPreview({ mode: "manual" }); | 485 | <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5 text-xs"> |
| 487 | }} | 486 | <dt className="font-medium text-foreground">Title</dt> |
| 488 | disabled={previewMutation.status === "pending" || submitMutation.status === "pending"} | 487 | <dd className="text-muted-foreground">{preview.title ?? "—"}</dd> |
| 489 | > | 488 | <dt className="font-medium text-foreground">Version</dt> |
| 490 | {previewMutation.status === "pending" ? "Previewing..." : "Preview Spec"} | 489 | <dd className="text-muted-foreground">{preview.version ?? "—"}</dd> |
| 491 | </Button> | 490 | <dt className="font-medium text-foreground">Operations</dt> |
| 492 | {preview && ( | 491 | <dd className="text-muted-foreground">{preview.operationCount}</dd> |
| 493 | <div className="text-xs text-muted-foreground"> | 492 | <dt className="font-medium text-foreground">Base URL</dt> |
| 494 | {preview.operationCount} operations | 493 | <dd className="font-mono text-muted-foreground">{preview.baseUrl ?? "—"}</dd> |
| 495 | {preview.version ? ` · v${preview.version}` : ""} | 494 | {preview.namespace && ( |
| 495 | <> | ||
| 496 | <dt className="font-medium text-foreground">Namespace</dt> | ||
| 497 | <dd className="text-muted-foreground">{preview.namespace}</dd> | ||
| 498 | </> | ||
| 499 | )} | ||
| 500 | {preview.securitySchemes.length > 0 && ( | ||
| 501 | <> | ||
| 502 | <dt className="font-medium text-foreground">Auth</dt> | ||
| 503 | <dd className="text-muted-foreground"> | ||
| 504 | {preview.securitySchemes | ||
| 505 | .map((scheme) => `${scheme.name} (${previewSecuritySchemeLabel(scheme)})`) | ||
| 506 | .join(", ")} | ||
| 507 | </dd> | ||
| 508 | </> | ||
| 509 | )} | ||
| 510 | </dl> | ||
| 511 | {preview.warnings.length > 0 && ( | ||
| 512 | <Alert variant="warning" className="mt-3 text-xs"> | ||
| 513 | {preview.warnings.join(" ")} | ||
| 514 | </Alert> | ||
| 515 | )} | ||
| 496 | </div> | 516 | </div> |
| 497 | )} | 517 | )} |
| 498 | </div> | 518 | </Section> |
| 499 | {preview && ( | 519 | )} |
| 500 | <div className="mt-3 rounded-lg border border-border bg-muted/30 px-4 py-3 text-sm"> | ||
| 501 | <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5 text-xs"> | ||
| 502 | <dt className="font-medium text-foreground">Title</dt> | ||
| 503 | <dd className="text-muted-foreground">{preview.title ?? "—"}</dd> | ||
| 504 | <dt className="font-medium text-foreground">Version</dt> | ||
| 505 | <dd className="text-muted-foreground">{preview.version ?? "—"}</dd> | ||
| 506 | <dt className="font-medium text-foreground">Base URL</dt> | ||
| 507 | <dd className="font-mono text-muted-foreground">{preview.baseUrl ?? "—"}</dd> | ||
| 508 | {preview.namespace && ( | ||
| 509 | <> | ||
| 510 | <dt className="font-medium text-foreground">Namespace</dt> | ||
| 511 | <dd className="text-muted-foreground">{preview.namespace}</dd> | ||
| 512 | </> | ||
| 513 | )} | ||
| 514 | {preview.securitySchemes.length > 0 && ( | ||
| 515 | <> | ||
| 516 | <dt className="font-medium text-foreground">Auth</dt> | ||
| 517 | <dd className="text-muted-foreground"> | ||
| 518 | {preview.securitySchemes | ||
| 519 | .map((scheme) => `${scheme.name} (${previewSecuritySchemeLabel(scheme)})`) | ||
| 520 | .join(", ")} | ||
| 521 | </dd> | ||
| 522 | </> | ||
| 523 | )} | ||
| 524 | </dl> | ||
| 525 | {preview.warnings.length > 0 && ( | ||
| 526 | <Alert variant="warning" className="mt-3 text-xs"> | ||
| 527 | {preview.warnings.join(" ")} | ||
| 528 | </Alert> | ||
| 529 | )} | ||
| 530 | </div> | ||
| 531 | )} | ||
| 532 | </Section> | ||
| 533 | 520 | ||
| 534 | {error && ( | 521 | {error && ( |
| 535 | <Alert variant="destructive"> | 522 | <Alert variant="destructive"> |
| @@ -548,7 +535,7 @@ function OpenApiSourceForm(props: { · 12 unmodified lines | |||
| 548 | {submitMutation.status === "pending" ? props.busyLabel : props.submitLabel} | 535 | {submitMutation.status === "pending" ? props.busyLabel : props.submitLabel} |
| 549 | </Button> | 536 | </Button> |
| 550 | </div> | 537 | </div> |
| 551 | </Card> | 538 | </div> |
| 552 | ); | 539 | ); |
| 553 | } | 540 | } |
| 554 | 541 | ||
| @@ -564,35 +551,50 @@ export function OpenApiAddSourcePage() { · 9 unmodified lines | |||
| 564 | 551 | ||
| 565 | if (!workspace.enabled) { | 552 | if (!workspace.enabled) { |
| 566 | return ( | 553 | return ( |
| 567 | <Section title="Add Source"> | 554 | <div className="min-h-0 flex-1 overflow-y-auto"> |
| 568 | <div className="text-sm text-muted-foreground">Loading workspace...</div> | 555 | <div className="mx-auto w-full max-w-3xl px-6 py-10"> |
| 569 | </Section> | 556 | <div className="text-sm text-muted-foreground">Loading workspace...</div> |
| 557 | </div> | ||
| 558 | </div> | ||
| 570 | ); | 559 | ); |
| 571 | } | 560 | } |
| 572 | 561 | ||
| 573 | return ( | 562 | return ( |
| 574 | <OpenApiSourceForm | 563 | <div className="min-h-0 flex-1 overflow-y-auto"> |
| 575 | mode="create" | 564 | <div className="mx-auto w-full max-w-3xl px-6 py-10"> |
| 576 | workspaceId={workspace.workspaceId} | 565 | <div className="mb-8"> |
| 577 | initialValue={initialValue} | 566 | <h1 className="font-display text-2xl tracking-tight text-foreground"> |
| 578 | submitLabel="Create Source" | 567 | Add OpenAPI Source |
| 579 | busyLabel="Creating..." | 568 | </h1> |
| 580 | onSubmit={async (payload) => { | 569 | <p className="mt-1.5 text-sm leading-6 text-muted-foreground"> |
| 581 | const source = await createSource({ | 570 | Import operations from an OpenAPI specification as callable tools. |
| 582 | path: { workspaceId: workspace.workspaceId }, | 571 | </p> |
| 583 | payload, | 572 | </div> |
| 584 | reactivityKeys: { | 573 | |
| 585 | sources: [workspace.workspaceId], | 574 | <OpenApiSourceForm |
| 586 | }, | 575 | mode="create" |
| 587 | }); | 576 | workspaceId={workspace.workspaceId} |
| 588 | 577 | initialValue={initialValue} | |
| 589 | startTransition(() => { | 578 | submitLabel="Create Source" |
| 590 | void navigation.detail(source.id, { | 579 | busyLabel="Creating..." |
| 591 | tab: "model", | 580 | onSubmit={async (payload) => { |
| 592 | }); | 581 | const source = await createSource({ |
| 593 | }); | 582 | path: { workspaceId: workspace.workspaceId }, |
| 594 | }} | 583 | payload, |
| 595 | /> | 584 | reactivityKeys: { |
| 585 | sources: [workspace.workspaceId], | ||
| 586 | }, | ||
| 587 | }); | ||
| 588 | |||
| 589 | startTransition(() => { | ||
| 590 | void navigation.detail(source.id, { | ||
| 591 | tab: "model", | ||
| 592 | }); | ||
| 593 | }); | ||
| 594 | }} | ||
| 595 | /> | ||
| 596 | </div> | ||
| 597 | </div> | ||
| 596 | ); | 598 | ); |
| 597 | } | 599 | } |
| 598 | 600 | ||
| @@ -603,9 +605,11 @@ export function OpenApiEditSourcePage(props: { · 4 unmodified lines | |||
| 603 | 605 | ||
| 604 | if (!workspace.enabled) { | 606 | if (!workspace.enabled) { |
| 605 | return ( | 607 | return ( |
| 606 | <Section title="Edit Source"> | 608 | <div className="min-h-0 flex-1 overflow-y-auto"> |
| 607 | <div className="text-sm text-muted-foreground">Loading workspace...</div> | 609 | <div className="mx-auto w-full max-w-3xl px-6 py-10"> |
| 608 | </Section> | 610 | <div className="text-sm text-muted-foreground">Loading workspace...</div> |
| 611 | </div> | ||
| 612 | </div> | ||
| 609 | ); | 613 | ); |
| 610 | } | 614 | } |
| 611 | 615 | ||
| @@ -641,53 +645,64 @@ function OpenApiEditSourcePageReady(props: { · 29 unmodified lines | |||
| 641 | ); | 645 | ); |
| 642 | 646 | ||
| 643 | if (!Result.isSuccess(configResult)) { | 647 | if (!Result.isSuccess(configResult)) { |
| 644 | if (Result.isFailure(configResult)) { | ||
| 645 | return ( | ||
| 646 | <Section title="Edit Source"> | ||
| 647 | <Alert variant="destructive"> | ||
| 648 | Failed loading source configuration. | ||
| 649 | </Alert> | ||
| 650 | </Section> | ||
| 651 | ); | ||
| 652 | } | ||
| 653 | |||
| 654 | return ( | 648 | return ( |
| 655 | <Section title="Edit Source"> | 649 | <div className="min-h-0 flex-1 overflow-y-auto"> |
| 656 | <div className="text-sm text-muted-foreground">Loading configuration...</div> | 650 | <div className="mx-auto w-full max-w-3xl px-6 py-10"> |
| 657 | </Section> | 651 | {Result.isFailure(configResult) ? ( |
| 652 | <Alert variant="destructive"> | ||
| 653 | Failed loading source configuration. | ||
| 654 | </Alert> | ||
| 655 | ) : ( | ||
| 656 | <div className="text-sm text-muted-foreground">Loading configuration...</div> | ||
| 657 | )} | ||
| 658 | </div> | ||
| 659 | </div> | ||
| 658 | ); | 660 | ); |
| 659 | } | 661 | } |
| 660 | 662 | ||
| 661 | return ( | 663 | return ( |
| 662 | <OpenApiSourceForm | 664 | <div className="min-h-0 flex-1 overflow-y-auto"> |
| 663 | mode="edit" | 665 | <div className="mx-auto w-full max-w-3xl px-6 py-10"> |
| 664 | workspaceId={props.workspaceId} | 666 | <div className="mb-8"> |
| 665 | initialValue={inputFromConfig(configResult.value)} | 667 | <h1 className="font-display text-2xl tracking-tight text-foreground"> |
| 666 | submitLabel="Save Changes" | 668 | Edit OpenAPI Source |
| 667 | busyLabel="Saving..." | 669 | </h1> |
| 668 | onSubmit={async (config) => { | 670 | <p className="mt-1.5 text-sm leading-6 text-muted-foreground"> |
| 669 | const source = await updateSource({ | 671 | Update the connection settings for {props.source.name}. |
| 670 | path: { | 672 | </p> |
| 671 | workspaceId: props.workspaceId, | 673 | </div> |
| 672 | sourceId: props.source.id, | 674 | |
| 673 | }, | 675 | <OpenApiSourceForm |
| 674 | payload: config, | 676 | mode="edit" |
| 675 | reactivityKeys: { | 677 | workspaceId={props.workspaceId} |
| 676 | sources: [props.workspaceId], | 678 | initialValue={inputFromConfig(configResult.value)} |
| 677 | source: [props.workspaceId, props.source.id], | 679 | submitLabel="Save Changes" |
| 678 | sourceInspection: [props.workspaceId, props.source.id], | 680 | busyLabel="Saving..." |
| 679 | sourceInspectionTool: [props.workspaceId, props.source.id], | 681 | onSubmit={async (config) => { |
| 680 | sourceDiscovery: [props.workspaceId, props.source.id], | 682 | const source = await updateSource({ |
| 681 | }, | 683 | path: { |
| 682 | }); | 684 | workspaceId: props.workspaceId, |
| 683 | 685 | sourceId: props.source.id, | |
| 684 | startTransition(() => { | 686 | }, |
| 685 | void navigation.detail(source.id, { | 687 | payload: config, |
| 686 | tab: "model", | 688 | reactivityKeys: { |
| 687 | }); | 689 | sources: [props.workspaceId], |
| 688 | }); | 690 | source: [props.workspaceId, props.source.id], |
| 689 | }} | 691 | sourceInspection: [props.workspaceId, props.source.id], |
| 690 | /> | 692 | sourceInspectionTool: [props.workspaceId, props.source.id], |
| 693 | sourceDiscovery: [props.workspaceId, props.source.id], | ||
| 694 | }, | ||
| 695 | }); | ||
| 696 | |||
| 697 | startTransition(() => { | ||
| 698 | void navigation.detail(source.id, { | ||
| 699 | tab: "model", | ||
| 700 | }); | ||
| 701 | }); | ||
| 702 | }} | ||
| 703 | /> | ||
| 704 | </div> | ||
| 705 | </div> | ||
| 691 | ); | 706 | ); |
| 692 | } | 707 | } |
| 693 | 708 | ||
| @@ -1,4 +1,7 @@ | |||
| 1 | import { defineExecutorFrontendPlugin } from "@executor/react/plugins"; | 1 | import { |
| 2 | defineExecutorFrontendPlugin, | ||
| 3 | liveFrontendPluginComponent, | ||
| 4 | } from "@executor/react/plugins"; | ||
| 2 | 5 | ||
| 3 | import { | 6 | import { |
| 4 | OpenApiAddSourcePage, | 7 | OpenApiAddSourcePage, |
| @@ -13,17 +16,17 @@ export const OpenApiReactPlugin = defineExecutorFrontendPlugin({ · 8 unmodified lines | |||
| 13 | { | 16 | { |
| 14 | key: "add", | 17 | key: "add", |
| 15 | path: "add", | 18 | path: "add", |
| 16 | component: OpenApiAddSourcePage, | 19 | component: liveFrontendPluginComponent(() => OpenApiAddSourcePage), |
| 17 | }, | 20 | }, |
| 18 | { | 21 | { |
| 19 | key: "detail", | 22 | key: "detail", |
| 20 | path: "sources/$sourceId", | 23 | path: "sources/$sourceId", |
| 21 | component: OpenApiDetailRoute, | 24 | component: liveFrontendPluginComponent(() => OpenApiDetailRoute), |
| 22 | }, | 25 | }, |
| 23 | { | 26 | { |
| 24 | key: "edit", | 27 | key: "edit", |
| 25 | path: "sources/$sourceId/edit", | 28 | path: "sources/$sourceId/edit", |
| 26 | component: OpenApiEditRoute, | 29 | component: liveFrontendPluginComponent(() => OpenApiEditRoute), |
| 27 | }, | 30 | }, |
| 28 | ], | 31 | ], |
| 29 | }); | 32 | }); |