Skip to content

Commit

Permalink
Merge pull request #11 from cnpem/updated-schemas
Browse files Browse the repository at this point in the history
Updated schemas
  • Loading branch information
matyson authored Nov 12, 2024
2 parents e38ca4a + ad962b4 commit 84c2a4a
Show file tree
Hide file tree
Showing 25 changed files with 1,419 additions and 110 deletions.
90 changes: 90 additions & 0 deletions apps/spu-ui/src/app/_components/env-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"use client";

import { buttonVariants } from "@sophys-web/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@sophys-web/ui/dropdown-menu";
import { api } from "../../trpc/react";

export function EnvMenu() {
const apiUtils = api.useUtils();

const {
data: status,
isError,
isLoading,
} = api.status.get.useQuery(undefined);

const { mutate: envUpdate } = api.environment.update.useMutation({
onSuccess: async () => {
await apiUtils.status.get.invalidate();
},
});

const { mutate: envOpen } = api.environment.open.useMutation({
onSuccess: async () => {
await apiUtils.status.get.invalidate();
},
});

const statusMessage = () => {
if (isLoading) {
return "Loading...";
}
if (isError) {
return "Error";
}
return status?.reState || "Unknown";
};

return (
<DropdownMenu>
<DropdownMenuTrigger
className={buttonVariants({ variant: "secondary" })}
disabled={isLoading || isError}
onClick={async () => {
await apiUtils.status.get.invalidate();
}}
>
Status: {statusMessage()}
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Env controls</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => {
envUpdate();
}}
>
Update
</DropdownMenuItem>

<DropdownMenuItem
onClick={() => {
envOpen();
}}
>
Open
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel>Full status</DropdownMenuLabel>
<div className="mb-2 p-2">
{status
? Object.entries(status).map(([key, value]) => (
<p
className="text-ellipsis text-xs text-muted-foreground"
key={key}
>
{key}: {value?.toString()}
</p>
))
: null}
</div>
</DropdownMenuContent>
</DropdownMenu>
);
}
6 changes: 4 additions & 2 deletions apps/spu-ui/src/app/_components/experiment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
clearSamples as clearServerSamples,
setSamples as setServerSamples,
} from "../actions/samples";
import { EnvMenu } from "./env-menu";
import { Queue } from "./queue";
import { SampleItem } from "./sample";
import { Tray } from "./tray";
Expand Down Expand Up @@ -237,8 +238,9 @@ export default function Experiment({
</Tabs>
<div className="flex w-2/3 flex-col space-y-4 p-4">
<div className="flex flex-col gap-2">
<h1 className="text-2xl font-bold">Experiment Queue</h1>
<div className="flex items-center justify-center space-x-2">
<div className="flex items-center justify-center gap-2">
<h1 className="mr-auto text-lg font-medium">Experiment Queue</h1>
<EnvMenu />
<Button
onClick={() => {
setIsProcessing(!isProcessing);
Expand Down
59 changes: 41 additions & 18 deletions apps/spu-ui/src/app/_components/upload-button.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
"use client";

import { useRef } from "react";
import { useRef, useState } from "react";
import { parse } from "papaparse";
import { cn } from "@sophys-web/ui";
import { buttonVariants } from "@sophys-web/ui/button";
import { Input } from "@sophys-web/ui/input";
import { Label } from "@sophys-web/ui/label";
import { toast } from "@sophys-web/ui/sonner";
import type { SampleParams } from "../../lib/schemas/sample";
import { samplesSchema } from "../../lib/schemas/sample";
import type { TableItem } from "../../lib/schemas/table-item";
import { tableItemSchema } from "../../lib/schemas/table-item";

interface ButtonProps {
children: React.ReactNode;
handleUpload: (data: SampleParams[]) => void;
handleUpload: (data: TableItem[]) => void;
}

export function UploadButton(props: ButtonProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [errorMessages, setErrorMessages] = useState<string[]>([]);
return (
<div className="flex flex-col items-center gap-8">
<Label
Expand All @@ -40,7 +41,7 @@ export function UploadButton(props: ButtonProps) {
parse(file, {
header: true,
skipEmptyLines: true,
complete: async (results) => {
complete: (results) => {
const { data: csvData, errors } = results;
if (errors.length > 0) {
console.error("Error reading CSV file", errors);
Expand All @@ -49,21 +50,43 @@ export function UploadButton(props: ButtonProps) {
});
return;
}
const parsedData = await samplesSchema.safeParseAsync(csvData);
if (!parsedData.success) {
console.error(
"Error parsing expected file variables",
parsedData.error.errors,
);
toast.error("Error parsing expected file variables", {
description: parsedData.error.errors
.map((err) => err.message)
.join("\n"),
});
const parsedData = csvData
.map((data, index) => {
const result = tableItemSchema.safeParse(data);
if (!result.success) {
console.error(
`Error parsing table item on line ${index + 1}`,
result.error.message,
);
const messages = result.error.errors.map(
(error) => error.message,
);
if (messages.length > 0) {
toast.error(
`Error parsing table item on line ${index + 1}`,
{
description: messages.join("; "),
},
);
setErrorMessages(messages);
}
return null;
}
return result.data;
})
.filter((item): item is TableItem => item !== null);
if (parsedData.length === 0) {
console.error("Error parsing table items. No data returned");
toast.error("Error parsing table items. No data returned");
return;
}
toast.success("CSV file parsed successfully");
props.handleUpload(parsedData.data);
// toast.success("CSV file parse complete");
if (errorMessages.length > 0) {
toast.info("CSV file parsed with errors");
} else {
toast.success("CSV file parsed successfully");
}
props.handleUpload(parsedData);
},
});
if (inputRef.current) {
Expand Down
36 changes: 36 additions & 0 deletions apps/spu-ui/src/app/_hooks/use-queue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";

import { api } from "../../trpc/react";

export const useQueue = () => {
const apiUtils = api.useUtils();

const get = api.queue.get.useQuery(undefined, {
refetchInterval: 4000,
});

const add = api.queue.item.add.useMutation({
onSuccess: async () => {
await apiUtils.queue.get.invalidate();
},
});

const remove = api.queue.item.remove.useMutation({
onSuccess: async () => {
await apiUtils.queue.get.invalidate();
},
});

const clear = api.queue.clear.useMutation({
onSuccess: async () => {
await apiUtils.queue.get.invalidate();
},
});

return {
get,
add,
remove,
clear,
};
};
Loading

0 comments on commit 84c2a4a

Please sign in to comment.