Skip to content

Commit

Permalink
show results of each request, and cute progress bar
Browse files Browse the repository at this point in the history
  • Loading branch information
Mazuh committed Feb 23, 2024
1 parent b267f72 commit 26efa2d
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 16 deletions.
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@hookform/resolvers": "^3.3.2",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0",
Expand Down
25 changes: 25 additions & 0 deletions src/components/ui/progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
import * as ProgressPrimitive from "@radix-ui/react-progress";
import { cn } from "@/lib/utils";

const Progress = forwardRef<
ElementRef<typeof ProgressPrimitive.Root>,
ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
className
)}
{...props}
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
));
Progress.displayName = ProgressPrimitive.Root.displayName;

export { Progress };
160 changes: 144 additions & 16 deletions src/features/project-workspace/ProjectWorkspacePage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useRef, SyntheticEvent } from "react";
import { useState, useRef, SyntheticEvent, useEffect } from "react";
import debounce from "lodash/debounce";
import { useParams } from "wouter";
import { validate as validateUuid } from "uuid";
Expand Down Expand Up @@ -32,6 +32,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Progress } from "@/components/ui/progress";

export function ProjectWorkspacePage() {
const params = useParams();
Expand Down Expand Up @@ -186,20 +187,69 @@ function RequestSpecEditor(props: {
);
const patchUrl = patchUrlRef.current;

const [isRunInProgress, setIsRunInProgress] = useState(false);
const [hadRanOk, setRanOk] = useState<boolean | null>(null);
const [runtime, setRuntime] = useState<{
step: "idle" | "running" | "success" | "unsuccess" | "error";
text: string;
status: number;
startedAt: number;
finishedAt: number;
}>({
step: "idle",
text: "",
status: 0,
startedAt: 0,
finishedAt: 0,
});

const begin = () =>
setRuntime({
step: "running",
text: "",
status: 0,
startedAt: Date.now(),
finishedAt: 0,
});

const success = (text: string, status: number) =>
setRuntime((updatingRuntime) => ({
...updatingRuntime,
step: "success",
status,
text,
finishedAt: Date.now(),
}));

const unsuccess = (text: string, status: number) =>
setRuntime((updatingRuntime) => ({
...updatingRuntime,
step: "unsuccess",
text,
status,
finishedAt: Date.now(),
}));

const error = (text: string) =>
setRuntime((updatingRuntime) => ({
...updatingRuntime,
step: "error",
text,
finishedAt: Date.now(),
}));

const runSpec = async (running: { method: string; url: string }) => {
setRanOk(null);
setIsRunInProgress(true);
begin();
try {
await fetch(running.url, {
const response = await fetch(running.url, {
method: running.method,
});
setRanOk(true);
} catch (error) {
setRanOk(false);
} finally {
setIsRunInProgress(false);
const text = await response.text();
if (response.ok) {
success(text, response.status);
} else {
unsuccess(text, response.status);
}
} catch (exception) {
error((exception as Error).message);
}
};

Expand Down Expand Up @@ -258,15 +308,93 @@ function RequestSpecEditor(props: {
/>
<Button type="submit">Run</Button>
</form>
<p className="mt-5">
{isRunInProgress && <span className="text-accent">Running...</span>}
{hadRanOk === true && <span className="text-green-500">Ok.</span>}
{hadRanOk === false && <span className="text-destructive">Error.</span>}
</p>
<div className="mt-5">
{runtime.step === "running" && <RuntimeProgressBar />}
{runtime.step === "success" && (
<>
<p className="text-green-400 mb-3">
<strong>HTTP success:</strong> <code>{runtime.status}</code>
</p>
{runtime.text ? (
<p>
<code>{runtime.text}</code>
</p>
) : (
<p>
But <strong>empty</strong> body.
</p>
)}
</>
)}
{runtime.step === "unsuccess" && (
<>
<p className="text-red-400 mb-3">
<strong>HTTP bad code:</strong> <code>{runtime.status}</code>
</p>
{runtime.text ? (
<p>
<code>{runtime.text}</code>
</p>
) : (
<p>
For <strong>unknown</strong> reasons.
</p>
)}
</>
)}
{runtime.step === "error" && (
<>
<p className="text-red-400 mb-3">Error.</p>
{runtime.text ? (
<p>
<strong>Browser reason:</strong> <code>{runtime.text}</code>
</p>
) : (
<p>
For <strong>unknown</strong> reasons.
</p>
)}
<p>
This error was thrown by the browser, not the server.
<br />
Open your browser console, run the request again and check if
there are more evidences.
</p>
</>
)}
{runtime.finishedAt > 0 && (
<p className="mt-3 text-xs text-gray-500">
{runtime.step.toUpperCase()} in{" "}
{runtime.finishedAt - runtime.startedAt}ms. Started at{" "}
{new Date(runtime.startedAt).toLocaleTimeString("en-US")}.
</p>
)}
</div>
</div>
);
}

export function RuntimeProgressBar() {
const [progress, setProgress] = useState(13);

useEffect(() => {
const timers = [
setTimeout(() => setProgress(42), 200),
setTimeout(() => setProgress(66), 500),
setTimeout(() => setProgress(80), 2000),
setTimeout(() => setProgress(85), 3000),
setTimeout(() => setProgress(90), 4000),
setTimeout(() => setProgress(95), 5000),
];

return () => {
timers.forEach((timer) => clearTimeout(timer));
};
}, []);

return <Progress value={progress} className="w-[60%] m-auto" />;
}

const HTTP_METHODS: Array<ProjectRequestSpec["method"]> = [

Check failure on line 398 in src/features/project-workspace/ProjectWorkspacePage.tsx

View workflow job for this annotation

GitHub Actions / build_and_deploy

The type 'readonly ["GET", "POST", "PUT", "PATCH", "DELETE"]' is 'readonly' and cannot be assigned to the mutable type '("GET" | "POST" | "PUT" | "PATCH" | "DELETE")[]'.
"GET",
"POST",
Expand Down

0 comments on commit 26efa2d

Please sign in to comment.