Files
dns-autoresolver/web/src/components/RecordEditor.tsx
T
2026-07-05 13:48:24 +07:00

122 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Info, Plus, Trash2 } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { FieldDescription } from "@/components/ui/field"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import type { RecordDTO } from "@/api/types"
const RECORD_TYPES = ["A", "AAAA", "CNAME", "MX", "TXT", "SRV", "NS", "SOA"] as const
const typeItems = RECORD_TYPES.map((t) => ({ value: t, label: t }))
const DEFAULT_TTL = 3600
function emptyRecord(): RecordDTO {
return { type: "A", name: "", ttl: DEFAULT_TTL, values: [""] }
}
export function RecordEditor({
value,
onChange,
}: {
value: RecordDTO[]
onChange: (records: RecordDTO[]) => void
}) {
function updateRecord(index: number, patch: Partial<RecordDTO>) {
onChange(value.map((r, i) => (i === index ? { ...r, ...patch } : r)))
}
function addRecord() {
onChange([...value, emptyRecord()])
}
function removeRecord(index: number) {
onChange(value.filter((_, i) => i !== index))
}
return (
<div className="flex flex-col gap-2">
<FieldDescription className="flex items-start gap-2 rounded-lg border border-border/60 bg-background/40 px-3 py-2.5">
<Info className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" strokeWidth={1.75} />
<span>
Используйте <code className="font-dns text-foreground">{"{{domain_name}}"}</code> в имени
или значении записи при проверке домена подставится имя его зоны (без завершающей
точки).
</span>
</FieldDescription>
{value.map((record, index) => (
<div
key={index}
className="flex flex-col gap-2 rounded-lg border border-border bg-background/40 p-2.5 sm:flex-row sm:items-start"
>
<Select
items={typeItems}
value={record.type}
onValueChange={(v) => updateRecord(index, { type: v as string })}
>
<SelectTrigger aria-label={`Тип записи ${index + 1}`} size="sm" className="font-dns sm:w-24">
<SelectValue />
</SelectTrigger>
<SelectContent>
{typeItems.map((item) => (
<SelectItem key={item.value} value={item.value} className="font-dns">
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
<Input
aria-label={`Имя записи ${index + 1}`}
className="font-dns sm:flex-1"
placeholder="www"
value={record.name}
onChange={(e) => updateRecord(index, { name: e.target.value })}
/>
<Input
aria-label={`TTL записи ${index + 1}`}
type="number"
min={0}
className="font-dns sm:w-24"
value={record.ttl}
onChange={(e) => updateRecord(index, { ttl: Number(e.target.value) })}
/>
<Textarea
aria-label={`Значения записи ${index + 1}`}
className="font-dns sm:flex-1"
placeholder="192.0.2.1"
rows={1}
value={record.values.join("\n")}
onChange={(e) => updateRecord(index, { values: e.target.value.split("\n") })}
/>
<Button
type="button"
variant="destructive"
size="icon-sm"
aria-label={`Удалить запись ${index + 1}`}
onClick={() => removeRecord(index)}
>
<Trash2 className="size-3.5" strokeWidth={1.75} />
</Button>
</div>
))}
<Button type="button" variant="outline" size="sm" onClick={addRecord} className="self-start">
<Plus className="size-3.5" strokeWidth={1.75} />
Добавить запись
</Button>
</div>
)
}