diff --git a/crates/web/frontend/package-lock.json b/crates/web/frontend/package-lock.json index bad6d34..a87e1bc 100644 --- a/crates/web/frontend/package-lock.json +++ b/crates/web/frontend/package-lock.json @@ -14,6 +14,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", @@ -890,6 +891,36 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.2.tgz", + "integrity": "sha512-/i0fl686zaJbDQLNKrkCbMyDm6FQMt4jg323k7HuqitoANm9sE23Ql8yOK3Wusk34HSLKDChhMux05FnP6KUkw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collapsible": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz", diff --git a/crates/web/frontend/package.json b/crates/web/frontend/package.json index 2dfcff6..7decb49 100644 --- a/crates/web/frontend/package.json +++ b/crates/web/frontend/package.json @@ -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", @@ -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", diff --git a/crates/web/frontend/src/components/headers-tab/headers-tab.tsx b/crates/web/frontend/src/components/headers-tab/headers-tab.tsx index d3df16e..231a131 100644 --- a/crates/web/frontend/src/components/headers-tab/headers-tab.tsx +++ b/crates/web/frontend/src/components/headers-tab/headers-tab.tsx @@ -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); }, @@ -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)); @@ -42,21 +43,28 @@ const HeadersTab = ({ headers, setHeaders }: HeadersTabProps) => { key={index} className="flex gap-4 items-center" > + + checked !== "indeterminate" && updateHeaders(index, header[0], header[1], checked) + } + className="peer" + /> 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" /> 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" /> { /> ))} + {/* */} ); }; diff --git a/crates/web/frontend/src/components/import-popup/import-popup.tsx b/crates/web/frontend/src/components/import-popup/import-popup.tsx index 3d4970b..f90cebe 100644 --- a/crates/web/frontend/src/components/import-popup/import-popup.tsx +++ b/crates/web/frontend/src/components/import-popup/import-popup.tsx @@ -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("", 50); + const [selectedUrlTab, setSelectedUrlTab] = useState<"headers" | "body">("headers"); const [url, setUrl] = useState(""); const [file, setFile] = useState(); @@ -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); @@ -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 ( @@ -146,10 +158,7 @@ const ImportPopup = ({
- @@ -170,7 +179,11 @@ const ImportPopup = ({ onChange={(e) => setUrl(e.target.value)} />
- + (e === "body" || e === "headers") && setSelectedUrlTab(e)} + className="flex flex-col h-full pb-2" + > diff --git a/crates/web/frontend/src/components/ui/checkbox.tsx b/crates/web/frontend/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..90e19f1 --- /dev/null +++ b/crates/web/frontend/src/components/ui/checkbox.tsx @@ -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, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)); +Checkbox.displayName = CheckboxPrimitive.Root.displayName; + +export { Checkbox };