Skip to content

Commit

Permalink
feat(client): added prodrg page, some fixes to failing api calls
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivo committed Apr 21, 2023
1 parent 7bbb1a6 commit 5246a43
Show file tree
Hide file tree
Showing 11 changed files with 381 additions and 40 deletions.
4 changes: 4 additions & 0 deletions apps/client/public/locales/en-US/my-dynamics.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"dynamic": {
"id": "Task ID"
},
"empty": {
"title": "You haven't performed a dynamic, yet...",
"description": "Maybe it's your first login, or you just haven't wanted to run anything yet. Anyway, here are some buttons that can help when you want to start a new simulation."
},
"downloads": {
"commands": "Commands",
"figures": "Figure Graphics",
Expand Down
4 changes: 3 additions & 1 deletion apps/client/public/locales/en-US/navigation.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"title": "Dynamics",
"models": {
"apo": "APO",
"acpype": "ACPYPE with Amber"
"acpype": "ACPYPE with Amber",
"prodrg": "PRODRG with GROMOS",
"prodrg-disabled": "This dynamic has been temporarily disabled as we migrate the generation model to be on par with ACPYPE and APO dynamics."
}
},
"forms": {
Expand Down
16 changes: 9 additions & 7 deletions apps/client/src/components/Auth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function Auth({ setTheme, theme }: AuthProps) {
if (status === "authenticated") {
return (
<div className="flex w-full gap-x-2">
<User className="transition-all duration-500 stroke-primary-950 my-auto h-7 w-7" />
<User className="transition-all duration-500 stroke-primary-950 my-auto min-h-[1.25rem] min-w-[1.25rem]" />
<div className="flex flex-col gap-y-1 flex-1">
<div className="flex gap-x-1">
<p className="uppercase -mb-2 transition-all duration-500 text-sm text-primary-950">
Expand All @@ -56,17 +56,19 @@ export function Auth({ setTheme, theme }: AuthProps) {
{t(`navigation:auth.role.${session.user.role.toLowerCase()}`)}
</small>
</div>
<small className="text-xs text-zinc-500">{session.user.email}</small>
<small
title={session.user.email}
className="text-xs text-zinc-500"
>
{session.user.email}
</small>
</div>
<TextButton
iconClassName="w-5 h-5"
LeftIcon={Cog}
onClick={() =>
signOut({
redirect: false
})
}
/>
<TextButton
iconClassName="w-5 h-5"
LeftIcon={LogOut}
onClick={() =>
signOut({
Expand Down
254 changes: 254 additions & 0 deletions apps/client/src/pages/dynamic/prodrg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
import { useEffect } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle, CloudCog, Download } from "lucide-react";
import { useRouter } from "next/router";
import { getServerSession } from "next-auth";
import { User } from "next-auth";
import { useSession } from "next-auth/react";
import { useTranslation } from "next-i18next";

import { Button } from "@app/components/Button";
import { Input } from "@app/components/Input";
import { PageLayout } from "@app/components/Layout/Page";
import { Select } from "@app/components/Select";
import { Switch } from "@app/components/Switch";
import { withSSRAuth } from "@app/hocs/withSSRAuth";
import { withSSRTranslations } from "@app/hocs/withSSRTranslations";
import { api } from "@app/lib/api";
import { getRunningDynamic } from "@app/queries/useRunningDynamic";
import {
PRODRGFormSchema,
PRODRGFormSchemaType
} from "@app/schemas/pages/dynamic/prodrg.zod";
import { boxTypes } from "@app/utils/box-types";
import { prodrgForceFields } from "@app/utils/force-fields";
import { waterModels } from "@app/utils/water-models";

import { authOptions } from "../api/auth/[...nextauth]";

export const getServerSideProps = withSSRTranslations(
withSSRAuth(async (ctx) => {
const session = await getServerSession(ctx.req, ctx.res, authOptions);
if (session) {
const data = await getRunningDynamic(session.user.username);

if (data?.status === "running") {
return {
redirect: {
destination: "/dynamic/running",
permanent: false
}
};
}
}

return {
props: {}
};
}),
{
namespaces: ["forms"]
}
);

export default function PRODRGDynamic({ user }: { user: User }) {
const {
formState: { errors },
handleSubmit,
register,
setValue,
watch
} = useForm<PRODRGFormSchemaType>({
resolver: zodResolver(PRODRGFormSchema),
defaultValues: {
neutralize: true,
ignore: true,
double: false
}
});
const router = useRouter();
const { t } = useTranslation(["forms", "navigation"]);
const { status } = useSession();

useEffect(() => {
if (status === "unauthenticated") {
router.replace("/");
}
}, [router, status]);

const handleSubmitDynamic: SubmitHandler<PRODRGFormSchemaType> = async (
data
) => {
const formData = new FormData();

formData.append("file_pdb", data.protein[0]);
formData.append("file_itp", data.ligandItp[0]);
formData.append("file_gro", data.ligandGro[0]);
formData.append("force_field", data.forceField);
formData.append("water_model", data.waterModel);
formData.append("box_type", data.boxType);
formData.append("box_distance", data.boxDistance);
formData.append("bootstrap", data.bootstrap ? "true" : "false");
formData.append("neutralize", data.neutralize ? "true" : "false");
formData.append("double", data.double ? "true" : "false");
formData.append("ignore", data.ignore ? "true" : "false");
formData.append("username", user.username);

await api
.post("/prodrg", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(async ({ data }) => {
if (data.status === "generated") {
await api
.post(
"/run",
{
folder: data.folder
},
{
headers: {
"Content-Type": "multipart/form-data"
}
}
)
.then(() => router.push("/dynamic/running"))
.catch(() => alert("not running"));
} else if (data.status === "commands") {
const link = document.createElement("a");
link.download = "dynamic-commands.txt";
link.href =
"data:text/plain;charset=utf-8," +
encodeURIComponent(data.commands.join(""));
link.click();
}
})
.catch(() => alert("Não foi"));
};

return (
<PageLayout title={t("navigation:dynamic.models.prodrg")}>
<div className="flex mb-2 gap-x-2 bg-yellow-400/20 border border-yellow-600 text-yellow-950 p-2 rounded-md">
<AlertTriangle className="my-auto stroke-yellow-950 h-10 w-10" />{" "}
{t("navigation:dynamic.models.prodrg-disabled")}
</div>
<form
className="flex flex-col gap-y-2"
onSubmit={handleSubmit(handleSubmitDynamic)}
>
<Input
label={t("forms:file-pdb.title")}
type="file"
accept=".pdb"
error={errors.protein}
{...register("protein")}
/>

<div className="flex flex-col md:flex-row gap-1">
<Input
label={t("forms:file-itp.title")}
type="file"
accept=".itp,.gro"
error={errors.ligandItp}
{...register("ligandItp")}
/>

<Input
label={t("forms:file-gro.title")}
type="file"
accept=".pdb,.gro"
error={errors.ligandGro}
{...register("ligandGro")}
/>
</div>

<div className="flex flex-col md:flex-row gap-1">
<Select<keyof typeof prodrgForceFields>
error={errors.forceField}
label={t("forms:force-field.title")}
name="forceField"
onChange={(newForceField) => setValue("forceField", newForceField)}
placeholder={t("forms:force-field.placeholder")}
selectedValue={watch("forceField")}
values={prodrgForceFields}
/>

<Select<keyof typeof waterModels>
error={errors.waterModel}
label={t("forms:water-model.title")}
name="waterModel"
onChange={(newWaterModel) => setValue("waterModel", newWaterModel)}
placeholder={t("forms:water-model.placeholder")}
selectedValue={watch("waterModel")}
values={waterModels}
/>
</div>

<div className="flex flex-col md:flex-row gap-1">
<Select<keyof typeof boxTypes>
error={errors.boxType}
label={t("forms:box-type.title")}
name="boxType"
onChange={(newBoxType) => setValue("boxType", newBoxType)}
placeholder={t("forms:box-type.placeholder")}
selectedValue={watch("boxType")}
values={boxTypes}
/>

<Input
label={t("forms:box-distance.title")}
error={errors.boxDistance}
type="number"
{...register("boxDistance")}
/>
</div>

<label>{t("forms:options")}</label>
<div className="flex flex-col gap-y-2">
<Switch
label={t("forms:neutralize.title")}
checked={watch("neutralize")}
onCheckedChange={(bool) => setValue("neutralize", bool)}
name="neutralize"
disabled
/>

<Switch
label={t("forms:ignore.title")}
checked={watch("ignore")}
onCheckedChange={(bool) => setValue("ignore", bool)}
name="ignore"
disabled
/>

<Switch
label={t("forms:double.title")}
checked={watch("double")}
onCheckedChange={(bool) => setValue("double", bool)}
name="double"
disabled
/>

<Switch
label={t("forms:run.title")}
checked={watch("bootstrap")}
onCheckedChange={(bool) => setValue("bootstrap", bool)}
name="bootstrap"
/>
</div>
<Button
disabled
LeftIcon={watch("bootstrap") === true ? CloudCog : Download}
type="submit"
>
{watch("bootstrap") === true
? t("forms:submit.run")
: t("forms:submit.download")}
</Button>
</form>
</PageLayout>
);
}
3 changes: 2 additions & 1 deletion apps/client/src/pages/dynamic/running.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from "react";
import { ArrowRight, Slash } from "lucide-react";
import { ArrowRight, FileCog, Slash } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { User } from "next-auth";
Expand Down Expand Up @@ -150,6 +150,7 @@ export default function Running({ user }: { user: User }) {
title={t("running:not-running.title")}
>
<div className="flex flex-col justify-center w-1/2 mx-auto">
<FileCog className="stoke-primary-950 h-14 w-14 mx-auto mb-2" />
<h1 className="text-primary-950 uppercase text-center font-bold text-2xl">
{t("running:not-running.title")}
</h1>
Expand Down
10 changes: 1 addition & 9 deletions apps/client/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import { Input } from "@app/components/Input";
import { PageLayout } from "@app/components/Layout/Page";
import { withSSRTranslations } from "@app/hocs/withSSRTranslations";

export const getStaticProps = withSSRTranslations(undefined);

export default function Home() {
return (
<PageLayout title="pages:home.title">
<Input
label="Label"
type="text"
/>
</PageLayout>
);
return <PageLayout title="pages:home.title">bem vindo</PageLayout>;
}
29 changes: 27 additions & 2 deletions apps/client/src/pages/my-dynamics.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { useEffect, useState } from "react";
import clsx from "clsx";
import {
ArrowRight,
CheckCircle,
Clock,
Download,
FileCode,
FileDigit,
FileDown,
Image,
Microscope,
RefreshCw,
Scroll,
Slash,
XCircle
} from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { User } from "next-auth";
import { useSession } from "next-auth/react";
Expand Down Expand Up @@ -233,8 +236,30 @@ export default function MyDynamics({ user }: { user: User }) {
}

return (
<PageLayout title="">
<h1>hehe</h1>
<PageLayout
className="justify-center"
title={t("my-dynamics:title")}
>
<div className="flex flex-col justify-center w-1/2 mx-auto">
<Microscope className="stoke-primary-950 h-14 w-14 mx-auto mb-2" />
<h1 className="text-primary-950 uppercase text-center font-bold text-2xl">
{t("my-dynamics:empty.title")}
</h1>
<p className="text-center">{t("my-dynamics:empty.description")}</p>

<div className="flex gap-x-2 flex-wrap mt-5 mx-auto">
<Link href="/dynamic/apo">
<Button RightIcon={ArrowRight}>
{t("navigation:dynamic.models.apo")}
</Button>
</Link>
<Link href="/dynamic/acpype">
<Button RightIcon={ArrowRight}>
{t("navigation:dynamic.models.acpype")}
</Button>
</Link>
</div>
</div>
</PageLayout>
);
}

0 comments on commit 5246a43

Please sign in to comment.