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

feat: reload job status every 5 sec. and show monitoring message #110

Merged
merged 3 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 app/bartender-client/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const BARTENDER_APPLICATION_NAME = "haddock3";
export const WORKFLOW_CONFIG_FILENAME = "workflow.cfg";
export const JOB_OUTPUT_DIR = "output";
export const PAGE_REFRESH_RATE_MS = 5000;
dmijatovic marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion app/builder/Form.css
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ Btn classes are copied from app/components/ui/button.tsx

.workflow-builder-app .page {
display: grid;
grid-template-columns: 9rem minmax(16rem, 1fr) 3fr;
grid-template-columns: 9rem minmax(20rem, 1fr) 3fr;
gap: 1rem;
}

Expand Down
47 changes: 47 additions & 0 deletions app/components/ui/DotsLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ComponentPropsWithoutRef, useEffect, useState } from "react";

// max. number of dots to draw
const maxLength = 30;

type DotsProps = {
state: string;
label?: string;
} & ComponentPropsWithoutRef<"div">;

export default function DotsLoader({
state,
label = "Loading",
...props
}: DotsProps) {
const [count, setCount] = useState(1);

useEffect(() => {
const timer = setInterval(() => {
// console.log("increase counter...")
setCount((cnt) => {
if (cnt > maxLength) {
// make negative, then the dot count decreases
return -maxLength;
}
return cnt + 1;
});
}, 1000);

return () => {
// clean up
clearInterval(timer);
};
}, []);

useEffect(() => {
// reset to 1 on each state change
setCount(1);
}, [state]);

return (
<div {...props}>
{label}
{" . ".repeat(Math.abs(count))}
dmijatovic marked this conversation as resolved.
Show resolved Hide resolved
</div>
);
}
35 changes: 34 additions & 1 deletion app/routes/jobs.$id._index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { useEffect } from "react";
import {
json,
redirect,
type LoaderFunctionArgs,
ActionFunctionArgs,
} from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { useLoaderData, useRevalidator } from "@remix-run/react";

import { deleteJob, getJobById, jobIdFromParams } from "~/models/job.server";
import { getUser } from "~/auth.server";
import { JobStatus } from "~/components/JobStatus";
import { getBartenderTokenByUser } from "~/bartender-client/token.server";
import { PAGE_REFRESH_RATE_MS } from "~/bartender-client/constants";
import DotsLoader from "~/components/ui/DotsLoader";

export const loader = async ({ params, request }: LoaderFunctionArgs) => {
const jobId = jobIdFromParams(params);
Expand Down Expand Up @@ -40,11 +43,41 @@ export const action = async ({ params, request }: ActionFunctionArgs) => {

export default function JobPage() {
const { job } = useLoaderData<typeof loader>();
const { revalidate } = useRevalidator();

useEffect(() => {
// set interval to refresh job status
const id = setInterval(() => {
// reload only if user is on the page/tab
if (
document.visibilityState === "visible" &&
dmijatovic marked this conversation as resolved.
Show resolved Hide resolved
// and job state is not "definitive"
job.state !== "error" &&
dmijatovic marked this conversation as resolved.
Show resolved Hide resolved
job.state !== "ok"
) {
console.log("JobPage...REVALIDATE");
revalidate();
}
}, PAGE_REFRESH_RATE_MS);
return () => {
// clear interval
console.log("JobPage...clearInterval...", id);
if (id) clearInterval(id);
};
}, [revalidate, job.state]);

return (
<main className="flex gap-16">
<div>
<JobStatus job={job} />
{/* show dots loader indicating we monitor state change */}
{job.state !== "error" && job.state !== "ok" ? (
<DotsLoader
className="py-4"
state={job.state}
label="Monitoring state change"
/>
) : null}
</div>
{/* TODO allow to read input files when job is not completed */}
{/* TODO allow job to be cancelled, need bartender support first */}
Expand Down
Loading