@@ -7,7 +7,7 @@ import { EntryStatus, Visibility } from "@mosaic/shared";
|
||||
import { EntryViewer } from "@/components/knowledge/EntryViewer";
|
||||
import { EntryEditor } from "@/components/knowledge/EntryEditor";
|
||||
import { EntryMetadata } from "@/components/knowledge/EntryMetadata";
|
||||
import { EntryGraphViewer } from "@/components/knowledge/EntryGraphViewer";
|
||||
import { VersionHistory } from "@/components/knowledge/VersionHistory";
|
||||
import { fetchEntry, updateEntry, deleteEntry, fetchTags } from "@/lib/api/knowledge";
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,6 @@ export default function EntryPage() {
|
||||
|
||||
const [entry, setEntry] = useState<KnowledgeEntryWithTags | null>(null);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [showGraph, setShowGraph] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -34,6 +33,7 @@ export default function EntryPage() {
|
||||
const [editTags, setEditTags] = useState<string[]>([]);
|
||||
const [availableTags, setAvailableTags] = useState<KnowledgeTag[]>([]);
|
||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<"content" | "history">("content");
|
||||
|
||||
// Load entry data
|
||||
useEffect(() => {
|
||||
@@ -46,7 +46,7 @@ export default function EntryPage() {
|
||||
setEditContent(data.content);
|
||||
setEditStatus(data.status);
|
||||
setEditVisibility(data.visibility);
|
||||
setEditTags(data.tags.map((tag: { id: string }) => tag.id));
|
||||
setEditTags(data.tags.map((tag) => tag.id));
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Failed to load entry");
|
||||
} finally {
|
||||
@@ -82,7 +82,7 @@ export default function EntryPage() {
|
||||
editStatus !== entry.status ||
|
||||
editVisibility !== entry.visibility ||
|
||||
JSON.stringify(editTags.sort()) !==
|
||||
JSON.stringify(entry.tags.map((t: { id: string }) => t.id).sort());
|
||||
JSON.stringify(entry.tags.map((t) => t.id).sort());
|
||||
|
||||
setHasUnsavedChanges(changed);
|
||||
}, [entry, isEditing, editTitle, editContent, editStatus, editVisibility, editTags]);
|
||||
@@ -158,7 +158,7 @@ export default function EntryPage() {
|
||||
setEditContent(entry.content);
|
||||
setEditStatus(entry.status);
|
||||
setEditVisibility(entry.visibility);
|
||||
setEditTags(entry.tags.map((tag: { id: string }) => tag.id));
|
||||
setEditTags(entry.tags.map((tag) => tag.id));
|
||||
setIsEditing(false);
|
||||
setHasUnsavedChanges(false);
|
||||
}
|
||||
@@ -181,6 +181,25 @@ export default function EntryPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleVersionRestore = (): void => {
|
||||
// Reload entry after version restore
|
||||
async function reload(): Promise<void> {
|
||||
try {
|
||||
const data = await fetchEntry(slug);
|
||||
setEntry(data);
|
||||
setEditTitle(data.title);
|
||||
setEditContent(data.content);
|
||||
setEditStatus(data.status);
|
||||
setEditVisibility(data.visibility);
|
||||
setEditTags(data.tags.map((tag) => tag.id));
|
||||
setActiveTab("content");
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Failed to reload entry");
|
||||
}
|
||||
}
|
||||
void reload();
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-6">
|
||||
@@ -250,7 +269,7 @@ export default function EntryPage() {
|
||||
</span>
|
||||
|
||||
{/* Tags */}
|
||||
{entry.tags.map((tag: { id: string; name: string; color: string | null }) => (
|
||||
{entry.tags.map((tag) => (
|
||||
<span
|
||||
key={tag.id}
|
||||
className="px-3 py-1 rounded-full text-xs font-medium bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300"
|
||||
@@ -270,31 +289,33 @@ export default function EntryPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* View Tabs */}
|
||||
{/* Tabs */}
|
||||
{!isEditing && (
|
||||
<div className="mb-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex gap-4">
|
||||
<div className="mb-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<nav className="flex gap-6">
|
||||
<button
|
||||
onClick={() => setShowGraph(false)}
|
||||
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
|
||||
!showGraph
|
||||
? "border-blue-500 text-blue-600 dark:text-blue-400"
|
||||
type="button"
|
||||
onClick={() => setActiveTab("content")}
|
||||
className={`pb-3 border-b-2 font-medium text-sm transition-colors ${
|
||||
activeTab === "content"
|
||||
? "border-blue-600 text-blue-600 dark:text-blue-400"
|
||||
: "border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
}`}
|
||||
>
|
||||
Content
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowGraph(true)}
|
||||
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
|
||||
showGraph
|
||||
? "border-blue-500 text-blue-600 dark:text-blue-400"
|
||||
type="button"
|
||||
onClick={() => setActiveTab("history")}
|
||||
className={`pb-3 border-b-2 font-medium text-sm transition-colors ${
|
||||
activeTab === "history"
|
||||
? "border-blue-600 text-blue-600 dark:text-blue-400"
|
||||
: "border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
}`}
|
||||
>
|
||||
Graph View
|
||||
History
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -302,12 +323,10 @@ export default function EntryPage() {
|
||||
<div className="mb-6">
|
||||
{isEditing ? (
|
||||
<EntryEditor content={editContent} onChange={setEditContent} />
|
||||
) : showGraph ? (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden" style={{ height: '600px' }}>
|
||||
<EntryGraphViewer slug={slug} initialDepth={1} />
|
||||
</div>
|
||||
) : (
|
||||
) : activeTab === "content" ? (
|
||||
<EntryViewer entry={entry} />
|
||||
) : (
|
||||
<VersionHistory slug={slug} onRestore={handleVersionRestore} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user