Files
Netcatty/components/host/HostNotesEditor.tsx
2026-06-01 21:03:43 +08:00

91 lines
3.1 KiB
TypeScript

import { FileText } from "lucide-react";
import React, { useEffect, useState } from "react";
import { useI18n } from "../../application/i18n/I18nProvider";
import { MessageResponse } from "../ai-elements/message";
import { ScrollArea } from "../ui/scroll-area";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
import { Textarea } from "../ui/textarea";
import { cn } from "../../lib/utils";
const PREVIEW_PROSE_CLASS =
"text-sm text-foreground/90 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0";
function defaultNotesTab(notes: string): "edit" | "preview" {
return notes.trim() ? "preview" : "edit";
}
export interface HostNotesEditorProps {
value: string;
onChange: (value: string) => void;
/** Changes when opening a different host (e.g. host id) to reset the active tab */
panelKey?: string;
className?: string;
showHeader?: boolean;
}
export const HostNotesEditor: React.FC<HostNotesEditorProps> = ({
value,
onChange,
panelKey,
className,
showHeader = true,
}) => {
const { t } = useI18n();
const [tab, setTab] = useState<"edit" | "preview">(() => defaultNotesTab(value));
useEffect(() => {
if (panelKey === undefined) return;
setTab(defaultNotesTab(value));
// Only reset tab when opening another host, not while editing notes.
// eslint-disable-next-line react-hooks/exhaustive-deps -- value read on panelKey change
}, [panelKey]);
const trimmed = value.trim();
return (
<div className={cn("space-y-2", className)}>
{showHeader && (
<>
<div className="flex items-center gap-2">
<FileText size={14} className="text-muted-foreground shrink-0" />
<p className="text-xs font-semibold">
{t("hostDetails.notes.label")}
</p>
</div>
<p className="text-xs text-muted-foreground">{t("hostDetails.notes.help")}</p>
</>
)}
<Tabs value={tab} onValueChange={(v) => setTab(v as "edit" | "preview")}>
<TabsList className="h-8 w-full">
<TabsTrigger value="edit" className="flex-1 text-xs">
{t("hostDetails.notes.tab.edit")}
</TabsTrigger>
<TabsTrigger value="preview" className="flex-1 text-xs">
{t("hostDetails.notes.tab.preview")}
</TabsTrigger>
</TabsList>
<TabsContent value="edit" className="mt-2">
<Textarea
placeholder={t("hostDetails.notes.placeholder")}
value={value}
onChange={(e) => onChange(e.target.value)}
className="min-h-[120px] text-sm"
rows={5}
/>
</TabsContent>
<TabsContent value="preview" className="mt-2">
<ScrollArea className="h-[120px] rounded-md border border-border/60 bg-muted/20 p-3">
{trimmed ? (
<MessageResponse className={PREVIEW_PROSE_CLASS}>{trimmed}</MessageResponse>
) : (
<p className="text-sm text-muted-foreground">
{t("hostDetails.notes.preview.empty")}
</p>
)}
</ScrollArea>
</TabsContent>
</Tabs>
</div>
);
};