Skip to content

Commit c7fe6b2

Browse files
committed
refactor some RequestPanel components into their own components
1 parent f0ddcf6 commit c7fe6b2

File tree

7 files changed

+496
-247
lines changed

7 files changed

+496
-247
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { useState, useMemo } from "react"
2+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
3+
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
4+
import { CopyButton } from "./CopyButton"
5+
import { CODE_SNIPPETS } from "@/utils/codeSnippets"
6+
import { AuthConfig, Header, Cookie } from "@/types"
7+
import {
8+
Select,
9+
SelectContent,
10+
SelectItem,
11+
SelectTrigger,
12+
SelectValue,
13+
} from "@/components/ui/select"
14+
import { ScrollArea } from "@/components/ui/scroll-area"
15+
16+
interface CodeSnippetViewerProps {
17+
method: string
18+
url: string
19+
headers: Header[]
20+
body: string
21+
contentType: string
22+
auth: AuthConfig
23+
cookies: Cookie[]
24+
}
25+
26+
export function CodeSnippetViewer({
27+
method,
28+
url,
29+
headers,
30+
body,
31+
contentType,
32+
auth,
33+
cookies,
34+
}: CodeSnippetViewerProps) {
35+
const [selectedLanguage, setSelectedLanguage] = useState(CODE_SNIPPETS[0].value)
36+
37+
const codeSnippet = useMemo(() => {
38+
const generator = CODE_SNIPPETS.find(s => s.value === selectedLanguage)?.generator
39+
if (!generator) return ''
40+
41+
return generator({
42+
method,
43+
url,
44+
headers,
45+
body,
46+
contentType,
47+
auth,
48+
cookies,
49+
})
50+
}, [selectedLanguage, method, url, headers, body, contentType, auth, cookies])
51+
52+
return (
53+
<ScrollArea className="h-full pr-4">
54+
<div className="space-y-4">
55+
<div className="flex justify-between items-center">
56+
<Select value={selectedLanguage} onValueChange={setSelectedLanguage}>
57+
<SelectTrigger className="w-[200px] bg-background border-input focus:ring-0 focus-visible:ring-1">
58+
<SelectValue placeholder="Select Language" />
59+
</SelectTrigger>
60+
<SelectContent className="bg-gray-800 border-border">
61+
{CODE_SNIPPETS.map((lang) => (
62+
<SelectItem
63+
key={lang.value}
64+
value={lang.value}
65+
className="hover:bg-muted focus:bg-muted text-white"
66+
>
67+
{lang.label}
68+
</SelectItem>
69+
))}
70+
</SelectContent>
71+
</Select>
72+
<CopyButton content={codeSnippet} />
73+
</div>
74+
75+
<div className="relative font-mono text-sm bg-muted rounded-md p-4">
76+
<SyntaxHighlighter
77+
language={selectedLanguage === 'curl' ? 'bash' : selectedLanguage}
78+
style={{
79+
...oneDark,
80+
'pre[class*="language-"]': {
81+
...oneDark['pre[class*="language-"]'],
82+
background: 'transparent',
83+
margin: 0,
84+
padding: 0,
85+
},
86+
'code[class*="language-"]': {
87+
...oneDark['code[class*="language-"]'],
88+
background: 'transparent',
89+
},
90+
'pre > code': {
91+
...oneDark['pre > code'],
92+
background: 'transparent',
93+
},
94+
'token': {
95+
background: 'transparent',
96+
}
97+
}}
98+
customStyle={{
99+
background: 'transparent',
100+
fontSize: 'inherit',
101+
whiteSpace: 'pre-wrap',
102+
wordBreak: 'break-all',
103+
overflowWrap: 'break-word',
104+
}}
105+
wrapLongLines
106+
>
107+
{codeSnippet}
108+
</SyntaxHighlighter>
109+
</div>
110+
</div>
111+
</ScrollArea>
112+
)
113+
}

src/components/CookieEditor.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React from "react"
2+
import { Button } from "@/components/ui/button"
3+
import { Input } from "@/components/ui/input"
4+
import { Plus, Trash2 } from "lucide-react"
5+
import { Cookie } from "@/types"
6+
7+
interface CookieEditorProps {
8+
cookies: Cookie[]
9+
onCookiesChange: (cookies: Cookie[]) => void
10+
}
11+
12+
export function CookieEditor({ cookies, onCookiesChange }: CookieEditorProps) {
13+
const updateCookie = (index: number, field: keyof Cookie, value: string | boolean) => {
14+
const newCookies = [...cookies]
15+
newCookies[index] = { ...newCookies[index], [field]: value }
16+
onCookiesChange(newCookies)
17+
}
18+
19+
const removeCookie = (index: number) => {
20+
const newCookies = cookies.filter((_, i) => i !== index)
21+
onCookiesChange(newCookies)
22+
}
23+
24+
const addCookie = () => {
25+
onCookiesChange([...cookies, { name: '', value: '' }])
26+
}
27+
28+
return (
29+
<div className="space-y-4">
30+
<div className="grid grid-cols-[1fr,1fr,auto] gap-2">
31+
{cookies.map((cookie, index) => (
32+
<React.Fragment key={index}>
33+
<Input
34+
value={cookie.name}
35+
onChange={(e) => updateCookie(index, 'name', e.target.value)}
36+
placeholder="Cookie name"
37+
/>
38+
<Input
39+
value={cookie.value}
40+
onChange={(e) => updateCookie(index, 'value', e.target.value)}
41+
placeholder="Cookie value"
42+
/>
43+
<Button variant="ghost" onClick={() => removeCookie(index)}>
44+
<Trash2 className="h-4 w-4" />
45+
</Button>
46+
</React.Fragment>
47+
))}
48+
</div>
49+
<Button onClick={addCookie}>
50+
<Plus className="h-4 w-4 mr-2" /> Add Cookie
51+
</Button>
52+
</div>
53+
)
54+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { Textarea } from "@/components/ui/textarea"
2+
import {
3+
Select,
4+
SelectContent,
5+
SelectItem,
6+
SelectTrigger,
7+
SelectValue,
8+
} from "@/components/ui/select"
9+
10+
const CONTENT_TYPES = [
11+
"application/json",
12+
"application/x-www-form-urlencoded",
13+
"text/plain",
14+
"text/html",
15+
"multipart/form-data",
16+
]
17+
18+
interface RequestBodyEditorProps {
19+
body: string
20+
contentType: string
21+
onBodyChange: (body: string) => void
22+
onContentTypeChange: (contentType: string) => void
23+
}
24+
25+
export function RequestBodyEditor({
26+
body,
27+
contentType,
28+
onBodyChange,
29+
onContentTypeChange,
30+
}: RequestBodyEditorProps) {
31+
return (
32+
<div className="flex flex-col gap-2 h-full">
33+
<Select value={contentType} onValueChange={onContentTypeChange}>
34+
<SelectTrigger className="bg-background border-input focus:ring-0 focus-visible:ring-1">
35+
<SelectValue placeholder="Content Type" />
36+
</SelectTrigger>
37+
<SelectContent className="bg-slate-800 border-border">
38+
{CONTENT_TYPES.map((type) => (
39+
<SelectItem
40+
key={type}
41+
value={type}
42+
className="hover:bg-muted focus:bg-muted text-white"
43+
>
44+
{type}
45+
</SelectItem>
46+
))}
47+
</SelectContent>
48+
</Select>
49+
<div className="flex-1 min-h-0 rounded-md border">
50+
<Textarea
51+
placeholder="Enter request body"
52+
value={body}
53+
onChange={(e) => onBodyChange(e.target.value)}
54+
onKeyDown={(e) => {
55+
const bracketPairs: { [key: string]: string } = {
56+
'{': '}',
57+
'[': ']',
58+
'(': ')',
59+
}
60+
61+
if (e.key in bracketPairs) {
62+
e.preventDefault()
63+
const textarea = e.currentTarget
64+
const { selectionStart, selectionEnd } = textarea
65+
const openBracket = e.key
66+
const closeBracket = bracketPairs[openBracket]
67+
68+
// Get current cursor position and text
69+
const currentText = textarea.value
70+
const beforeCursor = currentText.substring(0, selectionStart)
71+
const afterCursor = currentText.substring(selectionEnd)
72+
73+
// Insert brackets and move cursor between them
74+
const newText = beforeCursor + openBracket + closeBracket + afterCursor
75+
onBodyChange(newText)
76+
77+
// Set cursor position between brackets (needs to be done after React re-render)
78+
setTimeout(() => {
79+
textarea.selectionStart = textarea.selectionEnd = selectionStart + 1
80+
}, 0)
81+
}
82+
}}
83+
className="h-full resize-none border-0 focus-visible:ring-0"
84+
/>
85+
</div>
86+
</div>
87+
)
88+
}

0 commit comments

Comments
 (0)