Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/app/dialogs/connect-aws-frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import * as ConnectVercelForm from "@/app/forms/connect-vercel-form";
import { cn, type DialogContentProps, Frame } from "@/components";
import { type Region, useEngineCompatDataProvider } from "@/components/actors";
import { defineStepper } from "@/components/ui/stepper";
import { queryClient } from "@/queries/global";
import { StepperForm } from "../forms/stepper-form";
import { EnvVariablesStep } from "./connect-railway-frame";

Expand Down
31 changes: 11 additions & 20 deletions frontend/src/app/dialogs/connect-railway-frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
import { useEngineCompatDataProvider } from "@/components/actors";
import { defineStepper } from "@/components/ui/stepper";
import { engineEnv } from "@/lib/env";
import { queryClient } from "@/queries/global";
import { useRailwayTemplateLink } from "@/utils/use-railway-template-link";
import { StepperForm } from "../forms/stepper-form";

const stepper = defineStepper(
Expand Down Expand Up @@ -120,15 +122,16 @@ function FormStepper({
origin: { x: 1 },
});

await queryClient.invalidateQueries(provider.runnerConfigsQueryOptions());
await queryClient.invalidateQueries(
provider.runnerConfigsQueryOptions(),
);
onClose?.();
},
});
return (
<StepperForm
{...stepper}
onSubmit={async ({ values }) => {
console.log(values);
await mutateAsync({
name: values.runnerName,
config: {
Expand Down Expand Up @@ -328,18 +331,16 @@ function RivetNamespaceEnv() {
}

function DeployToRailwayButton() {
const dataProvider = useEngineCompatDataProvider();
const url = useSelectedDatacenter();
const { data } = useQuery(dataProvider.engineAdminTokenQueryOptions());
const runnerName = useWatch({ name: "runnerName" });

const url = useRailwayTemplateLink({
runnerName: runnerName || "default",
datacenter: useWatch({ name: "datacenter" }) || "auto",
});

return (
<a
href={`https://railway.com/new/template/rivet-cloud-starter?referralCode=RC7bza&utm_medium=integration&utm_source=template&utm_campaign=generic&RIVET_TOKEN=${data || ""}&RIVET_ENDPOINT=${
url || ""
}&RIVET_NAMESPACE=${
dataProvider.engineNamespace || ""
}&RIVET_RUNNER=${runnerName || ""}`}
href={url}
target="_blank"
rel="noreferrer"
className="inline-block h-10"
Expand All @@ -352,13 +353,3 @@ function DeployToRailwayButton() {
</a>
);
}

const useSelectedDatacenter = () => {
const datacenter = useWatch({ name: "datacenter" });

const { data } = useQuery(
useEngineCompatDataProvider().regionQueryOptions(datacenter || "auto"),
);

return data?.url || engineEnv().VITE_APP_API_URL;
};
5 changes: 3 additions & 2 deletions frontend/src/app/dialogs/edit-runner-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export default function EditRunnerConfigFrameContent({
},
});

const config = data.find(([id]) => id === name)?.[1].datacenters?.[dc]
.serverless;
const datacenters = data.find(([id]) => id === name)?.[1].datacenters;
const config = datacenters?.[dc].serverless;

if (!config) {
return (
Expand All @@ -52,6 +52,7 @@ export default function EditRunnerConfigFrameContent({
await mutateAsync({
name,
config: {
...datacenters,
[dc]: {
serverless: {
...values,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/forms/edit-runner-config-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from "@/components";

export const formSchema = z.object({
url: z.string().url().endsWith("/api/rivet"),
url: z.string().url(),
maxRunners: z.coerce.number().positive(),
minRunners: z.coerce.number().min(0),
requestLifespan: z.coerce.number().positive(),
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/runner-config-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function Row({
<WithTooltip
content={config.serverless?.url || "-"}
trigger={
<DiscreteCopyButton value={name}>
<DiscreteCopyButton value={config.serverless?.url}>
<span>
{config.serverless?.url &&
config.serverless.url.length > 32
Expand Down
73 changes: 65 additions & 8 deletions frontend/src/app/runners-table.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { faHourglassClock, Icon } from "@rivet-gg/icons";
import { faHourglassClock, faPlus, Icon } from "@rivet-gg/icons";
import type { Rivet } from "@rivetkit/engine-api-full";
import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { formatRelative } from "date-fns";
import {
Button,
Expand All @@ -15,6 +17,7 @@ import {
Text,
WithTooltip,
} from "@/components";
import { useEngineCompatDataProvider } from "@/components/actors";

interface RunnersTableProps {
isLoading?: boolean;
Expand Down Expand Up @@ -46,13 +49,7 @@ export function RunnersTable({
</TableHeader>
<TableBody>
{!isLoading && !isError && runners?.length === 0 ? (
<TableRow>
<TableCell colSpan={7}>
<Text className="text-center">
There's no runners matching criteria.
</Text>
</TableCell>
</TableRow>
<EmptyState />
) : null}
{isError ? (
<TableRow>
Expand Down Expand Up @@ -197,3 +194,63 @@ function RunnerStatusBadge(runner: Rivet.Runner) {
/>
);
}

function EmptyState() {
const { data: serverlessConfig } = useInfiniteQuery({
...useEngineCompatDataProvider().runnerConfigsQueryOptions(),
select(data) {
for (const page of data.pages) {
for (const rc of Object.values(page.runnerConfigs)) {
for (const [dc, config] of Object.entries(rc.datacenters)) {
if (config.serverless) {
return config;
}
}
}
}
return null;
},
});

const { data: actorNames } = useInfiniteQuery({
...useEngineCompatDataProvider().buildsQueryOptions(),
select(data) {
return data.pages[0].builds.length > 0;
},
});

return (
<TableRow>
<TableCell colSpan={7}>
{serverlessConfig ? (
<>
<Text className="text-center">
Runners will be created when an actor is created.
</Text>
{actorNames ? (
<div className="text-center mt-2">
<Button
asChild
size="sm"
startIcon={<Icon icon={faPlus} />}
>
<Link
to="."
search={{ modal: "create-actor" }}
>
Create Actor
</Link>
</Button>
</div>
) : null}
</>
) : (
<Text className="text-center">
There are no runners connected. You will not be able to
run actors until a runner appears here.
</Text>
)}
</TableCell>
</TableRow>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ export default function CreateActorDialog({ onClose }: ContentProps) {
}}
defaultValues={{
name,
key: "example-key",
crashPolicy: CrashPolicy.Destroy,
datacenter: "auto",
}}
>
<DialogHeader>
Expand All @@ -62,7 +62,11 @@ export default function CreateActorDialog({ onClose }: ContentProps) {
<ActorCreateForm.Keys />
<ActorCreateForm.ActorPreview />
{["engine", "cloud"].includes(__APP_TYPE__) ? (
<ActorCreateForm.PrefillRunnerName />
<>
<ActorCreateForm.PrefillActorName />
<ActorCreateForm.PrefillRunnerName />
<ActorCreateForm.PrefillDatacenter />
</>
) : null}

<Accordion type="single" collapsible>
Expand Down
58 changes: 56 additions & 2 deletions frontend/src/components/actors/form/actor-create-form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { useInfiniteQuery } from "@tanstack/react-query";
import {
useInfiniteQuery,
useSuspenseInfiniteQuery,
} from "@tanstack/react-query";
import { useEffect, useRef } from "react";
import { type UseFormReturn, useFormContext } from "react-hook-form";
import z from "zod";
Expand Down Expand Up @@ -212,11 +215,34 @@ export const ActorPreview = () => {
);
};

export const PrefillActorName = () => {
const prefilled = useRef(false);
const { watch } = useFormContext<FormValues>();

const { data: name, isSuccess } = useSuspenseInfiniteQuery({
...useEngineCompatDataProvider().buildsQueryOptions(),
select: (data) => data.pages[0].builds[0].name,
});

const watchedValue = watch("name");

const { setValue } = useFormContext<FormValues>();

useEffect(() => {
if (name && isSuccess && !watchedValue && !prefilled.current) {
setValue("name", name);
prefilled.current = true;
}
}, [name, setValue, isSuccess, watchedValue]);

return null;
};

export const PrefillRunnerName = () => {
const prefilled = useRef(false);
const { watch } = useFormContext<FormValues>();

const { data = [], isSuccess } = useInfiniteQuery(
const { data = [], isSuccess } = useSuspenseInfiniteQuery(
useEngineCompatDataProvider().runnerNamesQueryOptions(),
);

Expand All @@ -239,6 +265,33 @@ export const PrefillRunnerName = () => {
return null;
};

export const PrefillDatacenter = () => {
const prefilled = useRef(false);
const { watch } = useFormContext<FormValues>();

const { data: datacenter, isSuccess } = useSuspenseInfiniteQuery({
...useEngineCompatDataProvider().runnerConfigsQueryOptions(),
select: (data) => {
return Object.keys(
Object.values(data.pages[0].runnerConfigs)[0].datacenters,
)[0];
},
});

const watchedValue = watch("datacenter");

const { setValue } = useFormContext<FormValues>();

useEffect(() => {
if (datacenter && isSuccess && !watchedValue && !prefilled.current) {
setValue("datacenter", datacenter);
prefilled.current = true;
}
}, [datacenter, setValue, isSuccess, watchedValue]);

return null;
};

export const Datacenter = () => {
const { control } = useFormContext<FormValues>();

Expand All @@ -251,6 +304,7 @@ export const Datacenter = () => {
<FormLabel>Datacenter</FormLabel>
<FormControl>
<RegionSelect
showAuto={false}
value={field.value}
onValueChange={field.onChange}
/>
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/components/code-preview/code-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,15 @@ export function CodePreview({ className, code, language }: CodePreviewProps) {
);

if (isLoading) {
return <Skeleton className="w-full h-5" />;
return (
<div className="px-2 flex flex-col gap-0.5">
<Skeleton className="w-full h-5" />
<Skeleton className="w-full h-5" />
<Skeleton className="w-full h-5" />
<Skeleton className="w-full h-5" />
<Skeleton className="w-full h-5" />
</div>
);
}

return (
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/components/code.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { faCopy, faFile, Icon } from "@rivet-gg/icons";
import { Children, cloneElement, type ReactElement } from "react";
import {
Children,
cloneElement,
type ReactElement,
type ReactNode,
} from "react";
import { CopyButton } from "./copy-area";
import { cn } from "./lib/utils";
import { Badge } from "./ui/badge";
Expand Down Expand Up @@ -96,6 +101,7 @@ interface CodeFrameProps {
language: keyof typeof languageNames;
isInGroup?: boolean;
code?: () => string | string;
footer?: ReactNode;
children?: ReactElement;
}
export const CodeFrame = ({
Expand All @@ -104,6 +110,7 @@ export const CodeFrame = ({
language,
code,
title,
footer,
isInGroup,
}: CodeFrameProps) => {
return (
Expand All @@ -118,6 +125,7 @@ export const CodeFrame = ({

<div className="text-foreground flex items-center justify-between gap-2 border-t p-2 text-xs">
<div className="text-muted-foreground flex items-center gap-1">
{footer}
{file ? (
<>
<Icon icon={faFile} className="block" />
Expand Down
Loading
Loading