History

feat(web): refresh source plugin screens and hot reload

Rhys Sullivan committed 7 months ago

Commit e7a8bd41 parent f1b09f317 files · +799 −943Browse files

Showing the changes to plugins/openapi (2 of 17 files). Show all

Unified · Split

2 files
  1. plugins/openapi/react/components.tsx +140 −125
  2. plugins/openapi/react/index.ts +7 −4
plugins/openapi/react/components.tsx+140−125
@@ -15,7 +15,6 @@ import { · 14 unmodified lines
1515 import {
1616 Alert,
1717 Button,
18− Card,
1918 DocumentPanel,
2019 IconPencil,
2120 Input,
@@ -149,7 +148,7 @@ const Section = (props: { · 127 unmodified lines
149148 children: ReactNode;
150149 }) => (
151150 <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>
153152 {props.children}
154153 </section>
155154 );
@@ -387,7 +386,7 @@ function OpenApiSourceForm(props: { · 231 unmodified lines
387386 };
388387
389388 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]">
391390 <Section title="Connection">
392391 <div className="grid gap-4">
393392 <div className="grid gap-2">
@@ -477,59 +476,47 @@ function OpenApiSourceForm(props: { · 83 unmodified lines
477476 </div>
478477 </Section>
479478
480− <Section title="Preview">
481− <div className="flex items-center gap-3">
482− <Button
483− variant="outline"
484− type="button"
485− onClick={() => {
486− void runPreview({ mode: "manual" });
487− }}
488− disabled={previewMutation.status === "pending" || submitMutation.status === "pending"}
489− >
490− {previewMutation.status === "pending" ? "Previewing..." : "Preview Spec"}
491− </Button>
492− {preview && (
493− <div className="text-xs text-muted-foreground">
494− {preview.operationCount} operations
495− {preview.version ? ` · v${preview.version}` : ""}
479+ {(preview || previewMutation.status === "pending") && (
480+ <Section title="Preview">
481+ {previewMutation.status === "pending" ? (
482+ <div className="text-xs text-muted-foreground">Loading preview...</div>
483+ ) : preview && (
484+ <div className="rounded-lg border border-border bg-muted/30 px-4 py-3 text-sm">
485+ <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5 text-xs">
486+ <dt className="font-medium text-foreground">Title</dt>
487+ <dd className="text-muted-foreground">{preview.title ?? "—"}</dd>
488+ <dt className="font-medium text-foreground">Version</dt>
489+ <dd className="text-muted-foreground">{preview.version ?? "—"}</dd>
490+ <dt className="font-medium text-foreground">Operations</dt>
491+ <dd className="text-muted-foreground">{preview.operationCount}</dd>
492+ <dt className="font-medium text-foreground">Base URL</dt>
493+ <dd className="font-mono text-muted-foreground">{preview.baseUrl ?? "—"}</dd>
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+ )}
496516 </div>
497517 )}
498− </div>
499− {preview && (
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>
518+ </Section>
519+ )}
533520
534521 {error && (
535522 <Alert variant="destructive">
@@ -548,7 +535,7 @@ function OpenApiSourceForm(props: { · 12 unmodified lines
548535 {submitMutation.status === "pending" ? props.busyLabel : props.submitLabel}
549536 </Button>
550537 </div>
551− </Card>
538+ </div>
552539 );
553540 }
554541
@@ -564,35 +551,50 @@ export function OpenApiAddSourcePage() { · 9 unmodified lines
564551
565552 if (!workspace.enabled) {
566553 return (
567− <Section title="Add Source">
568− <div className="text-sm text-muted-foreground">Loading workspace...</div>
569− </Section>
554+ <div className="min-h-0 flex-1 overflow-y-auto">
555+ <div className="mx-auto w-full max-w-3xl px-6 py-10">
556+ <div className="text-sm text-muted-foreground">Loading workspace...</div>
557+ </div>
558+ </div>
570559 );
571560 }
572561
573562 return (
574− <OpenApiSourceForm
575− mode="create"
576− workspaceId={workspace.workspaceId}
577− initialValue={initialValue}
578− submitLabel="Create Source"
579− busyLabel="Creating..."
580− onSubmit={async (payload) => {
581− const source = await createSource({
582− path: { workspaceId: workspace.workspaceId },
583− payload,
584− reactivityKeys: {
585− sources: [workspace.workspaceId],
586− },
587− });
588−
589− startTransition(() => {
590− void navigation.detail(source.id, {
591− tab: "model",
592− });
593− });
594− }}
595− />
563+ <div className="min-h-0 flex-1 overflow-y-auto">
564+ <div className="mx-auto w-full max-w-3xl px-6 py-10">
565+ <div className="mb-8">
566+ <h1 className="font-display text-2xl tracking-tight text-foreground">
567+ Add OpenAPI Source
568+ </h1>
569+ <p className="mt-1.5 text-sm leading-6 text-muted-foreground">
570+ Import operations from an OpenAPI specification as callable tools.
571+ </p>
572+ </div>
573+
574+ <OpenApiSourceForm
575+ mode="create"
576+ workspaceId={workspace.workspaceId}
577+ initialValue={initialValue}
578+ submitLabel="Create Source"
579+ busyLabel="Creating..."
580+ onSubmit={async (payload) => {
581+ const source = await createSource({
582+ path: { workspaceId: workspace.workspaceId },
583+ payload,
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>
596598 );
597599 }
598600
@@ -603,9 +605,11 @@ export function OpenApiEditSourcePage(props: { · 4 unmodified lines
603605
604606 if (!workspace.enabled) {
605607 return (
606− <Section title="Edit Source">
607− <div className="text-sm text-muted-foreground">Loading workspace...</div>
608− </Section>
608+ <div className="min-h-0 flex-1 overflow-y-auto">
609+ <div className="mx-auto w-full max-w-3xl px-6 py-10">
610+ <div className="text-sm text-muted-foreground">Loading workspace...</div>
611+ </div>
612+ </div>
609613 );
610614 }
611615
@@ -641,53 +645,64 @@ function OpenApiEditSourcePageReady(props: { · 29 unmodified lines
641645 );
642646
643647 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−
654648 return (
655− <Section title="Edit Source">
656− <div className="text-sm text-muted-foreground">Loading configuration...</div>
657− </Section>
649+ <div className="min-h-0 flex-1 overflow-y-auto">
650+ <div className="mx-auto w-full max-w-3xl px-6 py-10">
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>
658660 );
659661 }
660662
661663 return (
662− <OpenApiSourceForm
663− mode="edit"
664− workspaceId={props.workspaceId}
665− initialValue={inputFromConfig(configResult.value)}
666− submitLabel="Save Changes"
667− busyLabel="Saving..."
668− onSubmit={async (config) => {
669− const source = await updateSource({
670− path: {
671− workspaceId: props.workspaceId,
672− sourceId: props.source.id,
673− },
674− payload: config,
675− reactivityKeys: {
676− sources: [props.workspaceId],
677− source: [props.workspaceId, props.source.id],
678− sourceInspection: [props.workspaceId, props.source.id],
679− sourceInspectionTool: [props.workspaceId, props.source.id],
680− sourceDiscovery: [props.workspaceId, props.source.id],
681− },
682− });
683−
684− startTransition(() => {
685− void navigation.detail(source.id, {
686− tab: "model",
687− });
688− });
689− }}
690− />
664+ <div className="min-h-0 flex-1 overflow-y-auto">
665+ <div className="mx-auto w-full max-w-3xl px-6 py-10">
666+ <div className="mb-8">
667+ <h1 className="font-display text-2xl tracking-tight text-foreground">
668+ Edit OpenAPI Source
669+ </h1>
670+ <p className="mt-1.5 text-sm leading-6 text-muted-foreground">
671+ Update the connection settings for {props.source.name}.
672+ </p>
673+ </div>
674+
675+ <OpenApiSourceForm
676+ mode="edit"
677+ workspaceId={props.workspaceId}
678+ initialValue={inputFromConfig(configResult.value)}
679+ submitLabel="Save Changes"
680+ busyLabel="Saving..."
681+ onSubmit={async (config) => {
682+ const source = await updateSource({
683+ path: {
684+ workspaceId: props.workspaceId,
685+ sourceId: props.source.id,
686+ },
687+ payload: config,
688+ reactivityKeys: {
689+ sources: [props.workspaceId],
690+ source: [props.workspaceId, props.source.id],
691+ sourceInspection: [props.workspaceId, props.source.id],
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>
691706 );
692707 }
693708
plugins/openapi/react/index.ts+7−4
@@ -1,4 +1,7 @@
1−import { defineExecutorFrontendPlugin } from "@executor/react/plugins";
1+import {
2+ defineExecutorFrontendPlugin,
3+ liveFrontendPluginComponent,
4+} from "@executor/react/plugins";
25
36 import {
47 OpenApiAddSourcePage,
@@ -13,17 +16,17 @@ export const OpenApiReactPlugin = defineExecutorFrontendPlugin({ · 8 unmodified lines
1316 {
1417 key: "add",
1518 path: "add",
16− component: OpenApiAddSourcePage,
19+ component: liveFrontendPluginComponent(() => OpenApiAddSourcePage),
1720 },
1821 {
1922 key: "detail",
2023 path: "sources/$sourceId",
21− component: OpenApiDetailRoute,
24+ component: liveFrontendPluginComponent(() => OpenApiDetailRoute),
2225 },
2326 {
2427 key: "edit",
2528 path: "sources/$sourceId/edit",
26− component: OpenApiEditRoute,
29+ component: liveFrontendPluginComponent(() => OpenApiEditRoute),
2730 },
2831 ],
2932 });