tftsr-devops_investigation/src/pages/Proxmox/NotesPage.tsx
Shaun Arman 84ddf75afe feat(proxmox): implement notes system, resource search, and administration panel (phases 12-13)
- NotesPage: load/display/edit cluster notes with draft/save/cancel flow
- SearchPage: cross-cluster resource search grouped by type with icon decorators
- AdminPage: tabbed node admin (status, apt updates, repositories, syslog, tasks)
  with cluster/node selector; imports ClusterInfo from domain.ts
2026-06-12 21:55:01 -05:00

106 lines
3.1 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/index';
import { Button } from '@/components/ui/index';
import { Textarea } from '@/components/ui/index';
import { Edit, Save, X } from 'lucide-react';
import { getClusterNotes, updateClusterNotes, listProxmoxClusters } from '@/lib/proxmoxClient';
export function ProxmoxNotesPage() {
const [notes, setNotes] = useState('');
const [editMode, setEditMode] = useState(false);
const [draft, setDraft] = useState('');
const [clusterId, setClusterId] = useState('');
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const init = async () => {
try {
const clusters = await listProxmoxClusters();
if (clusters.length > 0) {
setClusterId(clusters[0].id);
const n = await getClusterNotes(clusters[0].id);
setNotes(n);
}
} catch (e) {
setError(String(e));
}
};
void init();
}, []);
const handleEdit = () => {
setDraft(notes);
setEditMode(true);
};
const handleCancel = () => setEditMode(false);
const handleSave = async () => {
setSaving(true);
try {
await updateClusterNotes(clusterId, draft);
setNotes(draft);
setEditMode(false);
} catch (e) {
setError(String(e));
} finally {
setSaving(false);
}
};
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold">Notes</h1>
<p className="text-muted-foreground">Cluster notes and documentation</p>
</div>
{!editMode ? (
<Button variant="outline" onClick={handleEdit}>
<Edit className="mr-2 h-4 w-4" />
Edit
</Button>
) : (
<div className="flex space-x-2">
<Button variant="outline" onClick={handleCancel}>
<X className="mr-2 h-4 w-4" />
Cancel
</Button>
<Button onClick={() => void handleSave()} disabled={saving}>
<Save className="mr-2 h-4 w-4" />
{saving ? 'Saving...' : 'Save'}
</Button>
</div>
)}
</div>
{error && <div className="text-destructive text-sm">{error}</div>}
<Card>
<CardHeader>
<CardTitle>Cluster Notes</CardTitle>
</CardHeader>
<CardContent>
{!editMode ? (
<pre className="whitespace-pre-wrap text-sm font-mono min-h-[200px]">
{notes || (
<span className="text-muted-foreground">
No notes yet. Click Edit to add notes.
</span>
)}
</pre>
) : (
<Textarea
value={draft}
onChange={(e) => setDraft(e.target.value)}
className="min-h-[300px] font-mono text-sm"
placeholder="Enter cluster notes here..."
/>
)}
</CardContent>
</Card>
</div>
);
}