From 26efa2de38965be63f68f4ec5a44f8688367a8b1 Mon Sep 17 00:00:00 2001 From: mazuh Date: Fri, 23 Feb 2024 00:25:08 -0300 Subject: [PATCH] show results of each request, and cute progress bar --- package-lock.json | 25 +++ package.json | 1 + src/components/ui/progress.tsx | 25 +++ .../ProjectWorkspacePage.tsx | 160 ++++++++++++++++-- 4 files changed, 195 insertions(+), 16 deletions(-) create mode 100644 src/components/ui/progress.tsx diff --git a/package-lock.json b/package-lock.json index e700c9a..14c4ac2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,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", @@ -3746,6 +3747,30 @@ } } }, + "node_modules/@radix-ui/react-progress": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.0.3.tgz", + "integrity": "sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-select": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.0.0.tgz", diff --git a/package.json b/package.json index c7c1c0a..b1c3a3d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/ui/progress.tsx b/src/components/ui/progress.tsx new file mode 100644 index 0000000..ddd6526 --- /dev/null +++ b/src/components/ui/progress.tsx @@ -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, + ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)); +Progress.displayName = ProgressPrimitive.Root.displayName; + +export { Progress }; diff --git a/src/features/project-workspace/ProjectWorkspacePage.tsx b/src/features/project-workspace/ProjectWorkspacePage.tsx index 30d3604..5d4d6f8 100644 --- a/src/features/project-workspace/ProjectWorkspacePage.tsx +++ b/src/features/project-workspace/ProjectWorkspacePage.tsx @@ -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"; @@ -32,6 +32,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { Progress } from "@/components/ui/progress"; export function ProjectWorkspacePage() { const params = useParams(); @@ -186,20 +187,69 @@ function RequestSpecEditor(props: { ); const patchUrl = patchUrlRef.current; - const [isRunInProgress, setIsRunInProgress] = useState(false); - const [hadRanOk, setRanOk] = useState(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); } }; @@ -258,15 +308,93 @@ function RequestSpecEditor(props: { /> -

- {isRunInProgress && Running...} - {hadRanOk === true && Ok.} - {hadRanOk === false && Error.} -

+
+ {runtime.step === "running" && } + {runtime.step === "success" && ( + <> +

+ HTTP success: {runtime.status} +

+ {runtime.text ? ( +

+ {runtime.text} +

+ ) : ( +

+ But empty body. +

+ )} + + )} + {runtime.step === "unsuccess" && ( + <> +

+ HTTP bad code: {runtime.status} +

+ {runtime.text ? ( +

+ {runtime.text} +

+ ) : ( +

+ For unknown reasons. +

+ )} + + )} + {runtime.step === "error" && ( + <> +

Error.

+ {runtime.text ? ( +

+ Browser reason: {runtime.text} +

+ ) : ( +

+ For unknown reasons. +

+ )} +

+ This error was thrown by the browser, not the server. +
+ Open your browser console, run the request again and check if + there are more evidences. +

+ + )} + {runtime.finishedAt > 0 && ( +

+ {runtime.step.toUpperCase()} in{" "} + {runtime.finishedAt - runtime.startedAt}ms. Started at{" "} + {new Date(runtime.startedAt).toLocaleTimeString("en-US")}. +

+ )} +
); } +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 ; +} + const HTTP_METHODS: Array = [ "GET", "POST",