Progress displaying endpoints and editing them

This commit is contained in:
Matt Aitken
2023-06-19 11:48:33 +01:00
parent ebbbc85d7b
commit 47fdfb344d
4 changed files with 170 additions and 19 deletions
@@ -5,6 +5,7 @@ import {
} from "~/models/indexEndpoint.server";
import { Project } from "~/models/project.server";
import { User } from "~/models/user.server";
import { RuntimeEnvironmentType } from "../../../../packages/database/src";
export type Client = {
slug: string;
@@ -18,6 +19,11 @@ export type Client = {
export type ClientEndpoint =
| {
state: "unconfigured";
environment: {
id: string;
apiKey: string;
type: RuntimeEnvironmentType;
};
}
| {
state: "configured";
@@ -30,6 +36,11 @@ export type ClientEndpoint =
updatedAt: Date;
stats: IndexEndpointStats;
};
environment: {
id: string;
apiKey: string;
type: RuntimeEnvironmentType;
};
};
export class EnvironmentsPresenter {
@@ -98,8 +109,6 @@ export class EnvironmentsPresenter {
return true;
});
console.log("filtered", JSON.stringify(filtered));
//build up list of clients for display, with endpoints by type
const clients: Client[] = [];
for (const environment of filtered) {
@@ -109,9 +118,9 @@ export class EnvironmentsPresenter {
client = {
slug: endpoint.slug,
endpoints: {
DEVELOPMENT: { state: "unconfigured" },
STAGING: { state: "unconfigured" },
PRODUCTION: { state: "unconfigured" },
DEVELOPMENT: { state: "unconfigured", environment },
STAGING: { state: "unconfigured", environment },
PRODUCTION: { state: "unconfigured", environment },
},
};
clients.push(client);
@@ -137,6 +146,7 @@ export class EnvironmentsPresenter {
stats: parseEndpointIndexStats(latestIndex.stats),
}
: undefined,
environment,
};
});
}
@@ -1,12 +1,27 @@
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
import { Header1 } from "~/components/primitives/Headers";
import { Header1, Header2 } from "~/components/primitives/Headers";
import {
Sheet,
SheetBody,
SheetContent,
SheetHeader,
} from "~/components/primitives/Sheet";
import { ClientEndpoint } from "~/presenters/EnvironmentsPresenter.server";
import { RuntimeEnvironmentType } from "../../../../../packages/database/src";
import { useFetcher } from "@remix-run/react";
import { InputGroup } from "~/components/primitives/InputGroup";
import { Input } from "~/components/primitives/Input";
import { Button } from "~/components/primitives/Buttons";
import { Label } from "~/components/primitives/Label";
import { Hint } from "~/components/primitives/Hint";
import { InlineCode } from "~/components/code/InlineCode";
import { Paragraph } from "~/components/primitives/Paragraph";
import { Callout } from "~/components/primitives/Callout";
import { formatDateTime } from "~/utils";
import { conform, useForm } from "@conform-to/react";
import { parse } from "@conform-to/zod";
import { bodySchema } from "../resources.environments.$environmentParam.endpoint";
import { FormError } from "~/components/primitives/FormError";
type ConfigureEndpointSheetProps = {
slug: string;
@@ -18,9 +33,21 @@ type ConfigureEndpointSheetProps = {
export function ConfigureEndpointSheet({
slug,
endpoint,
type,
onClose,
}: ConfigureEndpointSheetProps) {
const setEndpointUrlFetcher = useFetcher();
const [form, { url, clientSlug }] = useForm({
id: "endpoint-url",
lastSubmission: setEndpointUrlFetcher.data,
onValidate({ formData }) {
return parse(formData, { schema: bodySchema });
},
});
const loadingEndpointUrl = setEndpointUrlFetcher.state !== "idle";
console.log("endpoint", endpoint);
return (
<Sheet
open={true}
@@ -33,9 +60,73 @@ export function ConfigureEndpointSheet({
<SheetContent>
<SheetHeader>
<Header1>
<EnvironmentLabel environment={{ type }} />
<div className="flex items-center gap-2">
<EnvironmentLabel
environment={{ type: endpoint.environment.type }}
/>
<Header1>Configure endpoint</Header1>
</div>
</Header1>
</SheetHeader>
<SheetBody>
<setEndpointUrlFetcher.Form
method="post"
action={`/resources/environments/${endpoint.environment.id}/endpoint`}
{...form.props}
>
<InputGroup className="max-w-none">
<Label>Endpoint URL</Label>
<div className="flex items-center">
<input
{...conform.input(clientSlug, { type: "hidden" })}
value={slug}
/>
<Input
className="rounded-r-none"
{...conform.input(url, { type: "url" })}
defaultValue={"url" in endpoint ? endpoint.url : ""}
placeholder="Path to your Trigger API route"
/>
<Button
type="submit"
variant="primary/medium"
className="rounded-l-none"
disabled={loadingEndpointUrl}
LeadingIcon={loadingEndpointUrl ? "spinner-white" : undefined}
>
{loadingEndpointUrl ? "Saving" : "Save"}
</Button>
</div>
<FormError id={url.errorId}>{url.error}</FormError>
<FormError id={form.errorId}>{form.error}</FormError>
<Hint>
This is the URL of your Trigger API route, Typically this would
be:{" "}
<InlineCode variant="extra-small">
https://yourdomain.com/api/trigger
</InlineCode>
.
</Hint>
</InputGroup>
</setEndpointUrlFetcher.Form>
{endpoint.state === "configured" && (
<div className="mt-4 flex flex-col">
<div>
<Header2>Status</Header2>
<Paragraph>
We connect to your endpoint and refresh your Jobs.
</Paragraph>
<Callout variant="success">
Endpoint configured. Last refreshed:{" "}
{endpoint.latestIndex
? formatDateTime(endpoint.latestIndex.updatedAt)
: ""}
</Callout>
</div>
</div>
)}
</SheetBody>
</SheetContent>
</Sheet>
);
@@ -1,4 +1,5 @@
import { LoaderArgs } from "@remix-run/server-runtime";
import { useMemo, useState } from "react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { ClipboardField } from "~/components/ClipboardField";
import {
@@ -6,6 +7,7 @@ import {
environmentTitle,
} from "~/components/environments/EnvironmentLabel";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import { ButtonContent } from "~/components/primitives/Buttons";
import { Header1, Header2 } from "~/components/primitives/Headers";
import {
PageDescription,
@@ -23,25 +25,15 @@ import {
TableHeaderCell,
TableRow,
} from "~/components/primitives/Table";
import { useProject } from "~/hooks/useProject";
import {
ClientEndpoint,
EnvironmentsPresenter,
} from "~/presenters/EnvironmentsPresenter.server";
import { requireUserId } from "~/services/session.server";
import { formatDateTime } from "~/utils";
import { Handle } from "~/utils/handle";
import { ProjectParamSchema } from "~/utils/pathBuilder";
import { RuntimeEnvironmentType } from "../../../../../packages/database/src";
import { formatDateTime } from "~/utils";
import { Button, ButtonContent } from "~/components/primitives/Buttons";
import { useMemo, useState } from "react";
import { s } from "vitest/dist/index-6e18a03a";
import {
Sheet,
SheetContent,
SheetHeader,
} from "~/components/primitives/Sheet";
import { select } from "@conform-to/react/helpers";
import { ConfigureEndpointSheet } from "./ConfigureEndpointSheet";
export const loader = async ({ request, params }: LoaderArgs) => {
@@ -0,0 +1,58 @@
import { parse } from "@conform-to/zod";
import { ActionArgs, json } from "@remix-run/server-runtime";
import { z } from "zod";
import { prisma } from "~/db.server";
import { CreateEndpointService } from "~/services/endpoints/createEndpoint.server";
import { requireUserId } from "~/services/session.server";
const ParamsSchema = z.object({
environmentParam: z.string(),
});
export const bodySchema = z.object({
clientSlug: z.string(),
url: z.string().url("Must be a valid URL"),
});
export async function action({ request, params }: ActionArgs) {
const userId = await requireUserId(request);
const { environmentParam } = ParamsSchema.parse(params);
const formData = await request.formData();
const object = Object.fromEntries(formData.entries());
console.log("object", object);
const submission = parse(formData, { schema: bodySchema });
console.log(submission);
if (!submission.value || submission.intent !== "submit") {
return json(submission);
}
try {
const environment = await prisma.runtimeEnvironment.findUnique({
include: {
organization: true,
project: true,
},
where: {
id: environmentParam,
},
});
if (!environment) {
throw new Error("Environment not found");
}
const service = new CreateEndpointService();
const result = await service.call({
id: submission.value.clientSlug,
url: submission.value.url,
environment,
});
return json(result);
} catch (e) {
return json(e, { status: 400 });
}
}