Skip to content

Commit

Permalink
Improve URL file importing (#38)
Browse files Browse the repository at this point in the history
Closes #37
  • Loading branch information
daavidrgz authored Oct 6, 2024
1 parent c2d9bf1 commit e92db6d
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 19 deletions.
31 changes: 31 additions & 0 deletions crates/web/frontend/package-lock.json

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

3 changes: 2 additions & 1 deletion crates/web/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"build": "npm i gq-web && next build",
"start": "next start",
"ci": "biome ci ./src",
"check-apply": "biome check ./src --apply"
"check-apply": "biome check ./src --write"
},
"dependencies": {
"@codemirror/autocomplete": "^6.18.1",
Expand All @@ -17,6 +17,7 @@
"@monaco-editor/react": "^4.6.0",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-context-menu": "^2.2.2",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
Expand Down
33 changes: 22 additions & 11 deletions crates/web/frontend/src/components/headers-tab/headers-tab.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import { cn } from "@/lib/utils";
import { Trash } from "lucide-react";
import { useCallback } from "react";
import { Checkbox } from "../ui/checkbox";
import { Input } from "../ui/input";
import HeadersDatalist from "./headers-datalist";

interface HeadersTabProps {
headers: [string, string][];
setHeaders: (headers: [string, string][]) => void;
headers: [string, string, boolean][];
setHeaders: (headers: [string, string, boolean][]) => void;
}

const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
const updateHeaders = useCallback(
(index: number, key: string, value: string) => {
const newHeaders: [string, string][] = headers.map((header, i) =>
i === index ? [key, value] : header,
(index: number, key: string, value: string, enabled: boolean) => {
const newHeaders: [string, string, boolean][] = headers.map((header, i) =>
i === index ? [key, value, enabled] : header,
);
if (index === headers.length - 1 && (key || value)) {
newHeaders.push(["", ""]);
newHeaders.push(["", "", true]);
}
setHeaders(newHeaders);
},
Expand All @@ -26,7 +27,7 @@ const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
const deleteHeaders = useCallback(
(index: number) => {
if (headers.length === 1) {
setHeaders([["", ""]]);
setHeaders([["", "", true]]);
return;
}
setHeaders(headers.filter((_, i) => i !== index));
Expand All @@ -42,21 +43,28 @@ const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
key={index}
className="flex gap-4 items-center"
>
<Checkbox
checked={header[2]}
onCheckedChange={(checked) =>
checked !== "indeterminate" && updateHeaders(index, header[0], header[1], checked)
}
className="peer"
/>
<Input
type="text"
placeholder="Header"
value={header[0]}
list="header-list"
onChange={(e) => updateHeaders(index, e.target.value, header[1])}
className="w-1/2 p-2 border rounded-md mb-0"
onChange={(e) => updateHeaders(index, e.target.value, header[1], header[2])}
className="w-1/2 p-2 border rounded-md mb-0 peer-data-[state=unchecked]:opacity-50 transition-opacity"
/>
<HeadersDatalist id="header-list" />
<Input
type="text"
placeholder="Value"
value={header[1]}
onChange={(e) => updateHeaders(index, header[0], e.target.value)}
className="w-1/2 p-2 border rounded-md mb-0"
onChange={(e) => updateHeaders(index, header[0], e.target.value, header[2])}
className="w-1/2 p-2 border rounded-md mb-0 peer-data-[state=unchecked]:opacity-50 transition-opacity"
/>
<Trash
className={cn(
Expand All @@ -70,6 +78,9 @@ const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => {
/>
</div>
))}
{/* <Button className="block mx-auto mt-2 px-2 py-2 text-xs h-auto" variant="outline">
Delete all
</Button> */}
</div>
);
};
Expand Down
27 changes: 20 additions & 7 deletions crates/web/frontend/src/components/import-popup/import-popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ const ImportPopup = ({
}: Props) => {
const [open, setOpen] = useState(false);
const [httpMethod, setHttpMethod] = useState<"GET" | "POST">("GET");
const [headers, setHeaders] = useState<[string, string][]>([["", ""]]);
const [headers, setHeaders] = useState<[string, string, boolean][]>([["", "", true]]);
const [body, setBody, instantBody] = useLazyState<string>("", 50);
const [selectedUrlTab, setSelectedUrlTab] = useState<"headers" | "body">("headers");
const [url, setUrl] = useState("");
const [file, setFile] = useState<ImportedFile>();

Expand Down Expand Up @@ -92,7 +93,10 @@ const ImportPopup = ({
});
setOpen(false);
try {
const data = await importUrl(currentType, url, httpMethod, headers, body);
const enabledHeaders: [string, string][] = headers
.filter(([, , enabled]) => enabled)
.map(([key, value]) => [key, value]);
const data = await importUrl(currentType, url, httpMethod, enabledHeaders, body);
onImportFile(data);
} catch (err) {
onError(err);
Expand All @@ -118,6 +122,14 @@ const ImportPopup = ({
file ? handleImportFile() : url && handleImportUrl();
};

const handleChangeHttpMethod = (value: string) => {
const method = fromString(value);
setHttpMethod(method);
if (method === "GET") {
setSelectedUrlTab("headers");
}
};

const headersCount = headers.reduce((acc, [key, value]) => (key || value ? acc + 1 : acc), 0);

return (
Expand Down Expand Up @@ -146,10 +158,7 @@ const ImportPopup = ({
</TabsList>
<TabsContent value="url" className="h-[32vh]">
<div className="flex items-center">
<Select
value={httpMethod}
onValueChange={(value) => setHttpMethod(fromString(value))}
>
<Select value={httpMethod} onValueChange={handleChangeHttpMethod}>
<SelectTrigger className="w-min rounded-r-none bg-accent-background font-semibold">
<SelectValue id="http-method" />
</SelectTrigger>
Expand All @@ -170,7 +179,11 @@ const ImportPopup = ({
onChange={(e) => setUrl(e.target.value)}
/>
</div>
<Tabs defaultValue="headers" className="flex flex-col h-full pb-2">
<Tabs
value={selectedUrlTab}
onValueChange={(e) => (e === "body" || e === "headers") && setSelectedUrlTab(e)}
className="flex flex-col h-full pb-2"
>
<TabsList className="flex justify-start my-4">
<TabsTrigger value="headers" className="w-32" variant="outline">
<span className="relative">
Expand Down
28 changes: 28 additions & 0 deletions crates/web/frontend/src/components/ui/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use client";

import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { Check } from "lucide-react";
import * as React from "react";

import { cn } from "@/lib/utils";

const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className,
)}
{...props}
>
<CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;

export { Checkbox };

0 comments on commit e92db6d

Please sign in to comment.