Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Minor issues #16

Merged
merged 9 commits into from
Aug 3, 2024
Merged
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
35 changes: 29 additions & 6 deletions web/app/components/custom-node-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { useFetcher } from "@remix-run/react";

import { InformationCircleIcon } from "@heroicons/react/24/outline";
import { action } from "~/routes/upload-workflow-file/route";
import { useToast } from "./ui/use-toast";

const UPLOAD_WF_FETCHER_KEY = "upload-workflow-file";

Expand All @@ -45,6 +46,19 @@ export default function CustomNodeForm({
const uploadWFFetcher = useFetcher<typeof action>({
key: UPLOAD_WF_FETCHER_KEY,
});

const { toast } = useToast();

useEffect(() => {
if (uploadWFFetcher.data?.result === "error") {
toast({
variant: "destructive",
title: uploadWFFetcher.data.error,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [uploadWFFetcher.data]);

return (
<div>
<div className="px-4 py-6 sm:p-8">
Expand Down Expand Up @@ -81,7 +95,10 @@ export default function CustomNodeForm({
<Button
onClick={onNextStep}
disabled={
!uploadWFFetcher?.data?.nodes && selectedCustomNodes.length === 0
uploadWFFetcher.state !== "idle" ||
((uploadWFFetcher.data?.result === "error" ||
!uploadWFFetcher.data?.data) &&
selectedCustomNodes.length === 0)
}
>
Next
Expand Down Expand Up @@ -196,11 +213,15 @@ function UploadWorkflowFileForm({
}

useEffect(() => {
if (fetcher.data?.nodes?.length ?? 0 > 0) {
onNodesGeneratedFromWFFile(fetcher.data?.nodes ?? []);
if (
fetcher.data?.result === "success" &&
(fetcher.data.data.length ?? 0 > 0)
) {
onNodesGeneratedFromWFFile(fetcher.data.data ?? []);
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetcher.data?.nodes]);
}, [fetcher.data]);
return (
<fetcher.Form
action="/upload-workflow-file"
Expand Down Expand Up @@ -263,9 +284,11 @@ function UploadWorkflowFileForm({
) : null}
</div>
</div>
{fetcher.state == "idle" && fetcher?.data?.nodes ? (
{fetcher.state == "idle" &&
fetcher.data?.result === "success" &&
fetcher?.data?.data ? (
<p className="text-sm mt-2">
<span className="font-semibold">{fetcher?.data?.nodes.length}</span>
<span className="font-semibold">{fetcher?.data?.data.length}</span>
<span> nodes selected from the workflow file</span>
</p>
) : null}
Expand Down
12 changes: 12 additions & 0 deletions web/app/components/loading-indicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Loader2 } from "lucide-react";

const LoadingIndicator = () => {
return (
<div className="flex items-center justify-center h-screen">
<Loader2 className="w-10 h-10 animate-spin text-primary" />
<span className="ml-2 text-lg font-semibold">Loading...</span>
</div>
);
};

export default LoadingIndicator;
4 changes: 4 additions & 0 deletions web/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
ScrollRestoration,
useLoaderData,
useLocation,
useNavigation,
} from "@remix-run/react";
import "./tailwind.css";

import { Toaster } from "~/components/ui/toaster";

import { ClerkApp, SignedIn, UserButton } from "@clerk/remix";
import { rootAuthLoader } from "@clerk/remix/ssr.server";
import LoadingIndicator from "./components/loading-indicator";

export async function loader(args: LoaderFunctionArgs) {
return rootAuthLoader(args, () => {
Expand Down Expand Up @@ -53,6 +55,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
}

function App() {
const navigation = useNavigation();
const location = useLocation();
const isAuthPage = ["/sign-in", "/sign-up"].includes(location.pathname);

Expand All @@ -73,6 +76,7 @@ function App() {
</nav>
) : null}
<main className="flex-grow">
{navigation.state === "loading" ? <LoadingIndicator /> : null}
<Outlet />
</main>
</div>
Expand Down
51 changes: 26 additions & 25 deletions web/app/routes/apps/route.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { json, LoaderFunctionArgs, redirect } from "@remix-run/node";
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
import {
Link,
useFetcher,
useLoaderData,
useRevalidator,
} from "@remix-run/react";
import { APIResponse, App } from "~/lib/types";
import { App } from "~/lib/types";
import { requireAuth } from "~/server/auth";

import { MoreHorizontal, PlusIcon } from "lucide-react";
Expand Down Expand Up @@ -36,6 +36,7 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
import { useEffect, useState } from "react";
import { useToast } from "~/components/ui/use-toast";
import { DELETE_APP_KEY } from "~/lib/constants";
import { sendErrorResponse, sendSuccessResponse } from "~/server/utils";

export async function action({ request }: LoaderFunctionArgs) {
const formData = await request.formData();
Expand All @@ -52,24 +53,20 @@ export async function action({ request }: LoaderFunctionArgs) {
X_API_KEY: process.env.MACHINE_BUILDER_API_KEY!,
},
});
const data = await response.json();
console.log("data", data);
if (!response.ok)
return json<APIResponse<App[]>>(
{ error: "Unable to delete app", result: "error" },
{ status: 400 }

if (!response.ok) {
return sendErrorResponse<App[]>(
"Unable to delete app",
response.status
);
}

return json<APIResponse<string>>(
{ data: data["app_id"], result: "success" },
{ status: 200 }
);
const data = await response.json();

return sendSuccessResponse<string>(data["app_id"], 200);
} catch (error) {
console.error("Delete app API error", error);
return json<APIResponse<App[]>>(
{ error: "Unable to delete app", result: "error" },
{ status: 400 }
);
return sendErrorResponse<App[]>("Unable to delete app", 500);
}
}
}
Expand All @@ -89,17 +86,19 @@ export const loader = async (args: LoaderFunctionArgs) => {
X_API_KEY: process.env.MACHINE_BUILDER_API_KEY!,
},
});

if (!response.ok) {
return sendErrorResponse<App[]>(
"Unable to fetch list of apps",
response.status
);
}

const apps = (await response.json()) as App[];
return json<APIResponse<App[]>>(
{ data: apps, result: "success" },
{ status: 200 }
);
return sendSuccessResponse<App[]>(apps, 200);
} catch (error) {
console.error("Get apps API error", error);
return json<APIResponse<App[]>>(
{ error: "Unable to fetch list of apps", result: "error" },
{ status: 400 }
);
return sendErrorResponse<App[]>("Unable to fetch list of apps", 500);
}
};

Expand Down Expand Up @@ -270,8 +269,10 @@ function AppsLayout({ apps }: AppsLayoutProps) {
<div
className={`
${
app.state === "deployed"
app.tasks === "1"
? "text-green-400 bg-green-400/10"
: app.state === "deployed"
? "text-orange-400 bg-orange-400/10"
: "text-rose-400 bg-rose-400/10"
}
flex-none rounded-full p-1`}
Expand Down
2 changes: 1 addition & 1 deletion web/app/routes/civitai-search/route.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { json, useFetcher } from "@remix-run/react";
import { useEffect, useState } from "react";
import { useDebounce, cn } from "~/lib/utils";
import { Model } from "../../lib/types";
import { Model } from "~/lib/types";

import {
Command,
Expand Down
4 changes: 2 additions & 2 deletions web/app/routes/create-app/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import ModelsForm from "~/components/models-form";
import GpuForm from "~/components/gpu-form";
import { getCustomNodes, getModels } from "~/server/github";
import { convertCustomNodesJson, convertModelsJson } from "~/lib/utils";
import { CREATE_APP_FETCHER_KEY } from "../../lib/constants";
import { CREATE_APP_FETCHER_KEY } from "~/lib/constants";
import { useToast } from "~/components/ui/use-toast";
import { generateCreateMachineRequestBody } from "../../server/utils";
import { generateCreateMachineRequestBody } from "~/server/utils";

import { requireAuth } from "~/server/auth";

Expand Down
30 changes: 23 additions & 7 deletions web/app/routes/upload-workflow-file/route.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ActionFunctionArgs, json } from "@remix-run/node";
import { CustomNode } from "../../lib/types";
import { ActionFunctionArgs } from "@remix-run/node";
import { CustomNode } from "~/lib/types";
import { sendErrorResponse, sendSuccessResponse } from "~/server/utils";

export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
Expand All @@ -10,18 +11,33 @@ export async function action({ request }: ActionFunctionArgs) {
method: "POST",
body: formData,
});
if (!response.ok) {
return sendErrorResponse<CustomNode[]>(
"Unable to fetch custom nodes from workflow file",
response.status
);
}

const custom_nodes = await response.json();
const nodes: CustomNode[] = custom_nodes.map(
(custom_node: string): CustomNode => ({
reference: custom_node,
})
);
return json({ nodes }, { status: 200 });

if (nodes.length === 0) {
return sendErrorResponse<CustomNode[]>(
"No custom nodes found in the uploaded workflow file",
400
);
}

return sendSuccessResponse<CustomNode[]>(nodes, 200);
} catch (error) {
console.error("upload-workflow-file-api-error", error);
return json(
{ nodes: [], error: "Unable to fetch custom nodes from workflow file" },
{ status: 400 }
console.error("upload-workflow-file API error", error);
return sendErrorResponse<CustomNode[]>(
"Unable to fetch custom nodes from workflow file",
500
);
}
}
25 changes: 25 additions & 0 deletions web/app/server/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { json } from "@remix-run/react";
import {
CreateAppRequestBody,
OutputCustomNodesJson,
OutputModel,
APIResponse,
} from "~/lib/types";

export function generateCreateMachineRequestBody(
Expand All @@ -24,3 +26,26 @@ export function generateCreateMachineRequestBody(
additional_dependencies,
};
}

export function sendErrorResponse<T>(
error: string,
status: number | undefined
) {
return json<APIResponse<T>>(
{
result: "error",
error: error,
},
{ status: status }
);
}

export function sendSuccessResponse<T>(data: T, status: number | undefined) {
return json<APIResponse<T>>(
{
result: "success",
data,
},
{ status: status }
);
}