diff --git a/app/write/[id]/page.tsx b/app/write/[id]/page.tsx deleted file mode 100644 index e9d8d6b..0000000 --- a/app/write/[id]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function ViewBlogPage() { - // This will take a blog id as a parameter and fetch the blog from the database using the get_blog_by_id command. -} diff --git a/app/write/edit/page.tsx b/app/write/edit/page.tsx new file mode 100644 index 0000000..fcd7ea8 --- /dev/null +++ b/app/write/edit/page.tsx @@ -0,0 +1,173 @@ +"use client"; + +import { useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; +import { invoke } from "@tauri-apps/api/core"; +import Link from "next/link"; +import { HomelabApp, Blog } from "@/types"; + +export default function WritePage() { + const params = useSearchParams(); + + const blogId = params.get("id") || null; + + if (!blogId) { + return ( +
+ + No document ID provided. + +
+ ); + } + + const [blog, setBlog] = useState(null); + const [linkedApps, setLinkedApps] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + if (!blogId) return; + + const fetchBlogAndApps = async () => { + try { + setLoading(true); + const currentBlog = (await invoke("get_blog", { id: blogId })) as + | Blog + | undefined; + + if (!currentBlog) { + setError("Blog post not found."); + return; + } + + setBlog(currentBlog); + + // 2. Fetch the metadata for any bound/linked apps if they exist + if (currentBlog.reference && currentBlog.reference.length > 0) { + const apps = (await invoke("get_apps_by_id", { + ids: currentBlog.reference, + })) as HomelabApp[]; + setLinkedApps(apps); + } + } catch (err) { + console.error("Failed to load blog page data:", err); + setError("An error occurred while loading the document."); + } finally { + setLoading(false); + } + }; + + fetchBlogAndApps(); + }, [blogId]); + + if (loading) { + return ( +
+ + Loading document... + +
+ ); + } + + if (error || !blog) { + return ( +
+

+ {error || "Something went wrong."} +

+ + Go Back Home + +
+ ); + } + + return ( +
+ {/* Top Header Controls */} +
+
+

+ {blog.title || "Untitled Document"} +

+
+ + + + + + Edit Document + +
+ + {/* Main Reading Workspace Layout */} +
+ {/* Left Side: Styled Canvas Viewport */} +
+ {/* Using your exact editor style class 'prose max-w-none' to ensure uniform markdown formatting */} +
+
+ + {/* Right Side: Associated Connected Homelab Apps */} + {linkedApps.length > 0 && ( +
+
+

+ Linked Assets +

+

+ Infrastructure context bound to this document. +

+
+ +
+ {linkedApps.map((app) => ( +
+
+ {app.name} + +
+
+ {app.address} +
+ {app.description && ( +
+ {app.description} +
+ )} +
+ ))} +
+
+ )} +
+
+ ); +} diff --git a/app/write/new/page.tsx b/app/write/new/page.tsx index e7dccb1..59bf313 100644 --- a/app/write/new/page.tsx +++ b/app/write/new/page.tsx @@ -4,14 +4,7 @@ import { useEffect, useState, Suspense } from "react"; import { useSearchParams } from "next/navigation"; import { invoke } from "@tauri-apps/api/core"; import TextEditor from "@/components/text-editor"; -import { HomelabApp } from "@/types"; - -// Create a type for the Blog returned by your Rust backend -interface Blog { - id: string; - title: string; - content: string; -} +import { HomelabApp, Blog } from "@/types"; function WritePageContent() { const searchParams = useSearchParams(); diff --git a/components/blog-card.tsx b/components/blog-card.tsx index 2656cf1..181c3d1 100644 --- a/components/blog-card.tsx +++ b/components/blog-card.tsx @@ -34,7 +34,7 @@ export default function BlogCard({ blog }: BlogCardProps) { return (
router.push(`/write/${blog.id}`)} + onClick={() => router.push(`/write/edit?id=${blog.id}`)} > {/* Decorative background glow that triggers on hover */}
diff --git a/src-tauri/src/db/mod.rs b/src-tauri/src/db/mod.rs index 5671e94..e8c031c 100644 --- a/src-tauri/src/db/mod.rs +++ b/src-tauri/src/db/mod.rs @@ -154,3 +154,13 @@ pub fn get_apps_by_ids(conn: &mut SqliteConnection, ids: &[String]) -> Vec(conn) .expect("Error loading apps by ids") } + +pub fn get_blog_by_id(conn: &mut SqliteConnection, blog_id: &str) -> Option { + use crate::db::schema::blog; + + blog::table + .filter(blog::id.eq(blog_id)) + .first::(conn) + .optional() + .expect("Error loading blog by id") +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2d581ef..a4426f6 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -121,6 +121,25 @@ fn get_apps_by_id(ids: Vec) -> Vec { db::get_apps_by_ids(&mut conn, &ids) } +#[tauri::command] +fn get_blog(id: &str) -> Option { + let mut conn = establish_connection(); + let blog = db::get_blog_by_id(&mut conn, id); + + blog.map(|b| { + let linked_app_ids: Option> = match &b.reference { + Some(ids) => serde_json::from_str(ids).ok(), + None => None, + }; + TempBlog { + id: b.id.clone(), + title: b.title.clone(), + content: b.content.clone(), + linked_app_ids, + } + }) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() @@ -132,7 +151,8 @@ pub fn run() { ping, get_blogs, save_blog, - get_apps_by_id + get_apps_by_id, + get_blog ]) .run(tauri::generate_context!()) .expect("error while running tauri application");