Skip to content

Commit 7b08099

Browse files
committed
split up some components
1 parent d106b7b commit 7b08099

File tree

9 files changed

+341
-307
lines changed

9 files changed

+341
-307
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"react": "^18.3.1",
3333
"react-dom": "^18.3.1",
3434
"react-resizable-panels": "^2.1.7",
35+
"react-router-dom": "^7.1.3",
3536
"react-syntax-highlighter": "^15.6.1",
3637
"sonner": "^1.7.2",
3738
"tailwind-merge": "^2.6.0",

pnpm-lock.yaml

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function App() {
5555
if (currentTab) {
5656
updateTab(currentTab.id, { params })
5757
}
58-
})
58+
}, currentTab?.params)
5959

6060
// Update raw URL when params change without affecting user input
6161
useEffect(() => {
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
2+
import { Input } from "@/components/ui/input"
3+
import { AuthConfig, AuthType } from "@/types"
4+
5+
interface AuthConfiguratorProps {
6+
auth: AuthConfig
7+
onAuthChange: (auth: AuthConfig) => void
8+
}
9+
10+
const AUTH_TYPES = [
11+
{ value: 'none', label: 'No Auth' },
12+
{ value: 'basic', label: 'Basic Auth' },
13+
{ value: 'bearer', label: 'Bearer Token' },
14+
{ value: 'api-key', label: 'API Key' },
15+
]
16+
17+
export function AuthConfigurator({ auth, onAuthChange }: AuthConfiguratorProps) {
18+
return (
19+
<div className="space-y-4">
20+
<Select value={auth.type} onValueChange={(value: AuthType) => onAuthChange({ ...auth, type: value })}>
21+
<SelectTrigger className="w-[200px] bg-background border-input focus:ring-0 focus-visible:ring-1">
22+
<SelectValue placeholder="Authentication Type" />
23+
</SelectTrigger>
24+
<SelectContent className="bg-gray-800 border-border">
25+
{AUTH_TYPES.map((type) => (
26+
<SelectItem
27+
key={type.value}
28+
value={type.value}
29+
className="hover:bg-muted focus:bg-muted text-white"
30+
>
31+
{type.label}
32+
</SelectItem>
33+
))}
34+
</SelectContent>
35+
</Select>
36+
37+
{auth.type === 'basic' && (
38+
<div className="space-y-2">
39+
<Input
40+
placeholder="Username"
41+
value={auth.username || ''}
42+
onChange={(e) => onAuthChange({ ...auth, username: e.target.value })}
43+
/>
44+
<Input
45+
type="password"
46+
placeholder="Password"
47+
value={auth.password || ''}
48+
onChange={(e) => onAuthChange({ ...auth, password: e.target.value })}
49+
/>
50+
</div>
51+
)}
52+
53+
{auth.type === 'bearer' && (
54+
<Input
55+
placeholder="Bearer Token"
56+
value={auth.token || ''}
57+
onChange={(e) => onAuthChange({ ...auth, token: e.target.value })}
58+
/>
59+
)}
60+
61+
{auth.type === 'api-key' && (
62+
<div className="space-y-2">
63+
<Input
64+
placeholder="Key"
65+
value={auth.key || ''}
66+
onChange={(e) => onAuthChange({ ...auth, key: e.target.value })}
67+
/>
68+
<Input
69+
placeholder="Value"
70+
value={auth.value || ''}
71+
onChange={(e) => onAuthChange({ ...auth, value: e.target.value })}
72+
/>
73+
<Select
74+
value={auth.addTo || 'header'}
75+
onValueChange={(value: 'header' | 'query') => onAuthChange({ ...auth, addTo: value })}
76+
>
77+
<SelectTrigger className="w-[200px] bg-background border-input focus:ring-0 focus-visible:ring-1">
78+
<SelectValue placeholder="Add to" />
79+
</SelectTrigger>
80+
<SelectContent className="bg-gray-800 border-border">
81+
<SelectItem value="header" className="hover:bg-muted focus:bg-muted text-white">
82+
Header
83+
</SelectItem>
84+
<SelectItem value="query" className="hover:bg-muted focus:bg-muted text-white">
85+
Query Parameter
86+
</SelectItem>
87+
</SelectContent>
88+
</Select>
89+
</div>
90+
)}
91+
</div>
92+
)
93+
}

src/components/CopyButton.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Button } from "@/components/ui/button"
2+
import { Copy, Check } from "lucide-react"
3+
import { useState } from "react"
4+
import { toast } from "sonner"
5+
6+
interface CopyButtonProps {
7+
content: string
8+
className?: string
9+
}
10+
11+
export function CopyButton({ content, className = "" }: CopyButtonProps) {
12+
const [copied, setCopied] = useState(false)
13+
14+
const copy = async () => {
15+
await navigator.clipboard.writeText(content)
16+
setCopied(true)
17+
toast.success("Copied to clipboard")
18+
setTimeout(() => setCopied(false), 2000)
19+
}
20+
21+
return (
22+
<Button
23+
variant="outline"
24+
size="icon"
25+
className={`h-8 w-8 ${className}`}
26+
onClick={copy}
27+
>
28+
{copied ? (
29+
<Check className="h-4 w-4" />
30+
) : (
31+
<Copy className="h-4 w-4" />
32+
)}
33+
</Button>
34+
)
35+
}

src/components/KeyValueList.tsx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { Input } from "@/components/ui/input"
2+
import { Button } from "@/components/ui/button"
3+
import { Trash2, Plus } from "lucide-react"
4+
import React from "react"
5+
6+
interface KeyValueListProps<T extends { key: string; value: string; enabled: boolean }> {
7+
items: T[]
8+
onItemsChange: (items: T[]) => void
9+
keyPlaceholder?: string
10+
valuePlaceholder?: string
11+
disabled?: boolean
12+
}
13+
14+
export function KeyValueList<T extends { key: string; value: string; enabled: boolean }>({
15+
items,
16+
onItemsChange,
17+
keyPlaceholder = "Name",
18+
valuePlaceholder = "Value",
19+
disabled = false
20+
}: KeyValueListProps<T>) {
21+
const updateItem = (index: number, field: keyof T, value: string | boolean) => {
22+
const newItems = [...items]
23+
newItems[index] = { ...newItems[index], [field]: value }
24+
onItemsChange(newItems)
25+
}
26+
27+
const removeItem = (index: number) => {
28+
onItemsChange(items.filter((_, i) => i !== index))
29+
}
30+
31+
const addItem = () => {
32+
onItemsChange([...items, { key: "", value: "", enabled: true } as T])
33+
}
34+
35+
return (
36+
<div className="space-y-2">
37+
<div className="grid grid-cols-[1fr,1fr,auto,auto] gap-2">
38+
{items.map((item, index) => (
39+
<React.Fragment key={index}>
40+
<Input
41+
placeholder={keyPlaceholder}
42+
value={item.key}
43+
onChange={(e) => updateItem(index, 'key', e.target.value)}
44+
disabled={disabled}
45+
/>
46+
<Input
47+
placeholder={valuePlaceholder}
48+
value={item.value}
49+
onChange={(e) => updateItem(index, 'value', e.target.value)}
50+
disabled={disabled}
51+
/>
52+
<Button
53+
variant="ghost"
54+
size="icon"
55+
onClick={() => updateItem(index, 'enabled', !item.enabled)}
56+
className={item.enabled ? "text-foreground" : "text-muted-foreground"}
57+
disabled={disabled}
58+
>
59+
<input
60+
type="checkbox"
61+
checked={item.enabled}
62+
className="h-4 w-4"
63+
onChange={() => {}} // Handled by button click
64+
/>
65+
</Button>
66+
<Button
67+
variant="ghost"
68+
size="icon"
69+
onClick={() => removeItem(index)}
70+
disabled={disabled}
71+
>
72+
<Trash2 className="h-4 w-4" />
73+
</Button>
74+
</React.Fragment>
75+
))}
76+
</div>
77+
<Button
78+
variant="outline"
79+
size="sm"
80+
className="w-full"
81+
onClick={addItem}
82+
disabled={disabled}
83+
>
84+
<Plus className="h-4 w-4 mr-2" />
85+
Add Item
86+
</Button>
87+
</div>
88+
)
89+
}

0 commit comments

Comments
 (0)